From d1b1f9092827da928eeda834d6a7136ef6cc96ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:34:18 +0000 Subject: [PATCH 001/172] chore(deps): bump github.com/getkin/kin-openapi from 0.108.0 to 0.109.0 (#116) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.108.0 to 0.109.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.108.0...v0.109.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 65416253..649095d4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.6.1 - github.com/getkin/kin-openapi v0.108.0 + github.com/getkin/kin-openapi v0.109.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index e8c15ed7..7db5c7f2 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.108.0 h1:EYf0GtsKa4hQNIlplGS+Au7NEfGQ1F7MoHD2kcVevPQ= -github.com/getkin/kin-openapi v0.108.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= +github.com/getkin/kin-openapi v0.109.0 h1:Cpb0PmIPFEV0LVvikEvfo3gw3rBMVSjJ57w15j+/A/U= +github.com/getkin/kin-openapi v0.109.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From a667e79442371dc26fe173f4c51692bb5983bb50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 12:18:15 +0100 Subject: [PATCH 002/172] chore(deps): bump github.com/samber/lo from 1.35.0 to 1.36.0 (#118) Bumps [github.com/samber/lo](https://github.com/samber/lo) from 1.35.0 to 1.36.0. - [Release notes](https://github.com/samber/lo/releases) - [Changelog](https://github.com/samber/lo/blob/master/CHANGELOG.md) - [Commits](https://github.com/samber/lo/compare/v1.35.0...v1.36.0) --- updated-dependencies: - dependency-name: github.com/samber/lo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 649095d4..b9abda94 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.46.1 - github.com/samber/lo v1.35.0 + github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 github.com/uptrace/bunrouter v1.0.19 diff --git a/go.sum b/go.sum index 7db5c7f2..46669604 100644 --- a/go.sum +++ b/go.sum @@ -428,8 +428,8 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= -github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= +github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= From d86df2bb2d40775bdc8d23fc49d8b6336ea2332f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:51:30 +0100 Subject: [PATCH 003/172] chore(deps): bump github.com/getkin/kin-openapi from 0.109.0 to 0.110.0 (#119) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.109.0 to 0.110.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.109.0...v0.110.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b9abda94..655bc20a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.6.1 - github.com/getkin/kin-openapi v0.109.0 + github.com/getkin/kin-openapi v0.110.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index 46669604..3170aa1b 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.109.0 h1:Cpb0PmIPFEV0LVvikEvfo3gw3rBMVSjJ57w15j+/A/U= -github.com/getkin/kin-openapi v0.109.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= +github.com/getkin/kin-openapi v0.110.0 h1:1GnJALxsltcSzCMqgtqKlLhYQeULv3/jesmV2sC5qE0= +github.com/getkin/kin-openapi v0.110.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 8d10a004d82ea2e67173b73f46f3851258e7e1b2 Mon Sep 17 00:00:00 2001 From: Giovanna Monti <60603265+GiovannaMonti@users.noreply.github.com> Date: Wed, 30 Nov 2022 15:23:20 +0100 Subject: [PATCH 004/172] fix: add additional headers to grant API (#120) * feat(RJC-460): add additional headers to grant API * fix:lint * Apply suggestions from code review Co-authored-by: Giovanna Monti Co-authored-by: Federico Maggi Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- internal/config/env.go | 65 +++++++++++++++++++++++++++---------- internal/config/env_test.go | 48 ++++++++++++++++++++++++++- main.go | 1 + main_test.go | 37 ++++++++++++++++++--- 4 files changed, 128 insertions(+), 23 deletions(-) diff --git a/internal/config/env.go b/internal/config/env.go index 745c2370..d243d4dc 100644 --- a/internal/config/env.go +++ b/internal/config/env.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "net/http" + "strings" "github.com/gorilla/mux" "github.com/mia-platform/configlib" @@ -36,24 +37,25 @@ const ( // EnvironmentVariables struct with the mapping of desired // environment variables. type EnvironmentVariables struct { - LogLevel string - HTTPPort string - ServiceVersion string - TargetServiceHost string - TargetServiceOASPath string - OPAModulesDirectory string - APIPermissionsFilePath string - UserPropertiesHeader string - UserGroupsHeader string - UserIdHeader string - ClientTypeHeader string - BindingsCrudServiceURL string - MongoDBUrl string - RolesCollectionName string - BindingsCollectionName string - PathPrefixStandalone string - DelayShutdownSeconds int - Standalone bool + LogLevel string + HTTPPort string + ServiceVersion string + TargetServiceHost string + TargetServiceOASPath string + OPAModulesDirectory string + APIPermissionsFilePath string + UserPropertiesHeader string + UserGroupsHeader string + UserIdHeader string + ClientTypeHeader string + BindingsCrudServiceURL string + MongoDBUrl string + RolesCollectionName string + BindingsCollectionName string + PathPrefixStandalone string + DelayShutdownSeconds int + Standalone bool + AdditionalHeadersToProxy string } var EnvVariablesConfig = []configlib.EnvConfig{ @@ -139,6 +141,11 @@ var EnvVariablesConfig = []configlib.EnvConfig{ Key: BindingsCrudServiceURL, Variable: "BindingsCrudServiceURL", }, + { + Key: "ADDITIONAL_HEADERS_TO_PROXY", + Variable: "AdditionalHeadersToProxy", + DefaultValue: "miauserid", + }, } type EnvKey struct{} @@ -180,3 +187,25 @@ func GetEnvOrDie() EnvironmentVariables { return env } + +var extraHeadersKeys = []string{"x-request-id", "x-forwarded-for", "x-forwarded-proto", "x-forwarded-host"} + +func (env EnvironmentVariables) GetAdditionalHeadersToProxy() []string { + if env.AdditionalHeadersToProxy == "" { + return extraHeadersKeys + } + customHeaders := strings.Split(env.AdditionalHeadersToProxy, ",") + for _, extraHeaderKey := range extraHeadersKeys { + duplicate := false + for _, customHeader := range customHeaders { + if customHeader == extraHeaderKey { + duplicate = true + break + } + } + if !duplicate { + customHeaders = append(customHeaders, extraHeaderKey) + } + } + return customHeaders +} diff --git a/internal/config/env_test.go b/internal/config/env_test.go index 5e9be5b1..29751c4e 100644 --- a/internal/config/env_test.go +++ b/internal/config/env_test.go @@ -80,7 +80,8 @@ func TestGetEnvOrDie(t *testing.T) { PathPrefixStandalone: "/eval", ServiceVersion: "latest", - OPAModulesDirectory: "/modules", + OPAModulesDirectory: "/modules", + AdditionalHeadersToProxy: "miauserid", } t.Run(`returns correctly - with TargetServiceHost`, func(t *testing.T) { @@ -174,3 +175,48 @@ func setEnvs(envsToSet []env) func() { } } } + +func TestGetAdditionalHeadersToProxy(t *testing.T) { + t.Run("without additional header to proxy use default extra headers", func(t *testing.T) { + env := EnvironmentVariables{} + headersToProxy := env.GetAdditionalHeadersToProxy() + + require.Equal(t, extraHeadersKeys, headersToProxy) + }) + + t.Run("with empty additional header to proxy use default extra headers", func(t *testing.T) { + env := EnvironmentVariables{ + AdditionalHeadersToProxy: "", + } + headersToProxy := env.GetAdditionalHeadersToProxy() + + require.Equal(t, extraHeadersKeys, headersToProxy) + }) + + t.Run("with additional header to proxy add default headers to custom headers", func(t *testing.T) { + env := EnvironmentVariables{ + AdditionalHeadersToProxy: "head1,head2", + } + headersToProxy := env.GetAdditionalHeadersToProxy() + + require.Equal(t, []string{"head1", "head2", "x-request-id", "x-forwarded-for", "x-forwarded-proto", "x-forwarded-host"}, headersToProxy) + }) + + t.Run("remove duplicated headers in custom and default", func(t *testing.T) { + env := EnvironmentVariables{ + AdditionalHeadersToProxy: "head1,head2,x-forwarded-for", + } + headersToProxy := env.GetAdditionalHeadersToProxy() + + require.Equal(t, []string{"head1", "head2", "x-forwarded-for", "x-request-id", "x-forwarded-proto", "x-forwarded-host"}, headersToProxy) + }) + + t.Run("all extra headers duplicated", func(t *testing.T) { + env := EnvironmentVariables{ + AdditionalHeadersToProxy: "head1,head2,x-forwarded-for,x-request-id,x-forwarded-proto,x-forwarded-host", + } + headersToProxy := env.GetAdditionalHeadersToProxy() + + require.Equal(t, []string{"head1", "head2", "x-forwarded-for", "x-request-id", "x-forwarded-proto", "x-forwarded-host"}, headersToProxy) + }) +} diff --git a/main.go b/main.go index a3adb201..3dae0452 100644 --- a/main.go +++ b/main.go @@ -155,6 +155,7 @@ func setupRouter( evalRouter := router.NewRoute().Subrouter() if env.Standalone { + router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) swaggerRouter, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(router), swagger.Options{ Context: context.Background(), Openapi: &openapi3.T{ diff --git a/main_test.go b/main_test.go index bcc7c73f..7476cbf8 100644 --- a/main_test.go +++ b/main_test.go @@ -15,6 +15,7 @@ package main import ( + "bytes" "context" "encoding/json" "fmt" @@ -1665,10 +1666,12 @@ func TestSetupRouterStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) env := config.EnvironmentVariables{ - Standalone: true, - TargetServiceHost: "my-service:4444", - PathPrefixStandalone: "/my-prefix", - ServiceVersion: "my-version", + Standalone: true, + TargetServiceHost: "my-service:4444", + PathPrefixStandalone: "/my-prefix", + ServiceVersion: "my-version", + BindingsCrudServiceURL: "http://crud:3030", + AdditionalHeadersToProxy: "miauserid", } opa := &OPAModuleConfig{ Name: "policies", @@ -1755,6 +1758,32 @@ filter_policy { assert.Equal(t, requestError.Error, "EOF") }) + t.Run("grant API with headers to proxy", func(t *testing.T) { + reqBody := GrantRequestBody{ + ResourceID: "my-company", + Subjects: []string{"subj"}, + Groups: []string{"group1"}, + Roles: []string{"role1"}, + Permissions: []string{"permission1"}, + } + reqBodyBytes, err := json.Marshal(reqBody) + require.Nil(t, err, "Unexpected error") + + w := httptest.NewRecorder() + + gock.New("http://crud:3030"). + Post("/"). + MatchHeader("miauserid", "my user id to proxy"). + Reply(200). + JSON([]byte(`{"_id":"theobjectid"}`)) + + req := httptest.NewRequest(http.MethodPost, "/grant/bindings/resource/some-resource", bytes.NewReader(reqBodyBytes)) + req.Header.Set("miauserid", "my user id to proxy") + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Result().StatusCode) + }) + t.Run("API documentation is correctly exposed - json", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/openapi/json", nil) From 84266128aab30b93937d937ed7377ac8fd3f0fbf Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Wed, 30 Nov 2022 15:40:02 +0100 Subject: [PATCH 005/172] Upgrade version to v1.5.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1d124944..c6f4bbfa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.5.1" +ENV SERVICE_VERSION="1.5.2" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 5482edd210192e0aa98e290075ca1f4a68dc4613 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 5 Dec 2022 09:35:25 +0100 Subject: [PATCH 006/172] feat: add evaluation info logs (#121) * feat: add evaluation info logs * docs: update CHANGELOG * fix: change import order * Apply suggestions from code review Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- handler_test.go | 127 ++++++++++++++++++++++++++++++++++++++++++ opaevaluator.go | 32 +++++++++-- opamiddleware.go | 45 ++++++++++++++- opamiddleware_test.go | 103 ++++++++++++++++++++++++++++++++++ router_test.go | 6 ++ 5 files changed, 306 insertions(+), 7 deletions(-) diff --git a/handler_test.go b/handler_test.go index d6ff5ef8..0affa5a0 100644 --- a/handler_test.go +++ b/handler_test.go @@ -41,6 +41,7 @@ import ( "github.com/open-policy-agent/opa/rego" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" "gotest.tools/v3/assert" ) @@ -548,6 +549,122 @@ allow { assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") }) + + t.Run("data evalutation logs correctly added", func(t *testing.T) { + partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + assert.Equal(t, err, nil, "Unexpected error") + + t.Run("no query generation", func(t *testing.T) { + invoked := false + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + + assert.Equal(t, r.URL.Path, "/api", "Mocked Backend: Unexpected path of request url") + assert.Equal(t, r.URL.RawQuery, "mockQuery=iamquery", "Mocked Backend: Unexpected rawQuery of request url") + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + + serverURL, _ := url.Parse(server.URL) + + ctx := createContext(t, + ctx, + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + nil, + mockXPermission, + mockOPAModule, + partialEvaluators, + ) + + log, hook := test.NewNullLogger() + ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) + assert.Equal(t, err, nil, "Unexpected error") + + w := httptest.NewRecorder() + + rbacHandler(w, r) + + assert.Assert(t, invoked, "Handler was not invoked.") + assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + + actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") + require.Len(t, actualLog, 1) + require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) + delete(actualLog[0].Data, "evaluationTimeMicroseconds") + require.Equal(t, logrus.Fields{ + "allowed": true, + "matchedPath": "/matched/path", + "method": "GET", + "partialEval": false, + "policyName": "todo", + "requestedPath": "/requested/path", + }, actualLog[0].Data) + }) + + t.Run("with query generation", func(t *testing.T) { + invoked := false + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + + assert.Equal(t, r.URL.Path, "/api", "Mocked Backend: Unexpected path of request url") + assert.Equal(t, r.URL.RawQuery, "mockQuery=iamquery", "Mocked Backend: Unexpected rawQuery of request url") + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + + serverURL, _ := url.Parse(server.URL) + + opaModuleConfig := &OPAModuleConfig{ + Name: "mypolicy.rego", + Content: `package policies +allow { + input.request.method == "GET" + input.request.path == "/api" + employee := data.resources[_] + employee.salary < 0 +}`, + } + partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + require.NoError(t, err) + + ctx := createContext(t, + ctx, + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + nil, + mockRondConfigWithQueryGen, + opaModuleConfig, + partialEvaluators, + ) + + log, hook := test.NewNullLogger() + ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) + assert.Equal(t, err, nil, "Unexpected error") + + w := httptest.NewRecorder() + + rbacHandler(w, r) + + assert.Assert(t, invoked, "Handler was not invoked.") + assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + + actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") + require.Len(t, actualLog, 1) + require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) + delete(actualLog[0].Data, "evaluationTimeMicroseconds") + require.Equal(t, logrus.Fields{ + "allowed": true, + "matchedPath": "/matched/path", + "method": "GET", + "partialEval": true, + "policyName": "allow", + "requestedPath": "/requested/path", + }, actualLog[0].Data) + }) + }) } func TestStandaloneMode(t *testing.T) { @@ -2000,3 +2117,13 @@ var testmongoMock = &mocks.MongoClientMock{ }, }, } + +func findLogWithMessage(logs []*logrus.Entry, message string) []*logrus.Entry { + logToReturn := []*logrus.Entry{} + for _, log := range logs { + if log.Message == message { + logToReturn = append(logToReturn, log) + } + } + return logToReturn +} diff --git a/opaevaluator.go b/opaevaluator.go index bd3416c7..db6bcc98 100644 --- a/opaevaluator.go +++ b/opaevaluator.go @@ -250,7 +250,20 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error()) } - logger.Tracef("OPA partial evaluation in: %+v", time.Since(opaEvaluationTime)) + routerInfo, err := GetRouterInfo(evaluator.Context) + if err != nil { + return nil, err + } + + logger.WithFields(logrus.Fields{ + "evaluationTimeMicroseconds": time.Since(opaEvaluationTime).Microseconds(), + "policyName": evaluator.PolicyName, + "partialEval": true, + "allowed": true, + "matchedPath": routerInfo.MatchedPath, + "requestedPath": routerInfo.RequestedPath, + "method": routerInfo.Method, + }).Info("policy evaluation completed") client := opatranslator.OPAClient{} q, err := client.ProcessQuery(partialResults) @@ -272,9 +285,20 @@ func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, erro if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error()) } + routerInfo, err := GetRouterInfo(evaluator.Context) + if err != nil { + return nil, err + } + logger.WithFields(logrus.Fields{ - "policyName": evaluator.PolicyName, - }).Tracef("OPA evaluation in: %+v", time.Since(opaEvaluationTime)) + "evaluationTimeMicroseconds": time.Since(opaEvaluationTime).Microseconds(), + "policyName": evaluator.PolicyName, + "partialEval": false, + "allowed": results.Allowed(), + "matchedPath": routerInfo.MatchedPath, + "requestedPath": routerInfo.RequestedPath, + "method": routerInfo.Method, + }).Info("policy evaluation completed") if results.Allowed() { logger.WithFields(logrus.Fields{ @@ -289,7 +313,7 @@ func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, erro // - Expressions: list of list // - Bindings: object // e.g. [{Expressions:[[map["element": true]]] Bindings:map[]}] - // Since we are ALWAYS querying ONE specifc policy the result length could not be greater than 1 + // Since we are ALWAYS querying ONE specific policy the result length could not be greater than 1 if len(results) == 1 { if exprs := results[0].Expressions; len(exprs) == 1 { if value, ok := exprs[0].Value.([]interface{}); ok && value != nil && len(value) != 0 { diff --git a/opamiddleware.go b/opamiddleware.go index 86cdf319..42c1db8c 100644 --- a/opamiddleware.go +++ b/opamiddleware.go @@ -37,6 +37,7 @@ var ( ) type OPAModuleConfigKey struct{} +type RouterInfoKey struct{} type OPAModuleConfig struct { Name string @@ -58,13 +59,15 @@ func OPAMiddleware(opaModuleConfig *OPAModuleConfig, openAPISpec *OpenAPISpec, e path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) } + logger := glogger.Get(r.Context()) + permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { fields := logrus.Fields{} if err != nil { fields["error"] = logrus.Fields{"message": err.Error()} } - glogger.Get(r.Context()).WithFields(fields).Info("Proxying call to OAS Path even with no permission") + logger.WithFields(fields).Info("Proxying call to OAS Path even with no permission") next.ServeHTTP(w, r) return } @@ -86,7 +89,7 @@ func OPAMiddleware(opaModuleConfig *OPAModuleConfig, openAPISpec *OpenAPISpec, e if errors.Is(err, ErrNotFoundOASDefinition) { statusCode = http.StatusNotFound } - glogger.Get(r.Context()).WithFields(fields).Errorf(errorMessage) + logger.WithFields(fields).Errorf(errorMessage) failResponseWithCode(w, statusCode, technicalError, errorMessage) return } @@ -94,7 +97,7 @@ func OPAMiddleware(opaModuleConfig *OPAModuleConfig, openAPISpec *OpenAPISpec, e ctx := WithXPermission( WithOPAModuleConfig( WithPartialResultsEvaluators( - r.Context(), + WithRouterInfo(logger, r.Context(), r), policyEvaluators, ), opaModuleConfig, @@ -150,3 +153,39 @@ func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error return permission, nil } + +type RouterInfo struct { + MatchedPath string + RequestedPath string + Method string +} + +func WithRouterInfo(logger *logrus.Entry, requestContext context.Context, req *http.Request) context.Context { + pathTemplate := getPathTemplateOrDefaultToEmptyString(logger, req) + return context.WithValue(requestContext, RouterInfoKey{}, RouterInfo{ + MatchedPath: utils.SanitizeString(pathTemplate), + RequestedPath: utils.SanitizeString(req.URL.Path), + Method: utils.SanitizeString(req.Method), + }) +} + +func getPathTemplateOrDefaultToEmptyString(logger *logrus.Entry, req *http.Request) string { + var pathTemplate string + route := mux.CurrentRoute(req) + if route != nil { + var err error + if pathTemplate, err = route.GetPathTemplate(); err != nil { + logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("path template is empty") + return "" + } + } + return pathTemplate +} + +func GetRouterInfo(requestContext context.Context) (RouterInfo, error) { + routerInfo, ok := requestContext.Value(RouterInfoKey{}).(RouterInfo) + if !ok { + return RouterInfo{}, fmt.Errorf("no router info found") + } + return routerInfo, nil +} diff --git a/opamiddleware_test.go b/opamiddleware_test.go index e88c4ea0..cd4daefb 100644 --- a/opamiddleware_test.go +++ b/opamiddleware_test.go @@ -23,8 +23,11 @@ import ( "os" "testing" + "github.com/gorilla/mux" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/types" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "gotest.tools/v3/assert" ) @@ -378,6 +381,106 @@ func TestGetOPAModuleConfig(t *testing.T) { }) } +func TestRouterInfoContext(t *testing.T) { + nullLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(nullLogger) + + t.Run("GetRouterInfo fails because no key has been set", func(t *testing.T) { + ctx := context.Background() + routerInfo, err := GetRouterInfo(ctx) + require.EqualError(t, err, "no router info found") + require.Empty(t, routerInfo) + }) + + t.Run("WithRouterInfo not inside mux router - empty matched path", func(t *testing.T) { + ctx := context.Background() + req := httptest.NewRequest("GET", "/hello", nil) + ctx = WithRouterInfo(logger, ctx, req) + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "", + RequestedPath: "/hello", + Method: "GET", + }, routerInfo) + }) + + t.Run("WithRouterInfo without router path - matched path is empty", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.NewRoute().HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "", + RequestedPath: "/hello", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) + + t.Run("correctly get router info", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.HandleFunc("/hello/{name}", func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/hello/{name}", + RequestedPath: "/hello/my-username", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello/my-username", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) + + t.Run("correctly get router info with path prefix", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.PathPrefix("/hello/").HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/hello/", + RequestedPath: "/hello/my-username", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello/my-username", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) +} + func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { t.Helper() diff --git a/router_test.go b/router_test.go index 1aabe439..8bb4bdf7 100644 --- a/router_test.go +++ b/router_test.go @@ -209,6 +209,12 @@ func createContext( log, _ := test.NewNullLogger() partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(log)) + partialContext = context.WithValue(partialContext, RouterInfoKey{}, RouterInfo{ + MatchedPath: "/matched/path", + RequestedPath: "/requested/path", + Method: "GET", + }) + return partialContext } From a4e6b5b7aeec69539875367594de982039245cb6 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 5 Dec 2022 09:36:13 +0100 Subject: [PATCH 007/172] Upgrade version to v1.5.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c6f4bbfa..99b9248e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.5.2" +ENV SERVICE_VERSION="1.5.3" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 12b3662558a3adb9759414854b5bb25901814f40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 18:45:57 +0100 Subject: [PATCH 008/172] chore(deps): bump github.com/open-policy-agent/opa from 0.46.1 to 0.47.0 (#122) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.46.1 to 0.47.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.46.1...v0.47.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 655bc20a..198c6701 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.46.1 + github.com/open-policy-agent/opa v0.47.0 github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 diff --git a/go.sum b/go.sum index 3170aa1b..aab32c26 100644 --- a/go.sum +++ b/go.sum @@ -71,7 +71,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bytecodealliance/wasmtime-go v1.0.0 h1:9u9gqaUiaJeN5IoD1L7egD8atOnTGyJcNp8BhkL9cUU= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= 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= @@ -98,7 +98,7 @@ github.com/davidebianchi/go-jsonclient v1.3.0 h1:6579uXOHEDcClHK8b3j/xWbQV4Khpn7 github.com/davidebianchi/go-jsonclient v1.3.0/go.mod h1:lIOd35jxGfGENA/M0s1klHJgagIDj2KM1O6A/V5vpPI= github.com/davidebianchi/gswagger v0.6.1 h1:bkdH2FXw9Ojo4GZ8Wzvn0B+k9eGbA+2Pu9xxqkBI9Ls= github.com/davidebianchi/gswagger v0.6.1/go.mod h1:ThUr/leOezLiSTj6Bk7CqXBV5asBSCSrMkx4+VFyfH8= -github.com/dgraph-io/badger/v3 v3.2103.3 h1:s63J1pisDhKpzWslXFe+ChuthuZptpwTE6qEKoczPb4= +github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -373,8 +373,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.46.1 h1:iG998SLK0rzalex7Hyekeq17b9WtUexM0AuyHrQ7fCc= -github.com/open-policy-agent/opa v0.46.1/go.mod h1:DY9ZkCyz+DKoWI5gDuLw5rGC2RSb37QUeEf+9VjsWkI= +github.com/open-policy-agent/opa v0.47.0 h1:d6g0oDNLraIcWl9LXW8cBzRYf2zt7vSbPGEd2+8K3Lg= +github.com/open-policy-agent/opa v0.47.0/go.mod h1:cM7ngEoEdAIfyu9mOHaVcgLAHYkY6amrYfotm+BSkYQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -399,12 +399,12 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= From ee0411fb0937eea9f73dee5a312d7ee5176999a5 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 6 Dec 2022 09:36:07 +0100 Subject: [PATCH 009/172] feat: exported metrics endpoint (#123) * feat: exported metrics endpoint * fix: mustregister * feat: add custom metrics and test it * evaluation log to debug level * fix: indentation * expose metrics by default * fix: test --- go.mod | 9 +++ go.sum | 23 ++++++ handler_test.go | 122 +++++++++++++++++++++++-------- internal/config/env.go | 6 ++ internal/config/env_test.go | 1 + internal/metrics/metrics.go | 33 +++++++++ internal/metrics/metrics_test.go | 39 ++++++++++ internal/metrics/routes.go | 46 ++++++++++++ internal/metrics/routes_test.go | 48 ++++++++++++ main.go | 10 +++ main_test.go | 66 +++++++++++++++++ opaevaluator.go | 34 +++++++-- router_test.go | 3 + 13 files changed, 405 insertions(+), 35 deletions(-) create mode 100644 internal/metrics/metrics.go create mode 100644 internal/metrics/metrics_test.go create mode 100644 internal/metrics/routes.go create mode 100644 internal/metrics/routes_test.go diff --git a/go.mod b/go.mod index 198c6701..b625ccb9 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.47.0 + github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 @@ -23,12 +24,15 @@ require ( require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect @@ -40,6 +44,7 @@ require ( github.com/knadh/koanf v1.4.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mia-platform/jsonschema v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -50,6 +55,9 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect @@ -71,6 +79,7 @@ require ( golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect golang.org/x/text v0.4.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index aab32c26..42ad1db3 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,7 @@ 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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -133,10 +134,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -288,6 +291,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -332,6 +336,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjKD5OWJ1s= github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= github.com/mia-platform/glogger/v2 v2.1.3 h1:Qt/qHETYaFa+Oso9XZauysnHF9CE5816AQJmMo2Uh0Q= @@ -361,6 +366,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -398,25 +404,33 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -615,7 +629,10 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= 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= @@ -626,6 +643,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -699,11 +718,14 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -874,6 +896,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/handler_test.go b/handler_test.go index 0affa5a0..a3b8ba84 100644 --- a/handler_test.go +++ b/handler_test.go @@ -28,8 +28,11 @@ import ( "testing" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" @@ -200,7 +203,7 @@ func TestDirectProxyHandler(t *testing.T) { opaModuleConfig := &OPAModuleConfig{ Name: "example.rego", Content: `package policies - todo { input.request.body.hello == "world" }`, + todo { input.request.body.hello == "world" }`, } partialEvaluators, err := setupEvaluators(ctx, nil, &oas, opaModuleConfig, envs) @@ -550,10 +553,7 @@ allow { assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") }) - t.Run("data evalutation logs correctly added", func(t *testing.T) { - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") - + t.Run("data evaluation correctly added - logs and metrics", func(t *testing.T) { t.Run("no query generation", func(t *testing.T) { invoked := false server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -567,6 +567,9 @@ allow { serverURL, _ := url.Parse(server.URL) + partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + assert.Equal(t, err, nil, "Unexpected error") + ctx := createContext(t, ctx, config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, @@ -577,6 +580,7 @@ allow { ) log, hook := test.NewNullLogger() + log.Level = logrus.TraceLevel ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -589,18 +593,48 @@ allow { assert.Assert(t, invoked, "Handler was not invoked.") assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") - actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") - require.Len(t, actualLog, 1) - require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) - delete(actualLog[0].Data, "evaluationTimeMicroseconds") - require.Equal(t, logrus.Fields{ - "allowed": true, - "matchedPath": "/matched/path", - "method": "GET", - "partialEval": false, - "policyName": "todo", - "requestedPath": "/requested/path", - }, actualLog[0].Data) + t.Run("logs", func(t *testing.T) { + actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") + require.Len(t, actualLog, 1) + require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) + delete(actualLog[0].Data, "evaluationTimeMicroseconds") + require.Equal(t, logrus.Fields{ + "allowed": true, + "matchedPath": "/matched/path", + "method": "GET", + "partialEval": false, + "policyName": "todo", + "requestedPath": "/requested/path", + }, actualLog[0].Data) + }) + + t.Run("metrics", func(t *testing.T) { + m, err := metrics.GetFromContext(ctx) + require.NoError(t, err) + registry := prometheus.NewPedanticRegistry() + m.MustRegister(registry) + + problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") + require.NoError(t, err, problem) + require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") + + metadata := ` + # HELP test_rond_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. + # TYPE test_rond_policy_evaluation_duration_milliseconds histogram + ` + expected := ` + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="1"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="5"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="10"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="50"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="100"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="+Inf"} 1 + test_rond_policy_evaluation_duration_milliseconds_sum{policy_name="todo"} 0 + test_rond_policy_evaluation_duration_milliseconds_count{policy_name="todo"} 1 + ` + + require.NoError(t, testutil.CollectAndCompare(registry, strings.NewReader(metadata+expected), "test_rond_policy_evaluation_duration_milliseconds")) + }) }) t.Run("with query generation", func(t *testing.T) { @@ -639,6 +673,7 @@ allow { ) log, hook := test.NewNullLogger() + log.Level = logrus.TraceLevel ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -651,18 +686,47 @@ allow { assert.Assert(t, invoked, "Handler was not invoked.") assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") - actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") - require.Len(t, actualLog, 1) - require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) - delete(actualLog[0].Data, "evaluationTimeMicroseconds") - require.Equal(t, logrus.Fields{ - "allowed": true, - "matchedPath": "/matched/path", - "method": "GET", - "partialEval": true, - "policyName": "allow", - "requestedPath": "/requested/path", - }, actualLog[0].Data) + t.Run("logs", func(t *testing.T) { + actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") + require.Len(t, actualLog, 1) + require.NotEmpty(t, actualLog[0].Data["evaluationTimeMicroseconds"]) + delete(actualLog[0].Data, "evaluationTimeMicroseconds") + require.Equal(t, logrus.Fields{ + "allowed": true, + "matchedPath": "/matched/path", + "method": "GET", + "partialEval": true, + "policyName": "allow", + "requestedPath": "/requested/path", + }, actualLog[0].Data) + }) + + t.Run("metrics", func(t *testing.T) { + m, err := metrics.GetFromContext(ctx) + require.NoError(t, err) + registry := prometheus.NewPedanticRegistry() + m.MustRegister(registry) + + problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") + require.NoError(t, err, problem) + require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") + + metadata := ` + # HELP test_rond_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. + # TYPE test_rond_policy_evaluation_duration_milliseconds histogram +` + expected := ` + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="1"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="5"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="10"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="50"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="100"} 1 + test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="+Inf"} 1 + test_rond_policy_evaluation_duration_milliseconds_sum{policy_name="allow"} 0 + test_rond_policy_evaluation_duration_milliseconds_count{policy_name="allow"} 1 +` + require.NoError(t, testutil.CollectAndCompare(registry, strings.NewReader(metadata+expected), "test_rond_policy_evaluation_duration_milliseconds")) + }) }) }) } diff --git a/internal/config/env.go b/internal/config/env.go index d243d4dc..751ba76a 100644 --- a/internal/config/env.go +++ b/internal/config/env.go @@ -56,6 +56,7 @@ type EnvironmentVariables struct { DelayShutdownSeconds int Standalone bool AdditionalHeadersToProxy string + ExposeMetrics bool } var EnvVariablesConfig = []configlib.EnvConfig{ @@ -146,6 +147,11 @@ var EnvVariablesConfig = []configlib.EnvConfig{ Variable: "AdditionalHeadersToProxy", DefaultValue: "miauserid", }, + { + Key: "EXPOSE_METRICS", + Variable: "ExposeMetrics", + DefaultValue: "true", + }, } type EnvKey struct{} diff --git a/internal/config/env_test.go b/internal/config/env_test.go index 29751c4e..6865b87f 100644 --- a/internal/config/env_test.go +++ b/internal/config/env_test.go @@ -82,6 +82,7 @@ func TestGetEnvOrDie(t *testing.T) { OPAModulesDirectory: "/modules", AdditionalHeadersToProxy: "miauserid", + ExposeMetrics: true, } t.Run(`returns correctly - with TargetServiceHost`, func(t *testing.T) { diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go new file mode 100644 index 00000000..c8d802c9 --- /dev/null +++ b/internal/metrics/metrics.go @@ -0,0 +1,33 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" +) + +type Metrics struct { + PolicyEvaluationDurationMilliseconds *prometheus.HistogramVec +} + +func SetupMetrics(prefix string) Metrics { + m := Metrics{ + PolicyEvaluationDurationMilliseconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: prefix, + Name: "policy_evaluation_duration_milliseconds", + Help: "A histogram of the policy evaluation durations in milliseconds.", + Buckets: []float64{1, 5, 10, 50, 100}, + }, []string{"policy_name"}), + } + + return m +} + +func (m Metrics) MustRegister(reg prometheus.Registerer) Metrics { + reg.MustRegister( + collectors.NewGoCollector(), + collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), + m.PolicyEvaluationDurationMilliseconds, + ) + + return m +} diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go new file mode 100644 index 00000000..c54077c2 --- /dev/null +++ b/internal/metrics/metrics_test.go @@ -0,0 +1,39 @@ +package metrics + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" +) + +func TestMetrics(t *testing.T) { + t.Run("setup and register metrics", func(t *testing.T) { + m := SetupMetrics("test_prefix") + registry := prometheus.NewPedanticRegistry() + m.MustRegister(registry) + + t.Run("PolicyEvaluationDurationMilliseconds", func(t *testing.T) { + m.PolicyEvaluationDurationMilliseconds.WithLabelValues("myPolicyName").Observe(10) + + metadata := ` + # HELP test_prefix_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. + # TYPE test_prefix_policy_evaluation_duration_milliseconds histogram +` + expected := ` + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="1"} 0 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="5"} 0 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="10"} 1 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="50"} 1 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="100"} 1 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="+Inf"} 1 + test_prefix_policy_evaluation_duration_milliseconds_sum{policy_name="myPolicyName"} 10 + test_prefix_policy_evaluation_duration_milliseconds_count{policy_name="myPolicyName"} 1 +` + + require.NoError(t, testutil.CollectAndCompare(m.PolicyEvaluationDurationMilliseconds, strings.NewReader(metadata+expected), "test_prefix_policy_evaluation_duration_milliseconds")) + }) + }) +} diff --git a/internal/metrics/routes.go b/internal/metrics/routes.go new file mode 100644 index 00000000..b58ca841 --- /dev/null +++ b/internal/metrics/routes.go @@ -0,0 +1,46 @@ +package metrics + +import ( + "context" + "fmt" + "net/http" + + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func MetricsRoute(r *mux.Router, registry *prometheus.Registry) { + r.Handle("/metrics", promhttp.InstrumentMetricHandler( + registry, + promhttp.HandlerFor(registry, promhttp.HandlerOpts{ + Registry: registry, + EnableOpenMetrics: true, + }), + )) +} + +type metricsContextKey struct{} + +// RequestMiddleware is a gorilla/mux middleware used to inject +// metrics struct into requests. +func RequestMiddleware(m Metrics) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := WithValue(r.Context(), m) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + +func WithValue(ctx context.Context, m Metrics) context.Context { + return context.WithValue(ctx, metricsContextKey{}, m) +} + +func GetFromContext(ctx context.Context) (Metrics, error) { + m, ok := ctx.Value(metricsContextKey{}).(Metrics) + if !ok { + return Metrics{}, fmt.Errorf("invalid metrics in context") + } + return m, nil +} diff --git a/internal/metrics/routes_test.go b/internal/metrics/routes_test.go new file mode 100644 index 00000000..e41563cf --- /dev/null +++ b/internal/metrics/routes_test.go @@ -0,0 +1,48 @@ +package metrics + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRequestMiddleware(t *testing.T) { + expectedMetrics := SetupMetrics("test_prefix") + + t.Run("set metrics in context", func(t *testing.T) { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m, err := GetFromContext(r.Context()) + require.NoError(t, err) + require.Equal(t, expectedMetrics, m) + + w.WriteHeader(202) + }) + + handlerToTest := RequestMiddleware(expectedMetrics).Middleware(handler) + + w := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/path", nil) + handlerToTest.ServeHTTP(w, req) + + require.Equal(t, http.StatusAccepted, w.Result().StatusCode) + }) +} + +func TestGetFromContext(t *testing.T) { + t.Run("ok", func(t *testing.T) { + expectedMetrics := SetupMetrics("test_prefix") + ctx := WithValue(context.Background(), expectedMetrics) + m, err := GetFromContext(ctx) + require.NoError(t, err) + require.Equal(t, expectedMetrics, m) + }) + + t.Run("metrics not in context", func(t *testing.T) { + m, err := GetFromContext(context.Background()) + require.EqualError(t, err, "invalid metrics in context") + require.Empty(t, m) + }) +} diff --git a/main.go b/main.go index 3dae0452..e91f3bc6 100644 --- a/main.go +++ b/main.go @@ -26,8 +26,10 @@ import ( swagger "github.com/davidebianchi/gswagger" "github.com/davidebianchi/gswagger/apirouter" "github.com/getkin/kin-openapi/openapi3" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" "github.com/gorilla/mux" @@ -151,6 +153,14 @@ func setupRouter( serviceName := "rönd" StatusRoutes(router, serviceName, env.ServiceVersion) + registry := prometheus.NewRegistry() + m := metrics.SetupMetrics("rond") + if env.ExposeMetrics { + m.MustRegister(registry) + metrics.MetricsRoute(router, registry) + } + router.Use(metrics.RequestMiddleware(m)) + router.Use(config.RequestMiddlewareEnvironments(env)) evalRouter := router.NewRoute().Subrouter() diff --git a/main_test.go b/main_test.go index 7476cbf8..f4a2d07f 100644 --- a/main_test.go +++ b/main_test.go @@ -1806,3 +1806,69 @@ filter_policy { assert.Assert(t, string(responseBody) != "") }) } + +func TestSetupRouterMetrics(t *testing.T) { + defer gock.Off() + defer gock.DisableNetworkingFilters() + defer gock.Flush() + + log, _ := test.NewNullLogger() + ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) + + env := config.EnvironmentVariables{ + Standalone: true, + TargetServiceHost: "my-service:4444", + PathPrefixStandalone: "/my-prefix", + ServiceVersion: "my-version", + BindingsCrudServiceURL: "http://crud:3030", + AdditionalHeadersToProxy: "miauserid", + ExposeMetrics: true, + } + opa := &OPAModuleConfig{ + Name: "policies", + Content: `package policies +test_policy { true } + +filter_policy { + query := data.resources[_] + query.answer = 42 +} +`, + } + oas := &OpenAPISpec{ + Paths: OpenAPIPaths{ + "/evalapi": PathVerbs{ + "get": VerbConfig{ + PermissionV2: &RondConfig{ + RequestFlow: RequestFlow{PolicyName: "test_policy"}, + }, + }, + }, + "/evalfilter": PathVerbs{ + "get": VerbConfig{ + PermissionV2: &RondConfig{ + RequestFlow: RequestFlow{PolicyName: "filter_policy", GenerateQuery: true, QueryOptions: QueryOptions{HeaderName: "my-query"}}, + }, + }, + }, + }, + } + + var mongoClient *mongoclient.MongoClient + evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, env) + require.NoError(t, err, "unexpected error") + + router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + require.NoError(t, err, "unexpected error") + + t.Run("metrics API exposed correctly", func(t *testing.T) { + w := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/metrics", nil) + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Result().StatusCode) + + responseBody := getResponseBody(t, w) + require.Contains(t, string(responseBody), "go_gc_duration_seconds") + }) +} diff --git a/opaevaluator.go b/opaevaluator.go index db6bcc98..376c6ffa 100644 --- a/opaevaluator.go +++ b/opaevaluator.go @@ -25,7 +25,9 @@ import ( "strings" "time" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/types" @@ -245,7 +247,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con } func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitive.M, error) { - opaEvaluationTime := time.Now() + opaEvaluationTimeStart := time.Now() partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.Context) if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error()) @@ -254,16 +256,26 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv if err != nil { return nil, err } + m, err := metrics.GetFromContext(evaluator.Context) + if err != nil { + return nil, err + } + + opaEvaluationTime := time.Since(opaEvaluationTimeStart) + + m.PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + "policy_name": evaluator.PolicyName, + }).Observe(float64(opaEvaluationTime.Milliseconds())) logger.WithFields(logrus.Fields{ - "evaluationTimeMicroseconds": time.Since(opaEvaluationTime).Microseconds(), + "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": true, "allowed": true, "matchedPath": routerInfo.MatchedPath, "requestedPath": routerInfo.RequestedPath, "method": routerInfo.Method, - }).Info("policy evaluation completed") + }).Debug("policy evaluation completed") client := opatranslator.OPAClient{} q, err := client.ProcessQuery(partialResults) @@ -280,7 +292,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv } func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, error) { - opaEvaluationTime := time.Now() + opaEvaluationTimeStart := time.Now() results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context) if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error()) @@ -289,16 +301,26 @@ func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, erro if err != nil { return nil, err } + m, err := metrics.GetFromContext(evaluator.Context) + if err != nil { + return nil, err + } + + opaEvaluationTime := time.Since(opaEvaluationTimeStart) + + m.PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + "policy_name": evaluator.PolicyName, + }).Observe(float64(opaEvaluationTime.Milliseconds())) logger.WithFields(logrus.Fields{ - "evaluationTimeMicroseconds": time.Since(opaEvaluationTime).Microseconds(), + "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": false, "allowed": results.Allowed(), "matchedPath": routerInfo.MatchedPath, "requestedPath": routerInfo.RequestedPath, "method": routerInfo.Method, - }).Info("policy evaluation completed") + }).Debug("policy evaluation completed") if results.Allowed() { logger.WithFields(logrus.Fields{ diff --git a/router_test.go b/router_test.go index 8bb4bdf7..f4ca8808 100644 --- a/router_test.go +++ b/router_test.go @@ -24,6 +24,7 @@ import ( "github.com/mia-platform/glogger/v2" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -215,6 +216,8 @@ func createContext( Method: "GET", }) + partialContext = metrics.WithValue(partialContext, metrics.SetupMetrics("test_rond")) + return partialContext } From c7b0afda7e989b33b2e0123ace76437965e14aba Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 6 Dec 2022 09:56:07 +0100 Subject: [PATCH 010/172] remove flaky test --- handler_test.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/handler_test.go b/handler_test.go index a3b8ba84..2e265929 100644 --- a/handler_test.go +++ b/handler_test.go @@ -617,23 +617,6 @@ allow { problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") require.NoError(t, err, problem) require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") - - metadata := ` - # HELP test_rond_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. - # TYPE test_rond_policy_evaluation_duration_milliseconds histogram - ` - expected := ` - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="1"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="5"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="10"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="50"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="100"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="todo",le="+Inf"} 1 - test_rond_policy_evaluation_duration_milliseconds_sum{policy_name="todo"} 0 - test_rond_policy_evaluation_duration_milliseconds_count{policy_name="todo"} 1 - ` - - require.NoError(t, testutil.CollectAndCompare(registry, strings.NewReader(metadata+expected), "test_rond_policy_evaluation_duration_milliseconds")) }) }) @@ -710,22 +693,6 @@ allow { problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") require.NoError(t, err, problem) require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") - - metadata := ` - # HELP test_rond_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. - # TYPE test_rond_policy_evaluation_duration_milliseconds histogram -` - expected := ` - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="1"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="5"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="10"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="50"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="100"} 1 - test_rond_policy_evaluation_duration_milliseconds_bucket{policy_name="allow",le="+Inf"} 1 - test_rond_policy_evaluation_duration_milliseconds_sum{policy_name="allow"} 0 - test_rond_policy_evaluation_duration_milliseconds_count{policy_name="allow"} 1 -` - require.NoError(t, testutil.CollectAndCompare(registry, strings.NewReader(metadata+expected), "test_rond_policy_evaluation_duration_milliseconds")) }) }) }) From ec9971f5092348e56c86276ede0f7caa270c15f4 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 6 Dec 2022 11:58:50 +0100 Subject: [PATCH 011/172] refactor: remove gotest dependency (#124) * remove gotest * remove gotest * fix: test --- go.mod | 2 - go.sum | 3 - handler_test.go | 378 +++++++++++------------ internal/config/env_test.go | 13 +- internal/mongoclient/mongoclient_test.go | 108 +++---- internal/testutils/mongodb.go | 16 +- main_test.go | 37 ++- opa_transport_test.go | 11 +- opaevaluator_test.go | 21 +- opamiddleware_test.go | 65 ++-- openapi_utils_test.go | 133 ++++---- router_test.go | 66 ++-- standalone_apis_test.go | 61 ++-- statusroutes_test.go | 19 +- utilities_test.go | 30 +- 15 files changed, 474 insertions(+), 489 deletions(-) diff --git a/go.mod b/go.mod index b625ccb9..05cebd2d 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/uptrace/bunrouter v1.0.19 go.mongodb.org/mongo-driver v1.11.0 gopkg.in/h2non/gock.v1 v1.1.2 - gotest.tools/v3 v3.4.0 ) require ( @@ -34,7 +33,6 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.8 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect diff --git a/go.sum b/go.sum index 42ad1db3..239f7955 100644 --- a/go.sum +++ b/go.sum @@ -205,7 +205,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -927,8 +926,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/handler_test.go b/handler_test.go index 2e265929..28c3e8b7 100644 --- a/handler_test.go +++ b/handler_test.go @@ -45,7 +45,6 @@ import ( "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" - "gotest.tools/v3/assert" ) func TestDirectProxyHandler(t *testing.T) { @@ -87,14 +86,14 @@ func TestDirectProxyHandler(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.URL.Path, "/api", "Mocked Backend: Unexpected path of request url") - assert.Equal(t, r.URL.RawQuery, "mockQuery=iamquery", "Mocked Backend: Unexpected rawQuery of request url") + require.Equal(t, "/api", r.URL.Path, "Mocked Backend: Unexpected path of request url") + require.Equal(t, "mockQuery=iamquery", r.URL.RawQuery, "Mocked Backend: Unexpected rawQuery of request url") w.WriteHeader(http.StatusOK) })) defer server.Close() partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -107,14 +106,14 @@ func TestDirectProxyHandler(t *testing.T) { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("sends request with custom headers", func(t *testing.T) { @@ -123,11 +122,11 @@ func TestDirectProxyHandler(t *testing.T) { mockHeaderValue := "mocked value" partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(mockHeader), mockHeaderValue, "Mocked Backend: Mocked Header not found") + require.Equal(t, mockHeaderValue, r.Header.Get(mockHeader), "Mocked Backend: Mocked Header not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -143,14 +142,14 @@ func TestDirectProxyHandler(t *testing.T) { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(mockHeader, mockHeaderValue) w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("sends request with body", func(t *testing.T) { @@ -158,14 +157,14 @@ func TestDirectProxyHandler(t *testing.T) { mockBodySting := "I am a body" partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true defer r.Body.Close() buf, err := io.ReadAll(r.Body) - assert.Equal(t, err, nil, "Mocked backend: Unexpected error") - assert.Equal(t, string(buf), mockBodySting, "Mocked backend: Unexpected Body received") + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -184,17 +183,17 @@ func TestDirectProxyHandler(t *testing.T) { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") buf, err := io.ReadAll(w.Body) - assert.Equal(t, err, nil, "Unexpected error to read body response") - assert.Equal(t, string(buf), "Mocked Backend Body Example", "Unexpected body response") + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) t.Run("sends request with body after serialization in rego input", func(t *testing.T) { @@ -207,13 +206,13 @@ func TestDirectProxyHandler(t *testing.T) { } partialEvaluators, err := setupEvaluators(ctx, nil, &oas, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true defer r.Body.Close() buf, err := io.ReadAll(r.Body) - assert.Equal(t, err, nil, "Mocked backend: Unexpected error") - assert.Equal(t, string(buf), mockBodySting, "Mocked backend: Unexpected Body received") + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -233,16 +232,16 @@ func TestDirectProxyHandler(t *testing.T) { r, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://www.example.com:8080/api", body) r.Header.Set(ContentTypeHeaderKey, "application/json") - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") buf, err := io.ReadAll(w.Body) - assert.Equal(t, err, nil, "Unexpected error to read body response") - assert.Equal(t, string(buf), "Mocked Backend Body Example", "Unexpected body response") + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) t.Run("sends filter query", func(t *testing.T) { @@ -276,11 +275,11 @@ allow { invoked = true defer r.Body.Close() buf, err := io.ReadAll(r.Body) - assert.Equal(t, err, nil, "Mocked backend: Unexpected error") - assert.Equal(t, string(buf), mockBodySting, "Mocked backend: Unexpected Body received") + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") filterQuery := r.Header.Get("rowfilterquery") expectedQuery := `{"$or":[{"$and":[{"manager":{"$eq":"manager_test"}}]},{"$and":[{"salary":{"$gt":0}}]}]}` - assert.Equal(t, expectedQuery, filterQuery) + require.Equal(t, expectedQuery, filterQuery) w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -291,7 +290,7 @@ allow { opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -304,7 +303,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set(ContentTypeHeaderKey, "text/plain") @@ -312,11 +311,11 @@ allow { rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") buf, err := io.ReadAll(w.Body) - assert.Equal(t, err, nil, "Unexpected error to read body response") - assert.Equal(t, string(buf), "Mocked Backend Body Example", "Unexpected body response") + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) t.Run("sends empty filter query", func(t *testing.T) { @@ -346,11 +345,11 @@ allow { invoked = true defer r.Body.Close() buf, err := io.ReadAll(r.Body) - assert.Equal(t, err, nil, "Mocked backend: Unexpected error") - assert.Equal(t, string(buf), mockBodySting, "Mocked backend: Unexpected Body received") + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") filterQuery := r.Header.Get("rowfilterquery") expectedQuery := `` - assert.Equal(t, expectedQuery, filterQuery) + require.Equal(t, expectedQuery, filterQuery) w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -363,7 +362,7 @@ allow { opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, @@ -374,7 +373,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set(ContentTypeHeaderKey, "text/plain") @@ -382,11 +381,11 @@ allow { rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") buf, err := io.ReadAll(w.Body) - assert.Equal(t, err, nil, "Unexpected error to read body response") - assert.Equal(t, string(buf), "Mocked Backend Body Example", "Unexpected body response") + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) t.Run("sends empty filter query with application-json as content-type", func(t *testing.T) { @@ -412,7 +411,7 @@ allow { opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, @@ -423,17 +422,17 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(ContentTypeHeaderKey, "application/json") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") - assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") buf, err := io.ReadAll(w.Body) - assert.Equal(t, err, nil, "Unexpected error to read body response") - assert.Equal(t, string(buf), "[]", "Unexpected body response") + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "[]", string(buf), "Unexpected body response") }) t.Run("sends empty filter query with text/plain as content-type", func(t *testing.T) { @@ -460,7 +459,7 @@ allow { opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, @@ -471,14 +470,14 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, !invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "Handler was not invoked.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) t.Run("filter query return not allow", func(t *testing.T) { @@ -512,11 +511,11 @@ allow { invoked = true defer r.Body.Close() buf, err := io.ReadAll(r.Body) - assert.Equal(t, err, nil, "Mocked backend: Unexpected error") - assert.Equal(t, string(buf), mockBodySting, "Mocked backend: Unexpected Body received") + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") filterQuery := r.Header.Get("rowfilterquery") expectedQuery := `` - assert.Equal(t, expectedQuery, filterQuery) + require.Equal(t, expectedQuery, filterQuery) w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -529,7 +528,7 @@ allow { opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, @@ -540,7 +539,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set(ContentTypeHeaderKey, "text/plain") @@ -548,9 +547,9 @@ allow { rbacHandler(w, r) - assert.Assert(t, !invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") - assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") + require.True(t, !invoked, "Handler was not invoked.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") }) t.Run("data evaluation correctly added - logs and metrics", func(t *testing.T) { @@ -559,8 +558,8 @@ allow { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.URL.Path, "/api", "Mocked Backend: Unexpected path of request url") - assert.Equal(t, r.URL.RawQuery, "mockQuery=iamquery", "Mocked Backend: Unexpected rawQuery of request url") + require.Equal(t, "/api", r.URL.Path, "Mocked Backend: Unexpected path of request url") + require.Equal(t, "mockQuery=iamquery", r.URL.RawQuery, "Mocked Backend: Unexpected rawQuery of request url") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -568,7 +567,7 @@ allow { serverURL, _ := url.Parse(server.URL) partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, ctx, @@ -584,14 +583,14 @@ allow { ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") t.Run("logs", func(t *testing.T) { actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") @@ -625,8 +624,8 @@ allow { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.URL.Path, "/api", "Mocked Backend: Unexpected path of request url") - assert.Equal(t, r.URL.RawQuery, "mockQuery=iamquery", "Mocked Backend: Unexpected rawQuery of request url") + require.Equal(t, "/api", r.URL.Path, "Mocked Backend: Unexpected path of request url") + require.Equal(t, "mockQuery=iamquery", r.URL.RawQuery, "Mocked Backend: Unexpected rawQuery of request url") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -644,7 +643,7 @@ allow { }`, } partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) - require.NoError(t, err) + require.NoError(t, err, "Unexpected error") ctx := createContext(t, ctx, @@ -660,14 +659,14 @@ allow { ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") t.Run("logs", func(t *testing.T) { actualLog := findLogWithMessage(hook.AllEntries(), "policy evaluation completed") @@ -735,7 +734,7 @@ func TestStandaloneMode(t *testing.T) { t.Run("ok", func(t *testing.T) { partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), env, @@ -746,13 +745,13 @@ func TestStandaloneMode(t *testing.T) { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("sends filter query", func(t *testing.T) { @@ -784,7 +783,7 @@ allow { body := strings.NewReader(mockBodySting) partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -796,7 +795,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set("Content-Type", "text/plain") @@ -804,10 +803,10 @@ allow { rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") filterQuery := r.Header.Get("rowfilterquery") expectedQuery := `{"$or":[{"$and":[{"manager":{"$eq":"manager_test"}}]},{"$and":[{"salary":{"$gt":0}}]}]}` - assert.Equal(t, expectedQuery, filterQuery) + require.Equal(t, expectedQuery, filterQuery) }) t.Run("sends empty filter query", func(t *testing.T) { @@ -834,7 +833,7 @@ allow { body := strings.NewReader(mockBodySting) partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -846,7 +845,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set("Content-Type", "text/plain") @@ -854,10 +853,10 @@ allow { rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") filterQuery := r.Header.Get("rowfilterquery") expectedQuery := `` - assert.Equal(t, expectedQuery, filterQuery) + require.Equal(t, expectedQuery, filterQuery) }) t.Run("filter query return not allow", func(t *testing.T) { @@ -886,7 +885,7 @@ allow { mockBodySting := "I am a body" partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") body := strings.NewReader(mockBodySting) @@ -900,7 +899,7 @@ allow { ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") r.Header.Set("Content-Type", "text/plain") @@ -908,7 +907,7 @@ allow { rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) } @@ -919,7 +918,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { "key": []string{"is", "not"}, } mockedUserPropertiesStringified, err := json.Marshal(mockedUserProperties) - assert.NilError(t, err) + require.NoError(t, err) userGroupsHeaderKey := "miausergroups" mockedUserGroups := []string{"group1", "group2"} @@ -929,7 +928,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mockedClientType := "fakeClient" userIdHeaderKey := "miauserid" - assert.NilError(t, err) + require.NoError(t, err) opaModule := &OPAModuleConfig{ Name: "example.rego", @@ -969,7 +968,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(mockHeader), mockHeaderValue, "Mocked Backend: Mocked Header not found") + require.Equal(t, mockHeaderValue, r.Header.Get(mockHeader), "Mocked Backend: Mocked Header not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -984,7 +983,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -998,24 +997,24 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { t.Run("request respects the policy", func(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(mockHeader, mockHeaderValue) rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("request does not have the required header", func(t *testing.T) { invoked = false w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") rbacHandler(w, r) - assert.Assert(t, !invoked, "The policy did not block the request as expected") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "The policy did not block the request as expected") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) }) @@ -1028,7 +1027,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1042,24 +1041,24 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { t.Run("request respects the policy", func(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(mockHeader, mockHeaderValue) rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("request does not have the required header", func(t *testing.T) { invoked = false w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") rbacHandler(w, r) - assert.Assert(t, !invoked, "The policy did not block the request as expected") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "The policy did not block the request as expected") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) }) }) @@ -1069,9 +1068,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(userPropertiesHeaderKey), string(mockedUserPropertiesStringified), "Mocked User properties not found") - assert.Equal(t, r.Header.Get(userGroupsHeaderKey), mockedUserGroupsHeaderValue, "Mocked User groups not found") - assert.Equal(t, r.Header.Get(clientTypeHeaderKey), mockedClientType, "Mocked client type not found") + require.Equal(t, string(mockedUserPropertiesStringified), r.Header.Get(userPropertiesHeaderKey), "Mocked User properties not found") + require.Equal(t, mockedUserGroupsHeaderValue, r.Header.Get(userGroupsHeaderKey), "Mocked User groups not found") + require.Equal(t, mockedClientType, r.Header.Get(clientTypeHeaderKey), "Mocked client type not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -1089,7 +1088,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }`, mockedUserProperties["my"], mockedClientType), } partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1108,26 +1107,26 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { t.Run("request respects the policy", func(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) r.Header.Set(userGroupsHeaderKey, mockedUserGroupsHeaderValue) r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("request does not have the required header", func(t *testing.T) { invoked = false w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") rbacHandler(w, r) - assert.Assert(t, !invoked, "The policy did not block the request as expected") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "The policy did not block the request as expected") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) }) @@ -1138,7 +1137,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(mockHeader), mockHeaderValue, "Mocked Backend: Mocked Header not found") + require.Equal(t, mockHeaderValue, r.Header.Get(mockHeader), "Mocked Backend: Mocked Header not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -1173,7 +1172,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } partialEvaluators, err := setupEvaluators(ctx, nil, &oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1187,13 +1186,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { t.Run("request respects the policy", func(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(mockHeader, mockHeaderValue) rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) }) }) @@ -1242,8 +1241,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } rolesIds := mongoclient.RolesIDsFromBindings(bindings) expected := []string{"role1", "role2", "role3", "role4"} - assert.Assert(t, reflect.DeepEqual(rolesIds, expected), - "Error while getting permissions") + require.True(t, reflect.DeepEqual(rolesIds, expected), "Error while getting permissions") }) t.Run("TestHandlerWithUserPermissionsRetrievalFromMongoDB", func(t *testing.T) { @@ -1264,7 +1262,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1286,7 +1284,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userGroupsHeaderKey, mockedUserGroupsHeaderValue) r.Header.Set(userIdHeaderKey, "miauserid") @@ -1294,8 +1292,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { rbacHandler(w, r) testutils.AssertResponseError(t, w, http.StatusInternalServerError, "") - assert.Assert(t, !invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError, "Unexpected status code.") + require.True(t, !invoked, "Handler was not invoked.") + require.Equal(t, w.Result().StatusCode, http.StatusInternalServerError, "Unexpected status code.") }) t.Run("return 500 if some errors occurs while querying mongoDB", func(t *testing.T) { @@ -1315,7 +1313,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1337,7 +1335,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) r.Header.Set(userIdHeaderKey, "miauserid") @@ -1345,8 +1343,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { rbacHandler(w, r) testutils.AssertResponseFullErrorMessages(t, w, http.StatusInternalServerError, "user bindings retrieval failed", GENERIC_BUSINESS_ERROR_MESSAGE) - assert.Assert(t, !invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError, "Unexpected status code.") + require.True(t, !invoked, "Handler was not invoked.") + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode, "Unexpected status code.") }) t.Run("return 403 if user bindings and roles retrieval is ok but user has not the required permission", func(t *testing.T) { @@ -1405,7 +1403,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1427,7 +1425,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") // Missing mia user properties required r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) @@ -1436,7 +1434,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { rbacHandler(w, r) testutils.AssertResponseFullErrorMessages(t, w, http.StatusForbidden, "RBAC policy evaluation failed", NO_PERMISSIONS_ERROR_MESSAGE) - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) t.Run("return 200", func(t *testing.T) { @@ -1444,10 +1442,10 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(userPropertiesHeaderKey), string(mockedUserPropertiesStringified), "Mocked User properties not found") - assert.Equal(t, r.Header.Get(userGroupsHeaderKey), string(mockedUserGroupsHeaderValue), "Mocked User groups not found") - assert.Equal(t, r.Header.Get(clientTypeHeaderKey), mockedClientType, "Mocked client type not found") - assert.Equal(t, r.Header.Get(userIdHeaderKey), userIdHeaderKey, "Mocked user id not found") + require.Equal(t, string(mockedUserPropertiesStringified), r.Header.Get(userPropertiesHeaderKey), "Mocked User properties not found") + require.Equal(t, string(mockedUserGroupsHeaderValue), r.Header.Get(userGroupsHeaderKey), "Mocked User groups not found") + require.Equal(t, mockedClientType, r.Header.Get(clientTypeHeaderKey), "Mocked client type not found") + require.Equal(t, userIdHeaderKey, r.Header.Get(userIdHeaderKey), "Mocked user id not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -1497,7 +1495,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1521,15 +1519,15 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) r.Header.Set(userIdHeaderKey, "miauserid") rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("return 200 with policy on bindings and roles", func(t *testing.T) { @@ -1551,10 +1549,10 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(userPropertiesHeaderKey), string(mockedUserPropertiesStringified), "Mocked User properties not found") - assert.Equal(t, r.Header.Get(userGroupsHeaderKey), string(mockedUserGroupsHeaderValue), "Mocked User groups not found") - assert.Equal(t, r.Header.Get(clientTypeHeaderKey), mockedClientType, "Mocked client type not found") - assert.Equal(t, r.Header.Get(userIdHeaderKey), userIdHeaderKey, "Mocked user id not found") + require.Equal(t, string(mockedUserPropertiesStringified), r.Header.Get(userPropertiesHeaderKey), "Mocked User properties not found") + require.Equal(t, string(mockedUserGroupsHeaderValue), r.Header.Get(userGroupsHeaderKey), "Mocked User groups not found") + require.Equal(t, mockedClientType, r.Header.Get(clientTypeHeaderKey), "Mocked client type not found") + require.Equal(t, userIdHeaderKey, r.Header.Get(userIdHeaderKey), "Mocked user id not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -1605,7 +1603,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1628,15 +1626,15 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) r.Header.Set(userIdHeaderKey, "miauserid") rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("return 200 without user header", func(t *testing.T) { @@ -1664,7 +1662,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -1686,12 +1684,12 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { w := httptest.NewRecorder() r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) rbacHandler(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("return 200 with policy on pathParams", func(t *testing.T) { @@ -1712,10 +1710,10 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.Header.Get(userPropertiesHeaderKey), string(mockedUserPropertiesStringified), "Mocked User properties not found") - assert.Equal(t, r.Header.Get(userGroupsHeaderKey), string(mockedUserGroupsHeaderValue), "Mocked User groups not found") - assert.Equal(t, r.Header.Get(clientTypeHeaderKey), mockedClientType, "Mocked client type not found") - assert.Equal(t, r.Header.Get(userIdHeaderKey), userIdHeaderKey, "Mocked user id not found") + require.Equal(t, string(mockedUserPropertiesStringified), r.Header.Get(userPropertiesHeaderKey), "Mocked User properties not found") + require.Equal(t, string(mockedUserGroupsHeaderValue), r.Header.Get(userGroupsHeaderKey), "Mocked User groups not found") + require.Equal(t, mockedClientType, r.Header.Get(clientTypeHeaderKey), "Mocked client type not found") + require.Equal(t, userIdHeaderKey, r.Header.Get(userIdHeaderKey), "Mocked user id not found") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -1729,7 +1727,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1756,15 +1754,15 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { "customerId": customerId, "productId": productId, }) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) r.Header.Set(userIdHeaderKey, "miauserid") rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) }) } @@ -1802,10 +1800,10 @@ project.tenantId == "1234" mongoMock := &mocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { - assert.Equal(t, collectionName, "projects") - assert.DeepEqual(t, query, map[string]interface{}{ + require.Equal(t, "projects", collectionName) + require.Equal(t, map[string]interface{}{ "projectId": "1234", - }) + }, query) }, FindOneResult: map[string]interface{}{"tenantId": "1234"}, } @@ -1819,7 +1817,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1832,14 +1830,14 @@ project.tenantId == "1234" ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, invoked, "Handler was not invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("blocks for mongo error", func(t *testing.T) { @@ -1852,10 +1850,10 @@ project.tenantId == "1234" mongoMock := &mocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { - assert.Equal(t, collectionName, "projects") - assert.DeepEqual(t, query, map[string]interface{}{ + require.Equal(t, "projects", collectionName) + require.Equal(t, map[string]interface{}{ "projectId": "1234", - }) + }, query) }, FindOneError: fmt.Errorf("FAILED MONGO QUERY"), } @@ -1865,7 +1863,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1878,14 +1876,14 @@ project.tenantId == "1234" ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, !invoked, "Handler was invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "Handler was invoked.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) t.Run("blocks for mongo not found", func(t *testing.T) { @@ -1898,10 +1896,10 @@ project.tenantId == "1234" mongoMock := &mocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { - assert.Equal(t, collectionName, "projects") - assert.DeepEqual(t, query, map[string]interface{}{ + require.Equal(t, "projects", collectionName) + require.Equal(t, map[string]interface{}{ "projectId": "1234", - }) + }, query) }, FindOneResult: nil, // not found corresponds to a nil interface. } @@ -1911,7 +1909,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -1924,14 +1922,14 @@ project.tenantId == "1234" ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() rbacHandler(w, r) - assert.Assert(t, !invoked, "Handler was invoked.") - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.True(t, !invoked, "Handler was invoked.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) } @@ -1965,7 +1963,7 @@ column_policy{ ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) @@ -1974,20 +1972,20 @@ column_policy{ t.Run("create evaluator with allowPolicy", func(t *testing.T) { evaluator, err := createQueryEvaluator(context.Background(), logger, r, envs, permission.AllowPermission, inputBytes, nil) - assert.Assert(t, evaluator != nil) - assert.Equal(t, err, nil, "Unexpected status code.") + require.True(t, evaluator != nil) + require.NoError(t, err, "Unexpected status code.") }) t.Run("create evaluator with policy for column filtering", func(t *testing.T) { evaluator, err := createQueryEvaluator(context.Background(), logger, r, envs, permission.ResponseFilter.Policy, inputBytes, nil) - assert.Assert(t, evaluator != nil) - assert.Equal(t, err, nil, "Unexpected status code.") + require.True(t, evaluator != nil) + require.NoError(t, err, "Unexpected status code.") }) } func BenchmarkEvaluateRequest(b *testing.B) { moduleConfig, err := loadRegoModule("./mocks/bench-policies") - assert.NilError(b, err, "Unexpected error") + require.NoError(b, err, "Unexpected error") permission := &RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_view_project"}} queryString := fmt.Sprintf("data.policies.%s", permission.RequestFlow.PolicyName) @@ -2045,7 +2043,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { b.StartTimer() EvaluateRequest(req, envs, recorder, partialEvaluators, permission) b.StopTimer() - assert.Equal(b, recorder.Code, http.StatusOK) + require.Equal(b, http.StatusOK, recorder.Code) } } diff --git a/internal/config/env_test.go b/internal/config/env_test.go index 6865b87f..c01f0908 100644 --- a/internal/config/env_test.go +++ b/internal/config/env_test.go @@ -23,7 +23,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "gotest.tools/v3/assert" ) func TestRequestMiddlewareEnvironments(t *testing.T) { @@ -34,8 +33,8 @@ func TestRequestMiddlewareEnvironments(t *testing.T) { builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { env, ok := r.Context().Value(EnvKey{}).(EnvironmentVariables) - assert.Assert(t, ok, "Unexpected type in context.") - assert.Equal(t, env.TargetServiceHost, "localhost:3000", "Unexpected session duration seconds env variable.") + require.True(t, ok, "Unexpected type in context.") + require.Equal(t, "localhost:3000", env.TargetServiceHost, "Unexpected session duration seconds env variable.") w.WriteHeader(http.StatusOK) })) @@ -43,7 +42,7 @@ func TestRequestMiddlewareEnvironments(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) } @@ -51,7 +50,7 @@ func TestGetEnv(t *testing.T) { t.Run(`GetEnv fails because no key has been passed`, func(t *testing.T) { ctx := context.Background() env, err := GetEnv(ctx) - assert.Assert(t, err != nil, "An error was expected.") + require.Error(t, err, "An error was expected.") t.Logf("Expected error: %s - env: %+v", err.Error(), env) }) @@ -60,8 +59,8 @@ func TestGetEnv(t *testing.T) { TargetServiceHost: "localhost:3000", }) env, err := GetEnv(ctx) - assert.Equal(t, err, nil, "Unexpected error.") - assert.Equal(t, env.TargetServiceHost, "localhost:3000", "Unexpected session duration seconds env variable.") + require.NoError(t, err, "Unexpected error.") + require.Equal(t, "localhost:3000", env.TargetServiceHost, "Unexpected session duration seconds env variable.") }) } diff --git a/internal/mongoclient/mongoclient_test.go b/internal/mongoclient/mongoclient_test.go index 0c4bd5d7..2dce0996 100644 --- a/internal/mongoclient/mongoclient_test.go +++ b/internal/mongoclient/mongoclient_test.go @@ -29,7 +29,7 @@ import ( "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/require" ) func TestMongoCollectionInjectorMiddleware(t *testing.T) { @@ -40,8 +40,8 @@ func TestMongoCollectionInjectorMiddleware(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true collection, ok := r.Context().Value(types.MongoClientContextKey{}).(*MongoClient) - assert.Assert(t, ok, "Collection not found") - assert.Equal(t, collection, testCollections) + require.True(t, ok, "Collection not found") + require.Equal(t, testCollections, collection) w.WriteHeader(http.StatusOK) }) @@ -54,8 +54,8 @@ func TestMongoCollectionInjectorMiddleware(t *testing.T) { builtMiddleware.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code") - assert.Assert(t, invoked, "Next middleware not invoked") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code") + require.True(t, invoked, "Next middleware not invoked") }) } @@ -63,16 +63,16 @@ func TestGetMongoCollectionFromContext(t *testing.T) { t.Run(`config not found in context`, func(t *testing.T) { ctx := context.Background() config, err := GetMongoClientFromContext(ctx) - assert.Assert(t, config == nil) - assert.NilError(t, err, "no error expected") + require.True(t, config == nil) + require.NoError(t, err, "no error expected") }) t.Run(`config found in context`, func(t *testing.T) { testCollections := &MongoClient{} ctx := context.WithValue(context.Background(), types.MongoClientContextKey{}, testCollections) foundConfig, err := GetMongoClientFromContext(ctx) - assert.NilError(t, err, "unexpected error") - assert.Assert(t, foundConfig != nil) + require.NoError(t, err, "unexpected error") + require.True(t, foundConfig != nil) }) } @@ -81,7 +81,7 @@ func TestSetupMongoCollection(t *testing.T) { env := config.EnvironmentVariables{} log, _ := test.NewNullLogger() adapter, _ := NewMongoClient(env, log) - assert.Assert(t, adapter == nil, "MongoDBUrl is not nil") + require.True(t, adapter == nil, "MongoDBUrl is not nil") }) t.Run("if RolesCollectionName empty, returns error", func(t *testing.T) { @@ -91,8 +91,8 @@ func TestSetupMongoCollection(t *testing.T) { } log, _ := test.NewNullLogger() adapter, err := NewMongoClient(env, log) - assert.Assert(t, adapter == nil, "RolesCollectionName collection is not nil") - assert.ErrorContains(t, err, `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "Some different name", RolesCollectionName: ""`) + require.True(t, adapter == nil, "RolesCollectionName collection is not nil") + require.Contains(t, err.Error(), `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "Some different name", RolesCollectionName: ""`) }) t.Run("throws if mongo url is without protocol", func(t *testing.T) { @@ -105,9 +105,9 @@ func TestSetupMongoCollection(t *testing.T) { } log, _ := test.NewNullLogger() adapter, err := NewMongoClient(env, log) - assert.Assert(t, err != nil, "setup mongo not returns error") - assert.ErrorContains(t, err, "failed MongoDB connection string validation:") - assert.Assert(t, adapter == nil) + require.True(t, err != nil, "setup mongo not returns error") + require.Contains(t, err.Error(), "failed MongoDB connection string validation:") + require.True(t, adapter == nil) }) t.Run("correctly returns mongodb collection", func(t *testing.T) { @@ -127,8 +127,8 @@ func TestSetupMongoCollection(t *testing.T) { mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") - assert.Assert(t, mongoClient != nil) + require.True(t, err == nil, "setup mongo returns error") + require.True(t, mongoClient != nil) }) } @@ -149,7 +149,7 @@ func TestMongoCollections(t *testing.T) { log, _ := test.NewNullLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") + require.True(t, err == nil, "setup mongo returns error") client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) mongoClient.client = client mongoClient.roles = rolesCollection @@ -217,7 +217,7 @@ func TestMongoCollections(t *testing.T) { CRUDDocumentState: "PUBLIC", }, } - assert.Assert(t, reflect.DeepEqual(result, expected), + require.True(t, reflect.DeepEqual(result, expected), "Error while getting permissions") }) @@ -237,7 +237,7 @@ func TestMongoCollections(t *testing.T) { log, _ := test.NewNullLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") + require.True(t, err == nil, "setup mongo returns error") client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) mongoClient.client = client mongoClient.roles = rolesCollection @@ -265,7 +265,7 @@ func TestMongoCollections(t *testing.T) { CRUDDocumentState: "PUBLIC", }, } - assert.Assert(t, reflect.DeepEqual(result, expected), + require.True(t, reflect.DeepEqual(result, expected), "Error while getting permissions") }) @@ -285,7 +285,7 @@ func TestMongoCollections(t *testing.T) { log, _ := test.NewNullLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") + require.True(t, err == nil, "setup mongo returns error") client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) mongoClient.client = client mongoClient.roles = rolesCollection @@ -308,7 +308,7 @@ func TestMongoCollections(t *testing.T) { CRUDDocumentState: "PUBLIC", }, } - assert.Assert(t, reflect.DeepEqual(result, expected), + require.True(t, reflect.DeepEqual(result, expected), "Error while getting permissions") }) } @@ -328,7 +328,7 @@ func TestMongoFindOne(t *testing.T) { log, _ := test.NewNullLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") + require.True(t, err == nil, "setup mongo returns error") client, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) mongoClient.client = client @@ -344,12 +344,12 @@ func TestMongoFindOne(t *testing.T) { result, err := mongoClient.FindOne(context.Background(), "roles", map[string]interface{}{ "roleId": "role3", }) - assert.NilError(t, err) + require.NoError(t, err) resultMap := result.(map[string]interface{}) - assert.Assert(t, resultMap["_id"] != nil) + require.True(t, resultMap["_id"] != nil) delete(resultMap, "_id") - assert.DeepEqual(t, result, map[string]interface{}{ + require.Equal(t, map[string]interface{}{ "roleId": "role3", "__STATE__": "PUBLIC", "permissions": []interface{}{ @@ -357,15 +357,15 @@ func TestMongoFindOne(t *testing.T) { string("permission5"), string("console.project.view"), }, - }) + }, result) }) t.Run("does not find a document", func(t *testing.T) { result, err := mongoClient.FindOne(context.Background(), "roles", map[string]interface{}{ "key": 42, }) - assert.NilError(t, err) - assert.Assert(t, result == nil) + require.NoError(t, err) + require.True(t, result == nil) }) } @@ -384,7 +384,7 @@ func TestMongoFindMany(t *testing.T) { log, _ := test.NewNullLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() - assert.Assert(t, err == nil, "setup mongo returns error") + require.True(t, err == nil, "setup mongo returns error") client, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) mongoClient.client = client @@ -404,14 +404,14 @@ func TestMongoFindMany(t *testing.T) { {"roleId": "role6"}, }, }) - assert.NilError(t, err) + require.NoError(t, err) - assert.Equal(t, len(result), 2) + require.Len(t, result, 2) resultMap := result[0].(map[string]interface{}) - assert.Assert(t, resultMap["_id"] != nil) + require.True(t, resultMap["_id"] != nil) delete(resultMap, "_id") - assert.DeepEqual(t, resultMap, map[string]interface{}{ + require.Equal(t, map[string]interface{}{ "roleId": "role3", "__STATE__": "PUBLIC", "permissions": []interface{}{ @@ -419,36 +419,36 @@ func TestMongoFindMany(t *testing.T) { string("permission5"), string("console.project.view"), }, - }) + }, resultMap) result1Map := result[1].(map[string]interface{}) - assert.Assert(t, result1Map["_id"] != nil) + require.True(t, result1Map["_id"] != nil) delete(result1Map, "_id") - assert.DeepEqual(t, result1Map, map[string]interface{}{ + require.Equal(t, map[string]interface{}{ "roleId": "role6", "__STATE__": "PRIVATE", "permissions": []interface{}{ string("permission3"), string("permission5"), }, - }) + }, result1Map) }) t.Run("does not find any document", func(t *testing.T) { result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ "roleId": "role9999", }) - assert.NilError(t, err) - assert.Equal(t, len(result), 0) + require.NoError(t, err) + require.Len(t, result, 0) }) t.Run("returns error on invalid query", func(t *testing.T) { result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ "$UNKWNONW": "role9999", }) - assert.ErrorContains(t, err, "unknown top level operator") - assert.Equal(t, len(result), 0) + require.Contains(t, err.Error(), "unknown top level operator") + require.Len(t, result, 0) }) } @@ -460,7 +460,7 @@ func TestRolesIDSFromBindings(t *testing.T) { {Roles: []string{"e"}}, }) - assert.DeepEqual(t, result, []string{"a", "b", "c", "d", "e"}) + require.Equal(t, []string{"a", "b", "c", "d", "e"}, result) } func TestRetrieveUserBindingsAndRoles(t *testing.T) { @@ -475,7 +475,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req = req.WithContext(context.WithValue(req.Context(), types.MongoClientContextKey{}, "test")) _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, env) - assert.Error(t, err, "Unexpected error retrieving MongoDB Client from request context") + require.Error(t, err, "Unexpected error retrieving MongoDB Client from request context") }) t.Run("extract user from request without querying MongoDB", func(t *testing.T) { @@ -484,11 +484,11 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("theuserheader", "userId") user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, env) - assert.NilError(t, err) - assert.DeepEqual(t, user, types.User{ + require.NoError(t, err) + require.Equal(t, types.User{ UserID: "userId", UserGroups: []string{"group1", "group2"}, - }) + }, user) }) t.Run("extract user with no id in headers does not perform queries", func(t *testing.T) { @@ -499,7 +499,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req = req.WithContext(WithMongoClient(req.Context(), mock)) _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) - assert.NilError(t, err) + require.NoError(t, err) }) t.Run("extract user but retrieve bindings fails", func(t *testing.T) { @@ -512,7 +512,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("theuserheader", "userId") _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) - assert.Error(t, err, "Error while retrieving user bindings: some error") + require.Error(t, err, "Error while retrieving user bindings: some error") }) t.Run("extract user bindings but retrieve roles by role id fails", func(t *testing.T) { @@ -528,7 +528,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("theuserheader", "userId") _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) - assert.Error(t, err, "Error while retrieving user Roles: some error 2") + require.Error(t, err, "Error while retrieving user Roles: some error 2") }) t.Run("extract user bindings and roles", func(t *testing.T) { @@ -549,8 +549,8 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("theuserheader", "userId") user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) - assert.NilError(t, err) - assert.DeepEqual(t, user, types.User{ + require.NoError(t, err) + require.Equal(t, types.User{ UserID: "userId", UserGroups: []string{"group1", "group2"}, UserBindings: []types.Binding{ @@ -562,6 +562,6 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { {RoleID: "r2", Permissions: []string{"p3", "p4"}}, {RoleID: "r3", Permissions: []string{"p5"}}, }, - }) + }, user) }) } diff --git a/internal/testutils/mongodb.go b/internal/testutils/mongodb.go index 027f9414..7e552cc8 100644 --- a/internal/testutils/mongodb.go +++ b/internal/testutils/mongodb.go @@ -28,11 +28,11 @@ import ( "github.com/rond-authz/rond/types" "github.com/samber/lo" + "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - "gotest.tools/v3/assert" ) const LocalhostMongoDB = "localhost:27017" @@ -78,7 +78,7 @@ func GetMongoClient(t *testing.T) *mongo.Client { } clientOpts := options.Client().ApplyURI(fmt.Sprintf("mongodb://%s", mongoHost)) client, err := mongo.Connect(context.Background(), clientOpts) - assert.Assert(t, err == nil, "failed mongo db connection") + require.NoError(t, err, "failed mongo db connection") return client } @@ -99,24 +99,24 @@ func AssertResponseError(t *testing.T, resp *httptest.ResponseRecorder, statusCo func AssertResponseFullErrorMessages(t *testing.T, resp *httptest.ResponseRecorder, statusCode int, technicalErrMsg, businessErrMsg string) { t.Helper() respBodyBuff, err := io.ReadAll(resp.Body) - assert.Equal(t, err, nil, "Unexpected error in the response body") + require.NoError(t, err, "Unexpected error in the response body") var respBody types.RequestError err = json.Unmarshal(respBodyBuff, &respBody) - assert.Equal(t, err, nil, "Unexpected error during unmarshalling of the response body") + require.NoError(t, err, "Unexpected error during unmarshalling of the response body") - assert.Equal(t, respBody.StatusCode, statusCode, "Unexpected status code") + require.Equal(t, statusCode, respBody.StatusCode, "Unexpected status code") if technicalErrMsg != "" { - assert.Equal(t, respBody.Error, technicalErrMsg, "Unexpected technical error message") + require.Equal(t, technicalErrMsg, respBody.Error, "Unexpected technical error message") } if businessErrMsg != "" { - assert.Equal(t, respBody.Message, businessErrMsg, "Unexpected technical error message") + require.Equal(t, businessErrMsg, respBody.Message, "Unexpected technical error message") } } -//#nosec G104 -- Ignored errors +// #nosec G104 -- Ignored errors func PopulateDBForTesting( t *testing.T, ctx context.Context, diff --git a/main_test.go b/main_test.go index f4a2d07f..2db8af3f 100644 --- a/main_test.go +++ b/main_test.go @@ -41,7 +41,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" "gopkg.in/h2non/gock.v1" - "gotest.tools/v3/assert" ) func TestProxyOASPath(t *testing.T) { @@ -1705,17 +1704,17 @@ filter_policy { var mongoClient *mongoclient.MongoClient evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, env) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") t.Run("some eval API", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/my-prefix/evalapi", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("eval with request filter generation", func(t *testing.T) { @@ -1723,9 +1722,9 @@ filter_policy { req := httptest.NewRequest(http.MethodGet, "/my-prefix/evalfilter", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) queryHeader := w.Header().Get("my-query") - assert.Equal(t, queryHeader, `{"$or":[{"$and":[{"answer":{"$eq":42}}]}]}`) + require.Equal(t, `{"$or":[{"$and":[{"answer":{"$eq":42}}]}]}`, queryHeader) }) t.Run("revoke API", func(t *testing.T) { @@ -1734,13 +1733,13 @@ filter_policy { router.ServeHTTP(w, req) // Bad request expected for missing body and so decoder fails! - assert.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) var requestError types.RequestError err := json.Unmarshal(w.Body.Bytes(), &requestError) - assert.NilError(t, err, "unexpected error") - assert.Equal(t, requestError.Message, "Internal server error, please try again later") - assert.Equal(t, requestError.Error, "EOF") + require.NoError(t, err, "unexpected error") + require.Equal(t, "Internal server error, please try again later", requestError.Message) + require.Equal(t, "EOF", requestError.Error) }) t.Run("grant API", func(t *testing.T) { @@ -1749,13 +1748,13 @@ filter_policy { router.ServeHTTP(w, req) // Bad request expected for missing body and so decoder fails! - assert.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) var requestError types.RequestError err := json.Unmarshal(w.Body.Bytes(), &requestError) - assert.NilError(t, err, "unexpected error") - assert.Equal(t, requestError.Message, "Internal server error, please try again later") - assert.Equal(t, requestError.Error, "EOF") + require.NoError(t, err, "unexpected error") + require.Equal(t, "Internal server error, please try again later", requestError.Message) + require.Equal(t, "EOF", requestError.Error) }) t.Run("grant API with headers to proxy", func(t *testing.T) { @@ -1781,7 +1780,7 @@ filter_policy { req.Header.Set("miauserid", "my user id to proxy") router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("API documentation is correctly exposed - json", func(t *testing.T) { @@ -1789,10 +1788,10 @@ filter_policy { req := httptest.NewRequest(http.MethodGet, "/openapi/json", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) responseBody := getResponseBody(t, w) - assert.Assert(t, string(responseBody) != "") + require.True(t, string(responseBody) != "") }) t.Run("API documentation is correctly exposed - yaml", func(t *testing.T) { @@ -1800,10 +1799,10 @@ filter_policy { req := httptest.NewRequest(http.MethodGet, "/openapi/yaml", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) responseBody := getResponseBody(t, w) - assert.Assert(t, string(responseBody) != "") + require.True(t, string(responseBody) != "") }) } diff --git a/opa_transport_test.go b/opa_transport_test.go index e884a5a0..67dbc01b 100644 --- a/opa_transport_test.go +++ b/opa_transport_test.go @@ -33,7 +33,6 @@ import ( "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" - "gotest.tools/v3/assert" ) func TestRoundTripErrors(t *testing.T) { @@ -64,17 +63,17 @@ func TestRoundTripErrors(t *testing.T) { } resp, err := transport.RoundTrip(req) - assert.NilError(t, err, "unexpected error") - assert.Equal(t, resp.StatusCode, http.StatusExpectationFailed, "unexpected status code") + require.NoError(t, err, "unexpected error") + require.Equal(t, http.StatusExpectationFailed, resp.StatusCode, "unexpected status code") bodyBytes, err := io.ReadAll(resp.Body) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") actualResponseBody := make(map[string]interface{}) err = json.Unmarshal(bodyBytes, &actualResponseBody) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") - assert.DeepEqual(t, responseBody, actualResponseBody) + require.Equal(t, responseBody, actualResponseBody) }) } diff --git a/opaevaluator_test.go b/opaevaluator_test.go index 248356a9..851febc6 100644 --- a/opaevaluator_test.go +++ b/opaevaluator_test.go @@ -33,7 +33,6 @@ import ( "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" - "gotest.tools/v3/assert" ) func TestNewOPAEvaluator(t *testing.T) { @@ -156,14 +155,14 @@ func TestCreatePolicyEvaluators(t *testing.T) { OPAModulesDirectory: "./mocks/rego-policies", } openApiSpec, err := loadOASFromFileOrNetwork(log, envs) - assert.Assert(t, err == nil, "unexpected error") + require.NoError(t, err, "unexpected error") opaModuleConfig, err := loadRegoModule(envs.OPAModulesDirectory) - assert.Assert(t, err == nil, "unexpected error") + require.NoError(t, err, "unexpected error") policyEvals, err := setupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) - assert.Assert(t, err == nil, "unexpected error creating evaluators") - assert.Equal(t, len(policyEvals), 4, "unexpected length") + require.NoError(t, err, "unexpected error creating evaluators") + require.Len(t, policyEvals, 4, "unexpected length") }) t.Run("with complete oas mock", func(t *testing.T) { @@ -175,14 +174,14 @@ func TestCreatePolicyEvaluators(t *testing.T) { OPAModulesDirectory: "./mocks/rego-policies", } openApiSpec, err := loadOASFromFileOrNetwork(log, envs) - assert.Assert(t, err == nil, "unexpected error") + require.NoError(t, err, "unexpected error") opaModuleConfig, err := loadRegoModule(envs.OPAModulesDirectory) - assert.Assert(t, err == nil, "unexpected error") + require.NoError(t, err, "unexpected error") policyEvals, err := setupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) - assert.Assert(t, err == nil, "unexpected error creating evaluators") - assert.Equal(t, len(policyEvals), 4, "unexpected length") + require.NoError(t, err, "unexpected error creating evaluators") + require.Len(t, policyEvals, 4, "unexpected length") }) } @@ -202,7 +201,7 @@ func TestBuildRolesMap(t *testing.T) { "role1": {"permission1", "permission2"}, "role2": {"permission3", "permission4"}, } - assert.DeepEqual(t, result, expected) + require.Equal(t, expected, result) } func TestBuildOptimizedResourcePermissionsMap(t *testing.T) { @@ -257,7 +256,7 @@ func TestBuildOptimizedResourcePermissionsMap(t *testing.T) { "permissionNotInRole2:type3:resource3": true, "permissionNotInRole3:type3:resource3": true, } - assert.DeepEqual(t, result, expected) + require.Equal(t, expected, result) } func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { diff --git a/opamiddleware_test.go b/opamiddleware_test.go index cd4daefb..ba3dc023 100644 --- a/opamiddleware_test.go +++ b/opamiddleware_test.go @@ -29,7 +29,6 @@ import ( "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" - "gotest.tools/v3/assert" ) var envs = config.EnvironmentVariables{} @@ -57,13 +56,13 @@ todo { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/not-existing-path", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusNotFound, "Unexpected status code.") - assert.DeepEqual(t, getJSONResponseBody[types.RequestError](t, w), &types.RequestError{ + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ Message: "The request doesn't match any known API", Error: "not found oas definition: GET /not-existing-path", StatusCode: http.StatusNotFound, - }) - assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") }) t.Run(`missing method`, func(t *testing.T) { @@ -75,13 +74,13 @@ todo { true }`, r := httptest.NewRequest(http.MethodDelete, "http://example.com/users/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusNotFound, "Unexpected status code.") - assert.DeepEqual(t, getJSONResponseBody[types.RequestError](t, w), &types.RequestError{ + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ Message: "The request doesn't match any known API", Error: "not found oas definition: DELETE /users/", StatusCode: http.StatusNotFound, - }) - assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader, "Unexpected content type.") + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") }) t.Run(`missing permission`, func(t *testing.T) { @@ -93,7 +92,7 @@ todo { true }`, r := httptest.NewRequest(http.MethodPost, "http://example.com/no-permission", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden, "Unexpected status code.") + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) }) @@ -106,7 +105,7 @@ foobar { true }`, t.Run(`ok - path is known on oas with no permission declared`, func(t *testing.T) { openAPISpec, err := loadOASFile("./mocks/documentationPathMock.json") - assert.NilError(t, err) + require.NoError(t, err) var envs = config.EnvironmentVariables{ TargetServiceOASPath: "/documentation/json", } @@ -119,12 +118,12 @@ foobar { true }`, r := httptest.NewRequest(http.MethodPost, "http://example.com/documentation/json", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run(`ok - path is missing on oas and request is equal to serviceTargetOASPath`, func(t *testing.T) { openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - assert.NilError(t, err) + require.NoError(t, err) var envs = config.EnvironmentVariables{ TargetServiceOASPath: "/documentation/json", } @@ -137,12 +136,12 @@ foobar { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/json", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run(`ok - path is NOT known on oas but is proxied anyway`, func(t *testing.T) { openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - assert.NilError(t, err) + require.NoError(t, err) var envs = config.EnvironmentVariables{ TargetServiceOASPath: "/documentation/custom/json", } @@ -155,13 +154,13 @@ foobar { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/custom/json", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) }) t.Run(`injects opa instance with correct query`, func(t *testing.T) { openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - assert.NilError(t, err) + require.NoError(t, err) t.Run(`rego package doesn't contain expected permission`, func(t *testing.T) { opaModule := &OPAModuleConfig{ @@ -182,7 +181,7 @@ todo { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run(`rego package contains expected permission`, func(t *testing.T) { @@ -204,7 +203,7 @@ foobar { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run(`rego package contains composed permission`, func(t *testing.T) { @@ -226,7 +225,7 @@ very_very_composed_permission { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/composed/permission/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("injects correct permission", func(t *testing.T) { @@ -253,7 +252,7 @@ very_very_composed_permission_with_eval { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) }) } @@ -286,7 +285,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) t.Run("injects correct path removing only one prefix", func(t *testing.T) { @@ -308,7 +307,7 @@ very_very_composed_permission_with_eval { true }`, r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/eval/composed/permission/", nil) builtHandler.ServeHTTP(w, r) - assert.Equal(t, w.Result().StatusCode, http.StatusOK, "Unexpected status code.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) } @@ -333,16 +332,16 @@ func TestGetHeaderFunction(t *testing.T) { inputBytes, _ := json.Marshal(input) opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) - assert.NilError(t, err, "Unexpected error during creation of opaEvaluator") + require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) - assert.NilError(t, err, "Unexpected error during rego validation") - assert.Assert(t, results.Allowed(), "The input is not allowed by rego") + require.NoError(t, err, "Unexpected error during rego validation") + require.True(t, results.Allowed(), "The input is not allowed by rego") partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) - assert.NilError(t, err, "Unexpected error during rego validation") + require.NoError(t, err, "Unexpected error during rego validation") - assert.Equal(t, 1, len(partialResults.Queries), "Rego policy allows illegal input") + require.Len(t, partialResults.Queries, 1, "Rego policy allows illegal input") }) t.Run("if header key not exists", func(t *testing.T) { @@ -352,16 +351,16 @@ func TestGetHeaderFunction(t *testing.T) { inputBytes, _ := json.Marshal(input) opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) - assert.NilError(t, err, "Unexpected error during creation of opaEvaluator") + require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) - assert.NilError(t, err, "Unexpected error during rego validation") - assert.Assert(t, !results.Allowed(), "Rego policy allows illegal input") + require.NoError(t, err, "Unexpected error during rego validation") + require.True(t, !results.Allowed(), "Rego policy allows illegal input") partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) - assert.NilError(t, err, "Unexpected error during rego validation") + require.NoError(t, err, "Unexpected error during rego validation") - assert.Equal(t, 0, len(partialResults.Queries), "Rego policy allows illegal input") + require.Len(t, partialResults.Queries, 0, "Rego policy allows illegal input") }) } diff --git a/openapi_utils_test.go b/openapi_utils_test.go index b1b3e330..931a2098 100644 --- a/openapi_utils_test.go +++ b/openapi_utils_test.go @@ -24,7 +24,6 @@ import ( "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" - "gotest.tools/v3/assert" ) func TestFetchOpenAPI(t *testing.T) { @@ -40,10 +39,10 @@ func TestFetchOpenAPI(t *testing.T) { openApiSpec, err := fetchOpenAPI(url) - assert.Assert(t, gock.IsDone(), "Mock has not been invoked") - assert.Assert(t, err == nil, "unexpected error") - assert.Assert(t, openApiSpec != nil, "unexpected nil result") - assert.DeepEqual(t, openApiSpec.Paths, OpenAPIPaths{ + require.True(t, gock.IsDone(), "Mock has not been invoked") + require.NoError(t, err, "unexpected error") + require.NotNil(t, openApiSpec, "unexpected nil result") + require.Equal(t, OpenAPIPaths{ "/users/": PathVerbs{ "get": VerbConfig{ PermissionV2: &RondConfig{ @@ -79,7 +78,7 @@ func TestFetchOpenAPI(t *testing.T) { }, }, }, - }) + }, openApiSpec.Paths) }) t.Run("request execution fails for invalid URL", func(t *testing.T) { @@ -88,7 +87,7 @@ func TestFetchOpenAPI(t *testing.T) { _, err := fetchOpenAPI(url) t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, errors.Is(err, ErrRequestFailed), "unexpected error") + require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") }) t.Run("request execution fails for invalid URL syntax", func(t *testing.T) { @@ -97,7 +96,7 @@ func TestFetchOpenAPI(t *testing.T) { _, err := fetchOpenAPI(url) t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, errors.Is(err, ErrRequestFailed), "unexpected error") + require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") }) t.Run("request execution fails for unexpected server response", func(t *testing.T) { @@ -113,7 +112,7 @@ func TestFetchOpenAPI(t *testing.T) { _, err := fetchOpenAPI(url) t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, errors.Is(err, ErrRequestFailed), "unexpected error") + require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") }) t.Run("request execution fails for unexpected server response", func(t *testing.T) { @@ -128,16 +127,16 @@ func TestFetchOpenAPI(t *testing.T) { _, err := fetchOpenAPI(url) t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, errors.Is(err, ErrRequestFailed), "unexpected error") + require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") }) } func TestLoadOASFile(t *testing.T) { t.Run("get oas config from file", func(t *testing.T) { openAPIFile, err := loadOASFile("./mocks/pathsConfig.json") - assert.Assert(t, err == nil, "unexpected error") - assert.Assert(t, openAPIFile != nil, "unexpected nil result") - assert.DeepEqual(t, openAPIFile.Paths, OpenAPIPaths{ + require.True(t, err == nil, "unexpected error") + require.True(t, openAPIFile != nil, "unexpected nil result") + require.Equal(t, OpenAPIPaths{ "/users-from-static-file/": PathVerbs{ "get": VerbConfig{ PermissionV2: &RondConfig{ @@ -159,14 +158,14 @@ func TestLoadOASFile(t *testing.T) { "/no-permission-from-static-file": PathVerbs{ "post": VerbConfig{}, }, - }) + }, openAPIFile.Paths) }) t.Run("fail for invalid filePath", func(t *testing.T) { _, err := loadOASFile("./notExistingFilePath.json") t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, err != nil, "failed documentation file read") + require.True(t, err != nil, "failed documentation file read") }) } @@ -180,9 +179,9 @@ func TestLoadOAS(t *testing.T) { APIPermissionsFilePath: "./mocks/pathsConfig.json", } openApiSpec, err := loadOASFromFileOrNetwork(log, envs) - assert.Assert(t, err == nil, "unexpected error") - assert.Assert(t, openApiSpec != nil, "unexpected nil result") - assert.DeepEqual(t, openApiSpec.Paths, OpenAPIPaths{ + require.True(t, err == nil, "unexpected error") + require.True(t, openApiSpec != nil, "unexpected nil result") + require.Equal(t, OpenAPIPaths{ "/users-from-static-file/": PathVerbs{ "get": VerbConfig{ PermissionV2: &RondConfig{ @@ -204,7 +203,7 @@ func TestLoadOAS(t *testing.T) { "/no-permission-from-static-file": PathVerbs{ "post": VerbConfig{}, }, - }) + }, openApiSpec.Paths) }) t.Run("expect to fetch oasApiSpec from API", func(t *testing.T) { @@ -220,10 +219,10 @@ func TestLoadOAS(t *testing.T) { File("./mocks/simplifiedMock.json") openApiSpec, err := loadOASFromFileOrNetwork(log, envs) - assert.Assert(t, gock.IsDone(), "Mock has not been invoked") - assert.Assert(t, err == nil, "unexpected error") - assert.Assert(t, openApiSpec != nil, "unexpected nil result") - assert.DeepEqual(t, openApiSpec.Paths, OpenAPIPaths{ + require.True(t, gock.IsDone(), "Mock has not been invoked") + require.NoError(t, err, "unexpected error") + require.NotNil(t, openApiSpec, "unexpected nil result") + require.Equal(t, OpenAPIPaths{ "/users/": PathVerbs{ "get": VerbConfig{ PermissionV2: &RondConfig{ @@ -259,7 +258,7 @@ func TestLoadOAS(t *testing.T) { }, }, }, - }) + }, openApiSpec.Paths) }) t.Run("expect to throw if TargetServiceOASPath or APIPermissionsFilePath is not set", func(t *testing.T) { @@ -269,7 +268,7 @@ func TestLoadOAS(t *testing.T) { _, err := loadOASFromFileOrNetwork(log, envs) t.Logf("Expected error occurred: %s", err.Error()) - assert.Assert(t, err != nil, fmt.Errorf("missing environment variables one of %s or %s is required", config.TargetServiceOASPathEnvKey, config.APIPermissionsFilePathEnvKey)) + require.True(t, err != nil, fmt.Errorf("missing environment variables one of %s or %s is required", config.TargetServiceOASPathEnvKey, config.APIPermissionsFilePathEnvKey)) }) } @@ -279,19 +278,19 @@ func TestFindPermission(t *testing.T) { OASRouter := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/not/existing/route", "GET") - assert.Equal(t, RondConfig{}, found) - assert.Equal(t, err.Error(), fmt.Sprintf("%s: GET /not/existing/route", ErrNotFoundOASDefinition)) + require.Empty(t, RondConfig{}, found) + require.EqualError(t, err, fmt.Sprintf("%s: GET /not/existing/route", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/no/method", "PUT") - assert.Equal(t, RondConfig{}, found) - assert.Equal(t, err.Error(), fmt.Sprintf("%s: PUT /no/method", ErrNotFoundOASDefinition)) + require.Equal(t, RondConfig{}, found) + require.EqualError(t, err, fmt.Sprintf("%s: PUT /no/method", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/use/method/that/not/existing/put", "PUT") - assert.Equal(t, RondConfig{}, found) - assert.Equal(t, err.Error(), fmt.Sprintf("%s: PUT /use/method/that/not/existing/put", ErrNotFoundOASDefinition)) + require.Equal(t, RondConfig{}, found) + require.EqualError(t, err, fmt.Sprintf("%s: PUT /use/method/that/not/existing/put", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/foo/bar/barId", "GET") - assert.Equal(t, RondConfig{ + require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar_params", GenerateQuery: true, @@ -300,10 +299,10 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - assert.Equal(t, err, nil) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/bar/barId/another-params-not-configured", "GET") - assert.Equal(t, RondConfig{ + require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar", GenerateQuery: true, @@ -312,14 +311,14 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - assert.Equal(t, err, nil) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/bar/nested/case/really/nested", "GET") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/bar/nested", "GET") - assert.Equal(t, RondConfig{ + require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar_nested", GenerateQuery: true, @@ -328,10 +327,10 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - assert.Equal(t, err, nil) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/simble", "PATCH") - assert.Equal(t, RondConfig{ + require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo", GenerateQuery: true, @@ -340,47 +339,47 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - assert.Equal(t, err, nil) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all", "GET") - assert.Equal(t, RondConfig{}, found) - assert.Equal(t, err.Error(), fmt.Sprintf("%s: GET /test/all", ErrNotFoundOASDefinition)) + require.Equal(t, RondConfig{}, found) + require.EqualError(t, err, fmt.Sprintf("%s: GET /test/all", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/test/all/", "GET") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "GET") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "POST") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PUT") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PATCH") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "DELETE") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "HEAD") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/projects/", "POST") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/projects/", "GET") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_get"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_get"}}, found) + require.NoError(t, err) }) t.Run("encoded cases", func(t *testing.T) { @@ -388,12 +387,12 @@ func TestFindPermission(t *testing.T) { OASRouter := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) + require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%2Fcms-backend%2FcmsProperties.json", "POST") - assert.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) - assert.Equal(t, err, nil) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) + require.NoError(t, err) }) } @@ -674,11 +673,11 @@ func TestAdaptOASSpec(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { adaptOASSpec(testCase.input) - assert.DeepEqual(t, testCase.input, testCase.expected) + require.Equal(t, testCase.expected, testCase.input) for path, pathConfig := range testCase.input.Paths { for verb, verbConfig := range pathConfig { - assert.Assert(t, verbConfig.PermissionV1 == nil, "Unexpected non-nil conf for %s %s", verb, path) + require.True(t, verbConfig.PermissionV1 == nil, "Unexpected non-nil conf for %s %s", verb, path) } } }) diff --git a/router_test.go b/router_test.go index f4ca8808..25f7a3fc 100644 --- a/router_test.go +++ b/router_test.go @@ -29,9 +29,9 @@ import ( "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" "github.com/gorilla/mux" - "gotest.tools/v3/assert" ) func TestSetupRoutes(t *testing.T) { @@ -66,7 +66,7 @@ func TestSetupRoutes(t *testing.T) { }) sort.Strings(foundPaths) - assert.DeepEqual(t, foundPaths, expectedPaths) + require.Equal(t, expectedPaths, foundPaths) }) t.Run("expect to register nested route correctly", func(t *testing.T) { @@ -100,7 +100,7 @@ func TestSetupRoutes(t *testing.T) { }) sort.Strings(foundPaths) - assert.DeepEqual(t, foundPaths, expectedPaths) + require.Equal(t, expectedPaths, foundPaths) }) t.Run("expect to register route correctly in standalone mode", func(t *testing.T) { @@ -136,7 +136,7 @@ func TestSetupRoutes(t *testing.T) { }) sort.Strings(foundPaths) - assert.DeepEqual(t, foundPaths, expectedPaths) + require.Equal(t, expectedPaths, foundPaths) }) } @@ -159,7 +159,7 @@ func TestConvertPathVariables(t *testing.T) { t.Run("convert correctly paths", func(t *testing.T) { for _, path := range listOfPaths { convertedPath := convertPathVariablesToBrackets(path.Path) - assert.Equal(t, convertedPath, path.ConvertedPath, "Path not converted correctly.") + require.Equal(t, path.ConvertedPath, convertedPath, "Path not converted correctly.") } }) } @@ -182,7 +182,7 @@ func TestConvertPathVariables2(t *testing.T) { t.Run("convert correctly paths", func(t *testing.T) { for _, path := range listOfPaths { convertedPath := convertPathVariablesToColons(path.Path) - assert.Equal(t, convertedPath, path.ConvertedPath, "Path not converted correctly.") + require.Equal(t, path.ConvertedPath, convertedPath, "Path not converted correctly.") } }) } @@ -249,8 +249,8 @@ func TestSetupRoutesIntegration(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.URL.Path, "/users/", "Mocked Backend: Unexpected path of request url") - assert.Equal(t, r.URL.RawQuery, "foo=bar", "Mocked Backend: Unexpected rawQuery of request url") + require.Equal(t, "/users/", r.URL.Path, "Mocked Backend: Unexpected path of request url") + require.Equal(t, "foo=bar", r.URL.RawQuery, "Mocked Backend: Unexpected rawQuery of request url") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -269,25 +269,25 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Assert(t, invoked, "mock server was not invoked") - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.True(t, invoked, "mock server was not invoked") + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("invokes unknown API", func(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true - assert.Equal(t, r.URL.Path, "/unknown/path", "Mocked Backend: Unexpected path of request url") - assert.Equal(t, r.URL.RawQuery, "foo=bar", "Mocked Backend: Unexpected rawQuery of request url") + require.Equal(t, "/unknown/path", r.URL.Path, "Mocked Backend: Unexpected path of request url") + require.Equal(t, "foo=bar", r.URL.RawQuery, "Mocked Backend: Unexpected rawQuery of request url") w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -306,17 +306,17 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/unknown/path?foo=bar", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Assert(t, invoked, "mock server was not invoked") - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.True(t, invoked, "mock server was not invoked") + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("blocks request on not allowed policy evaluation", func(t *testing.T) { @@ -339,16 +339,16 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusForbidden) + require.Equal(t, http.StatusForbidden, w.Result().StatusCode) }) t.Run("blocks request on policy evaluation error", func(t *testing.T) { @@ -371,16 +371,16 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/users/?foo=bar", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) }) t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { @@ -407,17 +407,17 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/foo/route-not-registered-explicitly", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Assert(t, invoked, "mock server was not invoked") - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.True(t, invoked, "mock server was not invoked") + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("invokes a specific API within a nested path", func(t *testing.T) { @@ -444,17 +444,17 @@ func TestSetupRoutesIntegration(t *testing.T) { ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/foo/bar/nested", nil) - assert.Equal(t, err, nil, "Unexpected error") + require.NoError(t, err, "Unexpected error") var matchedRouted mux.RouteMatch ok := router.Match(req, &matchedRouted) - assert.Assert(t, ok, "Route not found") + require.True(t, ok, "Route not found") w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - assert.Assert(t, invoked, "mock server was not invoked") - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.True(t, invoked, "mock server was not invoked") + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) } @@ -462,6 +462,6 @@ func prepareOASFromFile(t *testing.T, filePath string) *OpenAPISpec { t.Helper() oas, err := loadOASFile(filePath) - assert.NilError(t, err) + require.NoError(t, err) return oas } diff --git a/standalone_apis_test.go b/standalone_apis_test.go index ae6f1948..2f5277d5 100644 --- a/standalone_apis_test.go +++ b/standalone_apis_test.go @@ -30,7 +30,6 @@ import ( "github.com/rond-authz/rond/types" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" - "gotest.tools/v3/assert" ) func TestRevokeHandler(t *testing.T) { @@ -50,12 +49,12 @@ func TestRevokeHandler(t *testing.T) { ResourceIDs: []string{"mike"}, }) req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/", bytes.NewBuffer(reqBody)) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") w := httptest.NewRecorder() revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusBadRequest) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) }) t.Run("400 on missing resourceIds from body if resourceType request param is present", func(t *testing.T) { @@ -71,7 +70,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusBadRequest) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) }) reqBody := setupRevokeRequestBody(t, RevokeRequestBody{ @@ -95,7 +94,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) }) t.Run("error on CRUD delete API", func(t *testing.T) { @@ -134,7 +133,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) }) t.Run("does not invoke delete API if not necessary", func(t *testing.T) { @@ -181,7 +180,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("performs correct delete query only on subject", func(t *testing.T) { @@ -230,7 +229,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("performs correct delete query only on group", func(t *testing.T) { @@ -279,7 +278,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("performs correct patch API invocation", func(t *testing.T) { @@ -311,7 +310,7 @@ func TestRevokeHandler(t *testing.T) { AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { var body []PatchItem err := json.NewDecoder(req.Body).Decode(&body) - assert.NilError(t, err, "unxpected error parsing body in matcher") + require.NoError(t, err, "unxpected error parsing body in matcher") require.Equal(t, []PatchItem{ { @@ -341,7 +340,7 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("performs correct delete and patch APIs", func(t *testing.T) { @@ -394,7 +393,7 @@ func TestRevokeHandler(t *testing.T) { var body []PatchItem err := json.NewDecoder(req.Body).Decode(&body) - assert.NilError(t, err, "unxpected error parsing body in matcher") + require.NoError(t, err, "unxpected error parsing body in matcher") require.Equal(t, []PatchItem{ { @@ -425,11 +424,11 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) revokeResponse := RevokeResponseBody{} err := json.NewDecoder(w.Body).Decode(&revokeResponse) - assert.NilError(t, err) + require.NoError(t, err) }) t.Run("performs correct delete and patch APIs without resourceType request param", func(t *testing.T) { @@ -474,7 +473,7 @@ func TestRevokeHandler(t *testing.T) { var body []PatchItem err := json.NewDecoder(req.Body).Decode(&body) - assert.NilError(t, err, "unxpected error parsing body in matcher") + require.NoError(t, err, "unxpected error parsing body in matcher") require.Equal(t, []PatchItem{ { @@ -502,11 +501,11 @@ func TestRevokeHandler(t *testing.T) { revokeHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) revokeResponse := RevokeResponseBody{} err := json.NewDecoder(w.Body).Decode(&revokeResponse) - assert.NilError(t, err) + require.NoError(t, err) }) } @@ -525,12 +524,12 @@ func TestGrantHandler(t *testing.T) { ResourceID: "my-resource", }) req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/", bytes.NewBuffer(reqBody)) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") w := httptest.NewRecorder() grantHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusBadRequest) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) }) t.Run("400 on missing resourceId from body if resourceType request param is present", func(t *testing.T) { @@ -549,7 +548,7 @@ func TestGrantHandler(t *testing.T) { grantHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusBadRequest) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) }) t.Run("performs correct API invocation insert bindings only on subject", func(t *testing.T) { @@ -602,14 +601,14 @@ func TestGrantHandler(t *testing.T) { grantHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) var response GrantResponseBody err := json.NewDecoder(w.Body).Decode(&response) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") _, err = uuid.Parse(response.BindingID) - assert.NilError(t, err, "unxpected error") + require.NoError(t, err, "unxpected error") }) t.Run("performs correct API invocation insert bindings only on subject without resourceType request param", func(t *testing.T) { @@ -655,14 +654,14 @@ func TestGrantHandler(t *testing.T) { grantHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusOK) + require.Equal(t, http.StatusOK, w.Result().StatusCode) var response GrantResponseBody err := json.NewDecoder(w.Body).Decode(&response) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") _, err = uuid.Parse(response.BindingID) - assert.NilError(t, err, "unxpected error") + require.NoError(t, err, "unxpected error") }) t.Run("crud request return error", func(t *testing.T) { @@ -681,10 +680,10 @@ func TestGrantHandler(t *testing.T) { AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { var body types.Binding err := json.NewDecoder(req.Body).Decode(&body) - assert.NilError(t, err, "unxpected error parsing body in matcher") + require.NoError(t, err, "unxpected error parsing body in matcher") _, err = uuid.Parse(body.BindingID) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") body.BindingID = "REDACTED" require.Equal(t, types.Binding{ @@ -708,7 +707,7 @@ func TestGrantHandler(t *testing.T) { w := httptest.NewRecorder() grantHandler(w, req) - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) }) } @@ -827,7 +826,7 @@ func setupBodyBytes(t *testing.T, body interface{}) []byte { t.Helper() bodyBytes, err := json.Marshal(body) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") return bodyBytes } @@ -842,7 +841,7 @@ func requestWithParams( t.Helper() req, err := http.NewRequestWithContext(ctx, method, path, body) - assert.NilError(t, err, "unexpected error creating request with context and params") + require.NoError(t, err, "unexpected error creating request with context and params") if params != nil { req = mux.SetURLVars(req, params) diff --git a/statusroutes_test.go b/statusroutes_test.go index 7ee5c894..cf87fb24 100644 --- a/statusroutes_test.go +++ b/statusroutes_test.go @@ -30,7 +30,6 @@ import ( "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" - "gotest.tools/v3/assert" ) func TestStatusRoutes(testCase *testing.T) { @@ -112,7 +111,7 @@ test_policy { true } var mongoClient *mongoclient.MongoClient evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, envs) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { env := config.EnvironmentVariables{ @@ -121,28 +120,28 @@ test_policy { true } PathPrefixStandalone: "/my-prefix", } router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-ready", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("/-/rbac-healthz", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-healthz", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("/-/rbac-check-up", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-check-up", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) }) @@ -154,27 +153,27 @@ test_policy { true } ServiceVersion: "latest", } router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) - assert.NilError(t, err, "unexpected error") + require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-ready", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("/-/rbac-healthz", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-healthz", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) t.Run("/-/rbac-check-up", func(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/-/rbac-check-up", nil) router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Result().StatusCode) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) }) } diff --git a/utilities_test.go b/utilities_test.go index 0b61ef95..029a785f 100644 --- a/utilities_test.go +++ b/utilities_test.go @@ -23,7 +23,7 @@ import ( "github.com/rond-authz/rond/types" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/require" ) func TestUnmarshalHeader(t *testing.T) { @@ -33,7 +33,7 @@ func TestUnmarshalHeader(t *testing.T) { "key": []string{"is", "not"}, } mockedUserPropertiesStringified, err := json.Marshal(mockedUserProperties) - assert.NilError(t, err) + require.NoError(t, err) t.Run("header not exists", func(t *testing.T) { headers := http.Header{} @@ -41,8 +41,8 @@ func TestUnmarshalHeader(t *testing.T) { ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - assert.Assert(t, !ok, "Unmarshal not existing header") - assert.NilError(t, err, "Unexpected error if doesn't exist header") + require.True(t, !ok, "Unmarshal not existing header") + require.NoError(t, err, "Unexpected error if doesn't exist header") }) t.Run("header exists but the unmarshalling fails", func(t *testing.T) { @@ -51,9 +51,9 @@ func TestUnmarshalHeader(t *testing.T) { var userProperties string ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - - assert.Assert(t, !ok, "Unexpected success during unmarshalling") - assert.ErrorType(t, err, &json.UnmarshalTypeError{}, "Unexpected error on unmarshalling") + require.False(t, ok, "Unexpected success during unmarshalling") + var unmarshalErr = &json.UnmarshalTypeError{} + require.ErrorAs(t, err, &unmarshalErr, "Unexpected error on unmarshalling") }) t.Run("header exists and unmarshalling finishes correctly", func(t *testing.T) { @@ -62,8 +62,8 @@ func TestUnmarshalHeader(t *testing.T) { var userProperties map[string]interface{} ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - assert.Assert(t, ok, "Unexpected failure") - assert.NilError(t, err, "Unexpected error") + require.True(t, ok, "Unexpected failure") + require.NoError(t, err, "Unexpected error") }) } @@ -71,20 +71,20 @@ func TestFailResponseWithCode(t *testing.T) { w := httptest.NewRecorder() failResponseWithCode(w, http.StatusInternalServerError, "The Error", "The Message") - assert.Equal(t, w.Result().StatusCode, http.StatusInternalServerError) + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) - assert.Equal(t, w.Result().Header.Get(ContentTypeHeaderKey), JSONContentTypeHeader) + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey)) bodyBytes, err := io.ReadAll(w.Body) - assert.NilError(t, err) + require.NoError(t, err) var response types.RequestError err = json.Unmarshal(bodyBytes, &response) - assert.NilError(t, err) + require.NoError(t, err) - assert.DeepEqual(t, response, types.RequestError{ + require.Equal(t, types.RequestError{ StatusCode: http.StatusInternalServerError, Error: "The Error", Message: "The Message", - }) + }, response) } From c6e7e9d98bd2e126d2a4a98fe35b2331fdd671ca Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 6 Dec 2022 12:27:53 +0100 Subject: [PATCH 012/172] Upgrade version to v1.6.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 99b9248e..651969b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.5.3" +ENV SERVICE_VERSION="1.6.0" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 300ea167aab2efdbbc6cf333e3be14b72cb38032 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 6 Dec 2022 17:23:57 +0100 Subject: [PATCH 013/172] Expose metrics endpoint (#125) * fix: expose metrics endpoint * test: add route tests --- internal/metrics/routes.go | 4 +++- internal/utils/general.go | 2 ++ main_test.go | 2 +- opamiddleware.go | 2 +- router.go | 5 ++++- router_test.go | 23 ++++++++++++++++------- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/internal/metrics/routes.go b/internal/metrics/routes.go index b58ca841..0e3918b4 100644 --- a/internal/metrics/routes.go +++ b/internal/metrics/routes.go @@ -10,8 +10,10 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) +var MetricsRoutePath = "/-/rond/metrics" + func MetricsRoute(r *mux.Router, registry *prometheus.Registry) { - r.Handle("/metrics", promhttp.InstrumentMetricHandler( + r.Handle(MetricsRoutePath, promhttp.InstrumentMetricHandler( registry, promhttp.HandlerFor(registry, promhttp.HandlerOpts{ Registry: registry, diff --git a/internal/utils/general.go b/internal/utils/general.go index 1b075c8f..91920ee8 100644 --- a/internal/utils/general.go +++ b/internal/utils/general.go @@ -32,3 +32,5 @@ func SanitizeString(input string) string { sanitized = strings.Replace(sanitized, "\r", "", -1) return sanitized } + +var Union = lo.Union[string] diff --git a/main_test.go b/main_test.go index 2db8af3f..9dcf1766 100644 --- a/main_test.go +++ b/main_test.go @@ -1862,7 +1862,7 @@ filter_policy { t.Run("metrics API exposed correctly", func(t *testing.T) { w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/metrics", nil) + req := httptest.NewRequest(http.MethodGet, "/-/rond/metrics", nil) router.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Result().StatusCode) diff --git a/opamiddleware.go b/opamiddleware.go index 42c1db8c..95dc78b4 100644 --- a/opamiddleware.go +++ b/opamiddleware.go @@ -49,7 +49,7 @@ func OPAMiddleware(opaModuleConfig *OPAModuleConfig, openAPISpec *OpenAPISpec, e return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if utils.Contains(statusRoutes, r.URL.RequestURI()) { + if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { next.ServeHTTP(w, r) return } diff --git a/router.go b/router.go index a3287b76..6a0eb50e 100644 --- a/router.go +++ b/router.go @@ -24,12 +24,15 @@ import ( swagger "github.com/davidebianchi/gswagger" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" ) +var routesToNotProxy = utils.Union(statusRoutes, []string{metrics.MetricsRoutePath}) + var revokeDefinitions = swagger.Definitions{ RequestBody: &swagger.ContentValue{ Content: swagger.Content{ @@ -135,7 +138,7 @@ func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVar if env.Standalone { pathToRegister = fmt.Sprintf("%s%s", env.PathPrefixStandalone, path) } - if utils.Contains(statusRoutes, pathToRegister) { + if utils.Contains(routesToNotProxy, pathToRegister) { continue } if strings.Contains(pathToRegister, "*") { diff --git a/router_test.go b/router_test.go index 25f7a3fc..f649ef5d 100644 --- a/router_test.go +++ b/router_test.go @@ -42,15 +42,20 @@ func TestSetupRoutes(t *testing.T) { router := mux.NewRouter() oas := &OpenAPISpec{ Paths: OpenAPIPaths{ - "/foo": PathVerbs{}, - "/bar": PathVerbs{}, - "/foo/bar": PathVerbs{}, - "/-/ready": PathVerbs{}, - "/-/healthz": PathVerbs{}, - "/-/check-up": PathVerbs{}, + "/foo": PathVerbs{}, + "/bar": PathVerbs{}, + "/foo/bar": PathVerbs{}, + "/-/ready": PathVerbs{}, + "/-/healthz": PathVerbs{}, + "/-/check-up": PathVerbs{}, + "/-/metrics": PathVerbs{}, + "/-/rond/metrics": PathVerbs{}, + "/-/rbac-healthz": PathVerbs{}, + "/-/rbac-ready": PathVerbs{}, + "/-/rbac-check-up": PathVerbs{}, }, } - expectedPaths := []string{"/", "/-/check-up", "/-/healthz", "/-/ready", "/bar", "/documentation/json", "/foo", "/foo/bar"} + expectedPaths := []string{"/", "/-/check-up", "/-/healthz", "/-/metrics", "/-/ready", "/bar", "/documentation/json", "/foo", "/foo/bar"} setupRoutes(router, oas, envs) @@ -458,6 +463,10 @@ func TestSetupRoutesIntegration(t *testing.T) { }) } +func TestRoutesToNotProxy(t *testing.T) { + require.Equal(t, routesToNotProxy, []string{"/-/rbac-healthz", "/-/rbac-ready", "/-/rbac-check-up", "/-/rond/metrics"}) +} + func prepareOASFromFile(t *testing.T, filePath string) *OpenAPISpec { t.Helper() From 74a439a9a9406bd9f4fb1badb712e83ad5c2ea23 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 6 Dec 2022 17:41:44 +0100 Subject: [PATCH 014/172] Upgrade version to v1.6.1 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 651969b8..f10a6f24 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.6.0" +ENV SERVICE_VERSION="1.6.1" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 4dcb4879e74132ab0942d89f1597bd0e4e462d8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:49:47 +0100 Subject: [PATCH 015/172] chore(deps): bump golang from 1.19.3 to 1.19.4 (#126) Bumps golang from 1.19.3 to 1.19.4. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f10a6f24..85baf2eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.19.3 AS builder +FROM golang:1.19.4 AS builder WORKDIR /app From 52f7e5c7e49311d3e632c7c6f79a898cfd76c11b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Dec 2022 20:47:00 +0100 Subject: [PATCH 016/172] chore(deps): bump github.com/open-policy-agent/opa from 0.47.0 to 0.47.1 (#127) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.47.0 to 0.47.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.47.0...v0.47.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 05cebd2d..d104ec1f 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.47.0 + github.com/open-policy-agent/opa v0.47.1 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 239f7955..bcf0422d 100644 --- a/go.sum +++ b/go.sum @@ -378,8 +378,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.47.0 h1:d6g0oDNLraIcWl9LXW8cBzRYf2zt7vSbPGEd2+8K3Lg= -github.com/open-policy-agent/opa v0.47.0/go.mod h1:cM7ngEoEdAIfyu9mOHaVcgLAHYkY6amrYfotm+BSkYQ= +github.com/open-policy-agent/opa v0.47.1 h1:4Nf8FwguZeE5P83akiwaaoWx1XkmSkRcKmCEskiD/1c= +github.com/open-policy-agent/opa v0.47.1/go.mod h1:cM7ngEoEdAIfyu9mOHaVcgLAHYkY6amrYfotm+BSkYQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From dc21ed28676a67f3c8bb97946ecf645921099173 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:12:54 +0100 Subject: [PATCH 017/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.0 to 1.11.1 (#129) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.0 to 1.11.1. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.0...v1.11.1) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d104ec1f..ae11bc11 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 github.com/uptrace/bunrouter v1.0.19 - go.mongodb.org/mongo-driver v1.11.0 + go.mongodb.org/mongo-driver v1.11.1 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index bcf0422d..a98d4635 100644 --- a/go.sum +++ b/go.sum @@ -525,8 +525,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= +go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= 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= From 1f33848b4a20386cde57cbc2a9750fd757e90892 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:13:06 +0100 Subject: [PATCH 018/172] chore(deps): bump github.com/open-policy-agent/opa from 0.47.1 to 0.47.2 (#128) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.47.1 to 0.47.2. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.47.1...v0.47.2) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index ae11bc11..5dffff9f 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.47.1 + github.com/open-policy-agent/opa v0.47.2 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 @@ -75,8 +75,8 @@ require ( golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect - golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index a98d4635..cc1af910 100644 --- a/go.sum +++ b/go.sum @@ -378,8 +378,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.47.1 h1:4Nf8FwguZeE5P83akiwaaoWx1XkmSkRcKmCEskiD/1c= -github.com/open-policy-agent/opa v0.47.1/go.mod h1:cM7ngEoEdAIfyu9mOHaVcgLAHYkY6amrYfotm+BSkYQ= +github.com/open-policy-agent/opa v0.47.2 h1:9QmIumL6MRPYoXboBDSU/c1fG2PN5J4lo800RK36jrc= +github.com/open-policy-agent/opa v0.47.2/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -632,7 +632,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= 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= @@ -721,8 +721,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -735,8 +735,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 2b6de5a181c0996e126bc69bf11ab0459d8bb570 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 12:17:53 +0100 Subject: [PATCH 019/172] chore(deps): bump github.com/open-policy-agent/opa from 0.47.2 to 0.47.3 (#130) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.47.2 to 0.47.3. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.47.2...v0.47.3) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5dffff9f..e6870392 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.47.2 + github.com/open-policy-agent/opa v0.47.3 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.36.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index cc1af910..0dab9782 100644 --- a/go.sum +++ b/go.sum @@ -378,8 +378,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.47.2 h1:9QmIumL6MRPYoXboBDSU/c1fG2PN5J4lo800RK36jrc= -github.com/open-policy-agent/opa v0.47.2/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= +github.com/open-policy-agent/opa v0.47.3 h1:Uj8zw+q6Cvv1iiQFh704Q6sl3fKVvk35WZNJLsd6mgk= +github.com/open-policy-agent/opa v0.47.3/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 8fef97a12836e2e56164772e9922650bc8edf761 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 13 Dec 2022 15:24:44 +0100 Subject: [PATCH 020/172] feat: add new metrics bucket (#131) --- internal/metrics/metrics.go | 2 +- internal/metrics/metrics_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index c8d802c9..0b10cae6 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -15,7 +15,7 @@ func SetupMetrics(prefix string) Metrics { Namespace: prefix, Name: "policy_evaluation_duration_milliseconds", Help: "A histogram of the policy evaluation durations in milliseconds.", - Buckets: []float64{1, 5, 10, 50, 100}, + Buckets: []float64{1, 5, 10, 50, 100, 250, 500}, }, []string{"policy_name"}), } diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index c54077c2..346d7c56 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -28,6 +28,8 @@ func TestMetrics(t *testing.T) { test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="10"} 1 test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="50"} 1 test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="100"} 1 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="250"} 1 + test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="500"} 1 test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="+Inf"} 1 test_prefix_policy_evaluation_duration_milliseconds_sum{policy_name="myPolicyName"} 10 test_prefix_policy_evaluation_duration_milliseconds_count{policy_name="myPolicyName"} 1 From 903eea7e1225c6ffbf5dadf80f9832cf4856832d Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 13 Dec 2022 16:02:04 +0100 Subject: [PATCH 021/172] Upgrade version to v1.6.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 85baf2eb..4ddfa10a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.6.1" +ENV SERVICE_VERSION="1.6.2" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 229b7cbcdc2b475090be7f65403311fe53a7112f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Dec 2022 13:17:51 +0100 Subject: [PATCH 022/172] chore(deps): bump github.com/samber/lo from 1.36.0 to 1.37.0 (#133) Bumps [github.com/samber/lo](https://github.com/samber/lo) from 1.36.0 to 1.37.0. - [Release notes](https://github.com/samber/lo/releases) - [Changelog](https://github.com/samber/lo/blob/master/CHANGELOG.md) - [Commits](https://github.com/samber/lo/compare/v1.36.0...v1.37.0) --- updated-dependencies: - dependency-name: github.com/samber/lo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e6870392..238efbdd 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.47.3 github.com/prometheus/client_golang v1.14.0 - github.com/samber/lo v1.36.0 + github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 github.com/uptrace/bunrouter v1.0.19 diff --git a/go.sum b/go.sum index 0dab9782..8a78a59d 100644 --- a/go.sum +++ b/go.sum @@ -441,8 +441,8 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= -github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= +github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -487,7 +487,6 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= From c37a8e47e1dd8494c7a6025e2704d9ff5d2e0eb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 12:44:30 +0100 Subject: [PATCH 023/172] chore(deps): bump github.com/getkin/kin-openapi from 0.110.0 to 0.111.0 (#134) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.110.0 to 0.111.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.110.0...v0.111.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 238efbdd..d04830e7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.6.1 - github.com/getkin/kin-openapi v0.110.0 + github.com/getkin/kin-openapi v0.111.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index 8a78a59d..16a42b70 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.110.0 h1:1GnJALxsltcSzCMqgtqKlLhYQeULv3/jesmV2sC5qE0= -github.com/getkin/kin-openapi v0.110.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= +github.com/getkin/kin-openapi v0.111.0 h1:zspOcFKBCQOY8d9Yockcbit8iVR2hco9qLaoQoj7kmw= +github.com/getkin/kin-openapi v0.111.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From c9246289d0c3faf48a821ed7d6120a9c1b9ae9cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 12:07:13 +0100 Subject: [PATCH 024/172] chore(deps): bump github.com/open-policy-agent/opa from 0.47.3 to 0.47.4 (#135) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.47.3 to 0.47.4. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.47.3...v0.47.4) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d04830e7..487c0e22 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.47.3 + github.com/open-policy-agent/opa v0.47.4 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 16a42b70..4723d147 100644 --- a/go.sum +++ b/go.sum @@ -378,8 +378,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.47.3 h1:Uj8zw+q6Cvv1iiQFh704Q6sl3fKVvk35WZNJLsd6mgk= -github.com/open-policy-agent/opa v0.47.3/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= +github.com/open-policy-agent/opa v0.47.4 h1:CTPIoAv6/UJX+BkSkqytbofWrZHyfQ/A0ESE4FSKR9A= +github.com/open-policy-agent/opa v0.47.4/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 747aa1de8708c61faff569877bb6b6a3a265bc03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:51:25 +0100 Subject: [PATCH 025/172] chore(deps): bump github.com/getkin/kin-openapi from 0.111.0 to 0.112.0 (#136) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.111.0 to 0.112.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.111.0...v0.112.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 487c0e22..b05ba50d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.6.1 - github.com/getkin/kin-openapi v0.111.0 + github.com/getkin/kin-openapi v0.112.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index 4723d147..2579a709 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.111.0 h1:zspOcFKBCQOY8d9Yockcbit8iVR2hco9qLaoQoj7kmw= -github.com/getkin/kin-openapi v0.111.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= +github.com/getkin/kin-openapi v0.112.0 h1:lnLXx3bAG53EJVI4E/w0N8i1Y/vUZUEsnrXkgnfn7/Y= +github.com/getkin/kin-openapi v0.112.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 7804fd35e5c526e378c87c0f786bd272ca4a9178 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 10:13:26 +0000 Subject: [PATCH 026/172] chore(deps): bump github.com/davidebianchi/gswagger from 0.6.1 to 0.8.0 Bumps [github.com/davidebianchi/gswagger](https://github.com/davidebianchi/gswagger) from 0.6.1 to 0.8.0. - [Release notes](https://github.com/davidebianchi/gswagger/releases) - [Changelog](https://github.com/davidebianchi/gswagger/blob/main/CHANGELOG.md) - [Commits](https://github.com/davidebianchi/gswagger/compare/v0.6.1...v0.8.0) --- updated-dependencies: - dependency-name: github.com/davidebianchi/gswagger dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index b05ba50d..f3f3b233 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 - github.com/davidebianchi/gswagger v0.6.1 + github.com/davidebianchi/gswagger v0.8.0 github.com/getkin/kin-openapi v0.112.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index 2579a709..80be3d48 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidebianchi/go-jsonclient v1.3.0 h1:6579uXOHEDcClHK8b3j/xWbQV4Khpn7aMcH7tOmRjwY= github.com/davidebianchi/go-jsonclient v1.3.0/go.mod h1:lIOd35jxGfGENA/M0s1klHJgagIDj2KM1O6A/V5vpPI= -github.com/davidebianchi/gswagger v0.6.1 h1:bkdH2FXw9Ojo4GZ8Wzvn0B+k9eGbA+2Pu9xxqkBI9Ls= -github.com/davidebianchi/gswagger v0.6.1/go.mod h1:ThUr/leOezLiSTj6Bk7CqXBV5asBSCSrMkx4+VFyfH8= +github.com/davidebianchi/gswagger v0.8.0 h1:szFH4hYEyVPfhcpggWQgKDNnIek5rXXIW04EvNlG/M0= +github.com/davidebianchi/gswagger v0.8.0/go.mod h1:coXlWOGJDhZ1Zm/1ORvELzejxewFJIeB6DDD1vYo9zU= github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -314,8 +314,6 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= -github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -326,13 +324,11 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -493,8 +489,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/uptrace/bunrouter v1.0.19 h1:kdN1Nl/9RDq9eBPnjS6GvNKcgiAeMxdb9DbpslLndFg= github.com/uptrace/bunrouter v1.0.19/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= From 4874581dde8e26776281500c065ec6d08b3f654e Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 12:52:45 +0100 Subject: [PATCH 027/172] fix: breaking change --- main.go | 21 +++++++++++++++++---- router.go | 16 ---------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index e91f3bc6..8c76c1cb 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ import ( "time" swagger "github.com/davidebianchi/gswagger" - "github.com/davidebianchi/gswagger/apirouter" + "github.com/davidebianchi/gswagger/support/gorilla" "github.com/getkin/kin-openapi/openapi3" "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/helpers" @@ -166,7 +166,9 @@ func setupRouter( evalRouter := router.NewRoute().Subrouter() if env.Standalone { router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) - swaggerRouter, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(router), swagger.Options{ + + // swaggerRouter, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(router), + swaggerRouter, err := swagger.NewRouter(gorilla.NewRouter(router), swagger.Options{ Context: context.Background(), Openapi: &openapi3.T{ Info: &openapi3.Info{ @@ -180,11 +182,22 @@ func setupRouter( if err != nil { return nil, err } - if err := addStandaloneRoutes(swaggerRouter); err != nil { + + // standalone routes + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings/resource/{resourceType}", revokeHandler, revokeDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings/resource/{resourceType}", grantHandler, grantDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings", revokeHandler, revokeDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings", grantHandler, grantDefinitions); err != nil { return nil, err } - if err = swaggerRouter.GenerateAndExposeSwagger(); err != nil { + if err = swaggerRouter.GenerateAndExposeOpenapi(); err != nil { return nil, err } } diff --git a/router.go b/router.go index 6a0eb50e..56f51972 100644 --- a/router.go +++ b/router.go @@ -87,22 +87,6 @@ var grantDefinitions = swagger.Definitions{ }, } -func addStandaloneRoutes(router *swagger.Router) error { - if _, err := router.AddRoute(http.MethodPost, "/revoke/bindings/resource/{resourceType}", revokeHandler, revokeDefinitions); err != nil { - return err - } - if _, err := router.AddRoute(http.MethodPost, "/grant/bindings/resource/{resourceType}", grantHandler, grantDefinitions); err != nil { - return err - } - if _, err := router.AddRoute(http.MethodPost, "/revoke/bindings", revokeHandler, revokeDefinitions); err != nil { - return err - } - if _, err := router.AddRoute(http.MethodPost, "/grant/bindings", grantHandler, grantDefinitions); err != nil { - return err - } - return nil -} - func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVariables) { var documentationPermission string documentationPathInOAS := oas.Paths[env.TargetServiceOASPath] From a82ae362ed7b0c29f6edf0d1fe17ebb615be29bb Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Mon, 2 Jan 2023 12:54:07 +0100 Subject: [PATCH 028/172] Update main.go --- main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/main.go b/main.go index 8c76c1cb..ff353c90 100644 --- a/main.go +++ b/main.go @@ -167,7 +167,6 @@ func setupRouter( if env.Standalone { router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) - // swaggerRouter, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(router), swaggerRouter, err := swagger.NewRouter(gorilla.NewRouter(router), swagger.Options{ Context: context.Background(), Openapi: &openapi3.T{ From 7a2578767a3da414bee910d368b6bf9dcdb5651f Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 15:08:02 +0100 Subject: [PATCH 029/172] refactor: modulired code --- core/core.go | 103 ++++ opaevaluator.go => core/opaevaluator.go | 32 +- .../opaevaluator_test.go | 213 +++++++- handler.go | 38 +- handler_test.go | 301 +++++------ internal/utils/general.go | 14 + internal/utils/http.go | 37 ++ internal/utils/http_test.go | 50 ++ main.go | 16 +- main_test.go | 74 ++- opa_transport.go | 15 +- opa_transport_test.go | 9 +- opamiddleware.go | 191 ------- opamiddleware_test.go | 501 ------------------ openapi/opamiddleware.go | 64 +++ openapi/opamiddleware_test.go | 140 +++++ openapi_utils.go => openapi/openapi_utils.go | 79 +-- .../openapi_utils_test.go | 36 +- router.go | 87 ++- router_test.go | 397 ++++++++++++-- statusroutes.go | 3 +- statusroutes_test.go | 17 +- utilities.go | 20 +- utilities_test.go | 44 +- 24 files changed, 1318 insertions(+), 1163 deletions(-) create mode 100644 core/core.go rename opaevaluator.go => core/opaevaluator.go (92%) rename opaevaluator_test.go => core/opaevaluator_test.go (55%) create mode 100644 internal/utils/http.go create mode 100644 internal/utils/http_test.go delete mode 100644 opamiddleware.go delete mode 100644 opamiddleware_test.go create mode 100644 openapi/opamiddleware.go create mode 100644 openapi/opamiddleware_test.go rename openapi_utils.go => openapi/openapi_utils.go (83%) rename openapi_utils_test.go => openapi/openapi_utils_test.go (95%) diff --git a/core/core.go b/core/core.go new file mode 100644 index 00000000..a519dac9 --- /dev/null +++ b/core/core.go @@ -0,0 +1,103 @@ +package core + +import ( + "context" + "fmt" + "net/http" + "net/url" + "os" + "path/filepath" + + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/types" +) + +// type RondCore struct { +// partialEvaluators +// } +type OPAModuleConfigKey struct{} + +type OPAModuleConfig struct { + Name string + Content string +} + +func WithOPAModuleConfig(requestContext context.Context, permission *OPAModuleConfig) context.Context { + return context.WithValue(requestContext, OPAModuleConfigKey{}, permission) +} + +// GetOPAModuleConfig can be used by a request handler to get OPAModuleConfig instance from its context. +func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error) { + permission, ok := requestContext.Value(OPAModuleConfigKey{}).(*OPAModuleConfig) + if !ok { + return nil, fmt.Errorf("no opa module config found in request context") + } + + return permission, nil +} + +type Input struct { + Request InputRequest `json:"request"` + Response InputResponse `json:"response"` + ClientType string `json:"clientType,omitempty"` + User InputUser `json:"user"` +} +type InputRequest struct { + Body interface{} `json:"body,omitempty"` + Headers http.Header `json:"headers,omitempty"` + Query url.Values `json:"query,omitempty"` + PathParams map[string]string `json:"pathParams,omitempty"` + Method string `json:"method"` + Path string `json:"path"` +} + +type InputResponse struct { + Body interface{} `json:"body,omitempty"` +} + +type InputUser struct { + Properties map[string]interface{} `json:"properties,omitempty"` + Groups []string `json:"groups,omitempty"` + Bindings []types.Binding `json:"bindings,omitempty"` + Roles []types.Role `json:"roles,omitempty"` + ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` +} + +type PermissionOnResourceKey string + +type PermissionsOnResourceMap map[PermissionOnResourceKey]bool + +func buildPermissionOnResourceKey(permission string, resourceType string, resourceId string) PermissionOnResourceKey { + return PermissionOnResourceKey(fmt.Sprintf("%s:%s:%s", permission, resourceType, resourceId)) +} + +func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { + var regoModulePath string + //#nosec G104 -- Produces a false positive + filepath.Walk(rootDirectory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if regoModulePath != "" { + return nil + } + + if filepath.Ext(path) == ".rego" { + regoModulePath = path + } + return nil + }) + + if regoModulePath == "" { + return nil, fmt.Errorf("no rego module found in directory") + } + fileContent, err := utils.ReadFile(regoModulePath) + if err != nil { + return nil, fmt.Errorf("failed rego file read: %s", err.Error()) + } + + return &OPAModuleConfig{ + Name: filepath.Base(regoModulePath), + Content: string(fileContent), + }, nil +} diff --git a/opaevaluator.go b/core/opaevaluator.go similarity index 92% rename from opaevaluator.go rename to core/opaevaluator.go index 376c6ffa..1807b77e 100644 --- a/opaevaluator.go +++ b/core/opaevaluator.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package core import ( "bytes" @@ -29,6 +29,8 @@ import ( "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/opatranslator" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/rond-authz/rond/custom_builtins" @@ -47,7 +49,7 @@ type Evaluator interface { Partial(ctx context.Context) (*rego.PartialQueries, error) } -var unknowns = []string{"data.resources"} +var Unknowns = []string{"data.resources"} type OPAEvaluator struct { PolicyEvaluator Evaluator @@ -62,7 +64,7 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(policy string, ctx context.Context, mongoClient types.IMongoClient, oas *OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (*PartialEvaluator, error) { +func createPartialEvaluator(policy string, ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (*PartialEvaluator, error) { glogger.Get(ctx).Infof("precomputing rego query for allow policy: %s", policy) policyEvaluatorTime := time.Now() @@ -76,7 +78,7 @@ func createPartialEvaluator(policy string, ctx context.Context, mongoClient type return nil, err } -func setupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (PartialResultsEvaluators, error) { policyEvaluators := PartialResultsEvaluators{} for path, OASContent := range oas.Paths { for verb, verbConfig := range OASContent { @@ -165,7 +167,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod rego.Query(queryString), rego.Module(opaModuleConfig.Name, opaModuleConfig.Content), rego.ParsedInput(inputTerm.Value), - rego.Unknowns(unknowns), + rego.Unknowns(Unknowns), rego.Capabilities(ast.CapabilitiesForThisVersion()), rego.EnablePrintStatements(env.LogLevel == config.TraceLogLevel), rego.PrintHook(NewPrintHook(os.Stdout, policy)), @@ -181,7 +183,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod }, nil } -func createQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.Request, env config.EnvironmentVariables, policy string, input []byte, responseBody interface{}) (*OPAEvaluator, error) { +func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.Request, env config.EnvironmentVariables, policy string, input []byte, responseBody interface{}) (*OPAEvaluator, error) { opaModuleConfig, err := GetOPAModuleConfig(req.Context()) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no OPA module configuration found in context") @@ -209,7 +211,7 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf options := []func(*rego.Rego){ rego.Query(queryString), rego.Module(opaModuleConfig.Name, opaModuleConfig.Content), - rego.Unknowns(unknowns), + rego.Unknowns(Unknowns), rego.EnablePrintStatements(env.LogLevel == config.TraceLogLevel), rego.PrintHook(NewPrintHook(os.Stdout, policy)), rego.Capabilities(ast.CapabilitiesForThisVersion()), @@ -252,7 +254,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error()) } - routerInfo, err := GetRouterInfo(evaluator.Context) + routerInfo, err := openapi.GetRouterInfo(evaluator.Context) if err != nil { return nil, err } @@ -291,13 +293,13 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv return q, nil } -func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, error) { +func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, error) { opaEvaluationTimeStart := time.Now() results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context) if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error()) } - routerInfo, err := GetRouterInfo(evaluator.Context) + routerInfo, err := openapi.GetRouterInfo(evaluator.Context) if err != nil { return nil, err } @@ -349,24 +351,24 @@ func (evaluator *OPAEvaluator) evaluate(logger *logrus.Entry) (interface{}, erro return nil, fmt.Errorf("RBAC policy evaluation failed, user is not allowed") } -func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *RondConfig) (interface{}, primitive.M, error) { +func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *openapi.RondConfig) (interface{}, primitive.M, error) { if permission.RequestFlow.GenerateQuery { query, err := evaluator.partiallyEvaluate(logger) return nil, query, err } - dataFromEvaluation, err := evaluator.evaluate(logger) + dataFromEvaluation, err := evaluator.Evaluate(logger) if err != nil { return nil, nil, err } return dataFromEvaluation, nil, nil } -func createRegoQueryInput(req *http.Request, env config.EnvironmentVariables, enableResourcePermissionsMapOptimization bool, user types.User, responseBody interface{}) ([]byte, error) { +func CreateRegoQueryInput(req *http.Request, env config.EnvironmentVariables, enableResourcePermissionsMapOptimization bool, user types.User, responseBody interface{}) ([]byte, error) { requestContext := req.Context() logger := glogger.Get(requestContext) opaInputCreationTime := time.Now() userProperties := make(map[string]interface{}) - _, err := unmarshalHeader(req.Header, env.UserPropertiesHeader, &userProperties) + _, err := utils.UnmarshalHeader(req.Header, env.UserPropertiesHeader, &userProperties) if err != nil { return nil, fmt.Errorf("user properties header is not valid: %s", err.Error()) } @@ -406,7 +408,7 @@ func createRegoQueryInput(req *http.Request, env config.EnvironmentVariables, en }, } - shouldParseJSONBody := hasApplicationJSONContentType(req.Header) && + shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && req.ContentLength > 0 && (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) diff --git a/opaevaluator_test.go b/core/opaevaluator_test.go similarity index 55% rename from opaevaluator_test.go rename to core/opaevaluator_test.go index 851febc6..fc623472 100644 --- a/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package core import ( "bytes" @@ -26,6 +26,10 @@ import ( "testing" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/mia-platform/glogger/v2" @@ -36,6 +40,7 @@ import ( ) func TestNewOPAEvaluator(t *testing.T) { + envs := config.EnvironmentVariables{} input := map[string]interface{}{} inputBytes, _ := json.Marshal(input) t.Run("policy sanitization", func(t *testing.T) { @@ -64,7 +69,7 @@ func TestCreateRegoInput(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.Header.Set("userproperties", "") - _, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") }) @@ -75,7 +80,7 @@ func TestCreateRegoInput(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.Header.Set("userproperties", "1") - _, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Error(t, err) }) }) @@ -91,16 +96,16 @@ func TestCreateRegoInput(t *testing.T) { t.Run("ignored on method GET", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - inputBytes, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) }) t.Run("ignore nil body on method POST", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", nil) - req.Header.Set(ContentTypeHeaderKey, "application/json") + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - inputBytes, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) }) @@ -110,8 +115,8 @@ func TestCreateRegoInput(t *testing.T) { for _, method := range acceptedMethods { req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(ContentTypeHeaderKey, "application/json") - inputBytes, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") require.True(t, strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody)), "Unexpected body for method %s", method) @@ -120,8 +125,8 @@ func TestCreateRegoInput(t *testing.T) { t.Run("added with content-type specifying charset", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(ContentTypeHeaderKey, "application/json;charset=UTF-8") - inputBytes, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") + inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") require.True(t, strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody)), "Unexpected body for method %s", http.MethodPost) @@ -129,16 +134,16 @@ func TestCreateRegoInput(t *testing.T) { t.Run("reject on method POST but with invalid body", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(ContentTypeHeaderKey, "application/json") - _, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.True(t, err != nil) }) t.Run("ignore body on method POST but with another content type", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(ContentTypeHeaderKey, "multipart/form-data") + req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") - inputBytes, err := createRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) + inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) require.Nil(t, err, "Unexpected error") require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) }) @@ -154,13 +159,13 @@ func TestCreatePolicyEvaluators(t *testing.T) { APIPermissionsFilePath: "./mocks/simplifiedMock.json", OPAModulesDirectory: "./mocks/rego-policies", } - openApiSpec, err := loadOASFromFileOrNetwork(log, envs) + openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) require.NoError(t, err, "unexpected error") - opaModuleConfig, err := loadRegoModule(envs.OPAModulesDirectory) + opaModuleConfig, err := LoadRegoModule(envs.OPAModulesDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := setupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) + policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -173,13 +178,13 @@ func TestCreatePolicyEvaluators(t *testing.T) { APIPermissionsFilePath: "./mocks/pathsConfigAllInclusive.json", OPAModulesDirectory: "./mocks/rego-policies", } - openApiSpec, err := loadOASFromFileOrNetwork(log, envs) + openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) require.NoError(t, err, "unexpected error") - opaModuleConfig, err := loadRegoModule(envs.OPAModulesDirectory) + opaModuleConfig, err := LoadRegoModule(envs.OPAModulesDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := setupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) + policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -258,6 +263,56 @@ func TestBuildOptimizedResourcePermissionsMap(t *testing.T) { } require.Equal(t, expected, result) } +func TestCreateQueryEvaluator(t *testing.T) { + envs := config.EnvironmentVariables{} + policy := `package policies +allow { + true +} +column_policy{ + false +} +` + permission := openapi.XPermission{ + AllowPermission: "allow", + ResponseFilter: openapi.ResponseFilterConfiguration{ + Policy: "column_policy", + }, + } + + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{TargetServiceHost: "test"}, + nil, + &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "allow"}, + ResponseFlow: openapi.ResponseFlow{PolicyName: "column_policy"}, + }, + + &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + nil, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) + require.NoError(t, err, "Unexpected error") + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + input := Input{Request: InputRequest{}, Response: InputResponse{}} + inputBytes, _ := json.Marshal(input) + + t.Run("create evaluator with allowPolicy", func(t *testing.T) { + evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, envs, permission.AllowPermission, inputBytes, nil) + require.True(t, evaluator != nil) + require.NoError(t, err, "Unexpected status code.") + }) + + t.Run("create evaluator with policy for column filtering", func(t *testing.T) { + evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, envs, permission.ResponseFilter.Policy, inputBytes, nil) + require.True(t, evaluator != nil) + require.NoError(t, err, "Unexpected status code.") + }) +} func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { var roles []types.Role @@ -305,3 +360,121 @@ func TestPrint(t *testing.T) { var re = regexp.MustCompile(`"time":\d+`) require.JSONEq(t, `{"level":10,"msg":"the print message","time":123,"policyName":"policy-name"}`, string(re.ReplaceAll(buf.Bytes(), []byte("\"time\":123")))) } + +func createContext( + t *testing.T, + originalCtx context.Context, + env config.EnvironmentVariables, + mongoClient *mocks.MongoClientMock, + permission *openapi.RondConfig, + opaModuleConfig *OPAModuleConfig, + partialResultEvaluators PartialResultsEvaluators, +) context.Context { + t.Helper() + + var partialContext context.Context + partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) + partialContext = context.WithValue(partialContext, openapi.XPermissionKey{}, permission) + partialContext = context.WithValue(partialContext, OPAModuleConfigKey{}, opaModuleConfig) + if mongoClient != nil { + partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) + } + partialContext = context.WithValue(partialContext, PartialResultsEvaluatorConfigKey{}, partialResultEvaluators) + + log, _ := test.NewNullLogger() + partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(log)) + + partialContext = context.WithValue(partialContext, openapi.RouterInfoKey{}, openapi.RouterInfo{ + MatchedPath: "/matched/path", + RequestedPath: "/requested/path", + Method: "GET", + }) + + partialContext = metrics.WithValue(partialContext, metrics.SetupMetrics("test_rond")) + + return partialContext +} +func TestGetHeaderFunction(t *testing.T) { + headerKeyMocked := "exampleKey" + headerValueMocked := "value" + env := config.EnvironmentVariables{} + + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + todo { get_header("ExAmPlEkEy", input.headers) == "value" }`, + } + queryString := "todo" + + t.Run("if header key exists", func(t *testing.T) { + headers := http.Header{} + headers.Add(headerKeyMocked, headerValueMocked) + input := map[string]interface{}{ + "headers": headers, + } + inputBytes, _ := json.Marshal(input) + + opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) + require.NoError(t, err, "Unexpected error during creation of opaEvaluator") + + results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) + require.NoError(t, err, "Unexpected error during rego validation") + require.True(t, results.Allowed(), "The input is not allowed by rego") + + partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) + require.NoError(t, err, "Unexpected error during rego validation") + + require.Len(t, partialResults.Queries, 1, "Rego policy allows illegal input") + }) + + t.Run("if header key not exists", func(t *testing.T) { + input := map[string]interface{}{ + "headers": http.Header{}, + } + inputBytes, _ := json.Marshal(input) + + opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) + require.NoError(t, err, "Unexpected error during creation of opaEvaluator") + + results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) + require.NoError(t, err, "Unexpected error during rego validation") + require.True(t, !results.Allowed(), "Rego policy allows illegal input") + + partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) + require.NoError(t, err, "Unexpected error during rego validation") + + require.Len(t, partialResults.Queries, 0, "Rego policy allows illegal input") + }) +} + +func TestGetOPAModuleConfig(t *testing.T) { + t.Run(`GetOPAModuleConfig fails because no key has been passed`, func(t *testing.T) { + ctx := context.Background() + env, err := GetOPAModuleConfig(ctx) + require.True(t, err != nil, "An error was expected.") + t.Logf("Expected error: %s - env: %+v", err.Error(), env) + }) + + t.Run(`GetOPAModuleConfig returns OPAEvaluator from context`, func(t *testing.T) { + ctx := context.WithValue(context.Background(), OPAModuleConfigKey{}, &OPAModuleConfig{}) + opaEval, err := GetOPAModuleConfig(ctx) + require.True(t, err == nil, "Unexpected error.") + require.True(t, opaEval != nil, "OPA Module config not found.") + }) +} + +func TestGetPolicyEvaluators(t *testing.T) { + t.Run(`GetPolicyEvaluators fails because no key has been passed`, func(t *testing.T) { + ctx := context.Background() + env, err := GetPartialResultsEvaluators(ctx) + require.True(t, err != nil, "An error was expected.") + t.Logf("Expected error: %s - env: %+v", err.Error(), env) + }) + + t.Run(`GetPartialResultsEvaluators returns PartialResultsEvaluators from context`, func(t *testing.T) { + ctx := context.WithValue(context.Background(), PartialResultsEvaluatorConfigKey{}, PartialResultsEvaluators{}) + opaEval, err := GetPartialResultsEvaluators(ctx) + require.True(t, err == nil, "Unexpected error.") + require.True(t, opaEval != nil, "OPA Module config not found.") + }) +} diff --git a/handler.go b/handler.go index 8a9a98a9..a2c18d34 100644 --- a/handler.go +++ b/handler.go @@ -20,9 +20,12 @@ import ( "net/http" "net/http/httputil" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/mia-platform/glogger/v2" "github.com/sirupsen/logrus" @@ -38,8 +41,8 @@ func ReverseProxyOrResponse( env config.EnvironmentVariables, w http.ResponseWriter, req *http.Request, - permission *RondConfig, - partialResultsEvaluators PartialResultsEvaluators, + permission *openapi.RondConfig, + partialResultsEvaluators core.PartialResultsEvaluators, ) { if env.Standalone { if permission.RequestFlow.GenerateQuery { @@ -70,13 +73,13 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { return } - permission, err := GetXPermission(requestContext) + permission, err := openapi.GetXPermission(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no policy permission found in context") failResponse(w, "no policy permission found in context", GENERIC_BUSINESS_ERROR_MESSAGE) return } - partialResultEvaluators, err := GetPartialResultsEvaluators(requestContext) + partialResultEvaluators, err := core.GetPartialResultsEvaluators(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no partialResult evaluators found in context") failResponse(w, "no partialResult evaluators found in context", GENERIC_BUSINESS_ERROR_MESSAGE) @@ -89,7 +92,13 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { ReverseProxyOrResponse(logger, env, w, req, permission, partialResultEvaluators) } -func EvaluateRequest(req *http.Request, env config.EnvironmentVariables, w http.ResponseWriter, partialResultsEvaluators PartialResultsEvaluators, permission *RondConfig) error { +func EvaluateRequest( + req *http.Request, + env config.EnvironmentVariables, + w http.ResponseWriter, + partialResultsEvaluators core.PartialResultsEvaluators, + permission *openapi.RondConfig, +) error { requestContext := req.Context() logger := glogger.Get(requestContext) @@ -100,14 +109,14 @@ func EvaluateRequest(req *http.Request, env config.EnvironmentVariables, w http. return err } - input, err := createRegoQueryInput(req, env, permission.Options.EnableResourcePermissionsMapOptimization, userInfo, nil) + input, err := core.CreateRegoQueryInput(req, env, permission.Options.EnableResourcePermissionsMapOptimization, userInfo, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed rego query input creation") failResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", GENERIC_BUSINESS_ERROR_MESSAGE) return err } - var evaluatorAllowPolicy *OPAEvaluator + var evaluatorAllowPolicy *core.OPAEvaluator if !permission.RequestFlow.GenerateQuery { evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, input, env) if err != nil { @@ -116,7 +125,7 @@ func EvaluateRequest(req *http.Request, env config.EnvironmentVariables, w http. return err } } else { - evaluatorAllowPolicy, err = createQueryEvaluator(requestContext, logger, req, env, permission.RequestFlow.PolicyName, input, nil) + evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, env, permission.RequestFlow.PolicyName, input, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot create evaluator") failResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", NO_PERMISSIONS_ERROR_MESSAGE) @@ -126,8 +135,8 @@ func EvaluateRequest(req *http.Request, env config.EnvironmentVariables, w http. _, query, err := evaluatorAllowPolicy.PolicyEvaluation(logger, permission) if err != nil { - if errors.Is(err, opatranslator.ErrEmptyQuery) && hasApplicationJSONContentType(req.Header) { - w.Header().Set(ContentTypeHeaderKey, JSONContentTypeHeader) + if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { + w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) w.WriteHeader(http.StatusOK) if _, err := w.Write([]byte("[]")); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("failed response write") @@ -163,7 +172,14 @@ func EvaluateRequest(req *http.Request, env config.EnvironmentVariables, w http. return nil } -func ReverseProxy(logger *logrus.Entry, env config.EnvironmentVariables, w http.ResponseWriter, req *http.Request, permission *RondConfig, partialResultsEvaluators PartialResultsEvaluators) { +func ReverseProxy( + logger *logrus.Entry, + env config.EnvironmentVariables, + w http.ResponseWriter, + req *http.Request, + permission *openapi.RondConfig, + partialResultsEvaluators core.PartialResultsEvaluators, +) { targetHostFromEnv := env.TargetServiceHost proxy := httputil.ReverseProxy{ FlushInterval: -1, diff --git a/handler_test.go b/handler_test.go index 28c3e8b7..3e5b341d 100644 --- a/handler_test.go +++ b/handler_test.go @@ -30,12 +30,15 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" @@ -48,27 +51,29 @@ import ( ) func TestDirectProxyHandler(t *testing.T) { - oas := OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + var envs = config.EnvironmentVariables{} + + oas := openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, }, }, }, }, } - oasWithFilter := OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + oasWithFilter := openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: openapi.QueryOptions{ HeaderName: "rowfilterquery", }, }, @@ -92,7 +97,7 @@ func TestDirectProxyHandler(t *testing.T) { })) defer server.Close() - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -121,7 +126,7 @@ func TestDirectProxyHandler(t *testing.T) { mockHeader := "CustomHeader" mockHeaderValue := "mocked value" - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -156,7 +161,7 @@ func TestDirectProxyHandler(t *testing.T) { invoked := false mockBodySting := "I am a body" - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -184,7 +189,7 @@ func TestDirectProxyHandler(t *testing.T) { r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) require.NoError(t, err, "Unexpected error") - r.Header.Set(ContentTypeHeaderKey, "text/plain") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) @@ -199,13 +204,13 @@ func TestDirectProxyHandler(t *testing.T) { t.Run("sends request with body after serialization in rego input", func(t *testing.T) { invoked := false mockBodySting := `{"hello":"world"}` - opaModuleConfig := &OPAModuleConfig{ + OPAModuleConfig := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { input.request.body.hello == "world" }`, } - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true @@ -225,13 +230,13 @@ func TestDirectProxyHandler(t *testing.T) { context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, - &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}}, - opaModuleConfig, + &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, + OPAModuleConfig, partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://www.example.com:8080/api", body) - r.Header.Set(ContentTypeHeaderKey, "application/json") + r.Header.Set(utils.ContentTypeHeaderKey, "application/json") require.NoError(t, err, "Unexpected error") w := httptest.NewRecorder() @@ -287,9 +292,9 @@ allow { body := strings.NewReader(mockBodySting) - opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -298,7 +303,7 @@ allow { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) @@ -306,7 +311,7 @@ allow { require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") - r.Header.Set(ContentTypeHeaderKey, "text/plain") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) @@ -359,16 +364,16 @@ allow { serverURL, _ := url.Parse(server.URL) - opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) @@ -376,7 +381,7 @@ allow { require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") - r.Header.Set(ContentTypeHeaderKey, "text/plain") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) @@ -408,28 +413,28 @@ allow { serverURL, _ := url.Parse(server.URL) - opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) require.NoError(t, err, "Unexpected error") - r.Header.Set(ContentTypeHeaderKey, "application/json") + r.Header.Set(utils.ContentTypeHeaderKey, "application/json") w := httptest.NewRecorder() rbacHandler(w, r) require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") buf, err := io.ReadAll(w.Body) require.NoError(t, err, "Unexpected error to read body response") require.Equal(t, "[]", string(buf), "Unexpected body response") @@ -456,22 +461,22 @@ allow { serverURL, _ := url.Parse(server.URL) - opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) require.NoError(t, err, "Unexpected error") - r.Header.Set(ContentTypeHeaderKey, "text/plain") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) @@ -525,16 +530,16 @@ allow { serverURL, _ := url.Parse(server.URL) - opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) @@ -542,14 +547,14 @@ allow { require.NoError(t, err, "Unexpected error") r.Header.Set("miauserproperties", `{"name":"gianni"}`) r.Header.Set("examplekey", "value") - r.Header.Set(ContentTypeHeaderKey, "text/plain") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") w := httptest.NewRecorder() rbacHandler(w, r) require.True(t, !invoked, "Handler was not invoked.") require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") - require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") }) t.Run("data evaluation correctly added - logs and metrics", func(t *testing.T) { @@ -566,7 +571,7 @@ allow { serverURL, _ := url.Parse(server.URL) - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -632,7 +637,7 @@ allow { serverURL, _ := url.Parse(server.URL) - opaModuleConfig := &OPAModuleConfig{ + OPAModuleConfig := &core.OPAModuleConfig{ Name: "mypolicy.rego", Content: `package policies allow { @@ -642,7 +647,7 @@ allow { employee.salary < 0 }`, } - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, opaModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -650,7 +655,7 @@ allow { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, mockRondConfigWithQueryGen, - opaModuleConfig, + OPAModuleConfig, partialEvaluators, ) @@ -698,28 +703,30 @@ allow { } func TestStandaloneMode(t *testing.T) { + var envs = config.EnvironmentVariables{} + env := config.EnvironmentVariables{Standalone: true} - oas := OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + oas := openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, }, }, }, }, } - oasWithFilter := OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + oasWithFilter := openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: openapi.QueryOptions{ HeaderName: "rowfilterquery", }, }, @@ -733,7 +740,7 @@ func TestStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("ok", func(t *testing.T) { - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -782,7 +789,7 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -790,7 +797,7 @@ allow { env, nil, mockRondConfigWithQueryGen, - &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, partialEvaluators, ) @@ -832,7 +839,7 @@ allow { mockBodySting := "I am a body" body := strings.NewReader(mockBodySting) - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -840,7 +847,7 @@ allow { env, nil, mockRondConfigWithQueryGen, - &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, partialEvaluators, ) @@ -884,7 +891,7 @@ allow { ` mockBodySting := "I am a body" - partialEvaluators, err := setupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") body := strings.NewReader(mockBodySting) @@ -894,7 +901,7 @@ allow { env, nil, mockRondConfigWithQueryGen, - &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, partialEvaluators, ) @@ -912,6 +919,8 @@ allow { } func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { + var envs = config.EnvironmentVariables{} + userPropertiesHeaderKey := "miauserproperties" mockedUserProperties := map[string]interface{}{ "my": "other", @@ -930,7 +939,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { userIdHeaderKey := "miauserid" require.NoError(t, err) - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(` package policies @@ -941,12 +950,12 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }`, mockedUserProperties["my"], mockedClientType), } - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, }, }, }, @@ -976,20 +985,20 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) t.Run("without get_header built-in function", func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(`package policies todo { count(input.request.headers["%s"]) != 0 }`, mockHeader), } - partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, - &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}}, + &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, opaModule, partialEvaluators, ) @@ -1020,13 +1029,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { t.Run("using get_header built-in function to access in case-insensitive mode", func(t *testing.T) { invoked = false - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { get_header("x-backdoor", input.request.headers) == "mocked value" }`, } - partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1077,7 +1086,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(` package policies @@ -1087,7 +1096,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { input.clientType == "%s" }`, mockedUserProperties["my"], mockedClientType), } - partialEvaluators, err := setupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1144,7 +1153,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(`package policies todo[msg]{ @@ -1159,26 +1168,26 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { `, mockHeader), } - oas := OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + oas := openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, }, }, }, }, } - partialEvaluators, err := setupEvaluators(ctx, nil, &oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, nil, - &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}}, + &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, opaModule, partialEvaluators, ) @@ -1261,7 +1270,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1312,7 +1321,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1402,7 +1411,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1494,7 +1503,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1531,8 +1540,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }) t.Run("return 200 with policy on bindings and roles", func(t *testing.T) { - - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(` package policies @@ -1602,7 +1610,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1638,8 +1646,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }) t.Run("return 200 without user header", func(t *testing.T) { - - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(` package policies @@ -1661,7 +1668,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1693,10 +1700,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }) t.Run("return 200 with policy on pathParams", func(t *testing.T) { - customerId, productId := "1234", "5678" - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: fmt.Sprintf(` package policies @@ -1726,7 +1732,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1768,7 +1774,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } func TestPolicyWithMongoBuiltinIntegration(t *testing.T) { - var mockOPAModule = &OPAModuleConfig{ + envs := config.EnvironmentVariables{} + var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: ` package policies @@ -1777,13 +1784,13 @@ project := find_one("projects", {"projectId": "1234"}) project.tenantId == "1234" }`, } - var mockXPermission = &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}} - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/api": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + var mockXPermission = &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, }, }, }, @@ -1816,7 +1823,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1862,7 +1869,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1908,7 +1915,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := setupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1933,66 +1940,16 @@ project.tenantId == "1234" }) } -func TestCreateQueryEvaluator(t *testing.T) { - policy := `package policies -allow { - true -} -column_policy{ - false -} -` - permission := XPermission{ - AllowPermission: "allow", - ResponseFilter: ResponseFilterConfiguration{ - Policy: "column_policy", - }, - } - - ctx := createContext(t, - context.Background(), - config.EnvironmentVariables{TargetServiceHost: "test"}, - nil, - &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "allow"}, - ResponseFlow: ResponseFlow{PolicyName: "column_policy"}, - }, - - &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - nil, - ) - - r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - require.NoError(t, err, "Unexpected error") - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - - input := Input{Request: InputRequest{}, Response: InputResponse{}} - inputBytes, _ := json.Marshal(input) - - t.Run("create evaluator with allowPolicy", func(t *testing.T) { - evaluator, err := createQueryEvaluator(context.Background(), logger, r, envs, permission.AllowPermission, inputBytes, nil) - require.True(t, evaluator != nil) - require.NoError(t, err, "Unexpected status code.") - }) - - t.Run("create evaluator with policy for column filtering", func(t *testing.T) { - evaluator, err := createQueryEvaluator(context.Background(), logger, r, envs, permission.ResponseFilter.Policy, inputBytes, nil) - require.True(t, evaluator != nil) - require.NoError(t, err, "Unexpected status code.") - }) -} - func BenchmarkEvaluateRequest(b *testing.B) { - moduleConfig, err := loadRegoModule("./mocks/bench-policies") + moduleConfig, err := core.LoadRegoModule("./mocks/bench-policies") require.NoError(b, err, "Unexpected error") - permission := &RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_view_project"}} + permission := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "allow_view_project"}} queryString := fmt.Sprintf("data.policies.%s", permission.RequestFlow.PolicyName) query := rego.New( rego.Query(queryString), rego.Module(moduleConfig.Name, moduleConfig.Content), - rego.Unknowns(unknowns), + rego.Unknowns(core.Unknowns), rego.Capabilities(ast.CapabilitiesForThisVersion()), custom_builtins.GetHeaderFunction, custom_builtins.MongoFindOne, @@ -2004,8 +1961,8 @@ func BenchmarkEvaluateRequest(b *testing.B) { panic(err) } - partialEvaluators := PartialResultsEvaluators{ - permission.RequestFlow.PolicyName: PartialEvaluator{PartialEvaluator: &pr}, + partialEvaluators := core.PartialResultsEvaluators{ + permission.RequestFlow.PolicyName: core.PartialEvaluator{PartialEvaluator: &pr}, } envs := config.EnvironmentVariables{ @@ -2023,8 +1980,8 @@ func BenchmarkEvaluateRequest(b *testing.B) { glogger.WithLogger( context.WithValue( context.WithValue( - WithXPermission( - WithOPAModuleConfig(originalRequest.Context(), moduleConfig), + openapi.WithXPermission( + core.WithOPAModuleConfig(originalRequest.Context(), moduleConfig), permission, ), types.MongoClientContextKey{}, testmongoMock, diff --git a/internal/utils/general.go b/internal/utils/general.go index 91920ee8..f300b307 100644 --- a/internal/utils/general.go +++ b/internal/utils/general.go @@ -15,11 +15,16 @@ package utils import ( + "errors" + "fmt" + "os" "strings" "github.com/samber/lo" ) +var ErrFileLoadFailed = errors.New("file loading failed") + var Contains = lo.Contains[string] func FilterList(list []string, valuesToFilter []string) []string { @@ -34,3 +39,12 @@ func SanitizeString(input string) string { } var Union = lo.Union[string] + +func ReadFile(path string) ([]byte, error) { + //#nosec G304 -- This is an expected behaviour + fileContentByte, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrFileLoadFailed, err.Error()) + } + return fileContentByte, nil +} diff --git a/internal/utils/http.go b/internal/utils/http.go new file mode 100644 index 00000000..f2598bbf --- /dev/null +++ b/internal/utils/http.go @@ -0,0 +1,37 @@ +// 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 utils + +import ( + "encoding/json" + "net/http" + "strings" +) + +const ContentTypeHeaderKey = "content-type" +const JSONContentTypeHeader = "application/json" + +func UnmarshalHeader(headers http.Header, headerKey string, v interface{}) (bool, error) { + headerValueStringified := headers.Get(headerKey) + if headerValueStringified != "" { + err := json.Unmarshal([]byte(headerValueStringified), &v) + return err == nil, err + } + return false, nil +} + +func HasApplicationJSONContentType(headers http.Header) bool { + return strings.HasPrefix(headers.Get(ContentTypeHeaderKey), JSONContentTypeHeader) +} diff --git a/internal/utils/http_test.go b/internal/utils/http_test.go new file mode 100644 index 00000000..e2470b67 --- /dev/null +++ b/internal/utils/http_test.go @@ -0,0 +1,50 @@ +package utils + +import ( + "encoding/json" + "net/http" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUnmarshalHeader(t *testing.T) { + userPropertiesHeaderKey := "miauserproperties" + mockedUserProperties := map[string]interface{}{ + "my": "other", + "key": []string{"is", "not"}, + } + mockedUserPropertiesStringified, err := json.Marshal(mockedUserProperties) + require.NoError(t, err) + + t.Run("header not exists", func(t *testing.T) { + headers := http.Header{} + var userProperties map[string]interface{} + + ok, err := UnmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) + + require.True(t, !ok, "Unmarshal not existing header") + require.NoError(t, err, "Unexpected error if doesn't exist header") + }) + + t.Run("header exists but the unmarshalling fails", func(t *testing.T) { + headers := http.Header{} + headers.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) + var userProperties string + + ok, err := UnmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) + require.False(t, ok, "Unexpected success during unmarshalling") + var unmarshalErr = &json.UnmarshalTypeError{} + require.ErrorAs(t, err, &unmarshalErr, "Unexpected error on unmarshalling") + }) + + t.Run("header exists and unmarshalling finishes correctly", func(t *testing.T) { + headers := http.Header{} + headers.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) + var userProperties map[string]interface{} + + ok, err := UnmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) + require.True(t, ok, "Unexpected failure") + require.NoError(t, err, "Unexpected error") + }) +} diff --git a/main.go b/main.go index e91f3bc6..1bd0c9e9 100644 --- a/main.go +++ b/main.go @@ -27,18 +27,18 @@ import ( "github.com/davidebianchi/gswagger/apirouter" "github.com/getkin/kin-openapi/openapi3" "github.com/prometheus/client_golang/prometheus" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/openapi" "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" "github.com/sirupsen/logrus" ) -const HTTPScheme = "http" - func main() { entrypoint(make(chan os.Signal, 1)) os.Exit(0) @@ -61,7 +61,7 @@ func entrypoint(shutdown chan os.Signal) { return } - opaModuleConfig, err := loadRegoModule(env.OPAModulesDirectory) + opaModuleConfig, err := core.LoadRegoModule(env.OPAModulesDirectory) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, @@ -71,7 +71,7 @@ func entrypoint(shutdown chan os.Signal) { } log.WithField("opaModuleFileName", opaModuleConfig.Name).Trace("rego module successfully loaded") - oas, err := loadOASFromFileOrNetwork(log, env) + oas, err := openapi.LoadOASFromFileOrNetwork(log, env) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, @@ -98,7 +98,7 @@ func entrypoint(shutdown chan os.Signal) { logrus.NewEntry(log), ) - policiesEvaluators, err := setupEvaluators(ctx, mongoClient, oas, opaModuleConfig, env) + policiesEvaluators, err := core.SetupEvaluators(ctx, mongoClient, oas, opaModuleConfig, env) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, @@ -143,9 +143,9 @@ func entrypoint(shutdown chan os.Signal) { func setupRouter( log *logrus.Logger, env config.EnvironmentVariables, - opaModuleConfig *OPAModuleConfig, - oas *OpenAPISpec, - policiesEvaluators PartialResultsEvaluators, + opaModuleConfig *core.OPAModuleConfig, + oas *openapi.OpenAPISpec, + policiesEvaluators core.PartialResultsEvaluators, mongoClient *mongoclient.MongoClient, ) (*mux.Router, error) { router := mux.NewRouter().UseEncodedPath() diff --git a/main_test.go b/main_test.go index 9dcf1766..ffd41c91 100644 --- a/main_test.go +++ b/main_test.go @@ -29,9 +29,12 @@ import ( "time" "github.com/mia-platform/glogger/v2" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -712,7 +715,7 @@ func TestEntrypoint(t *testing.T) { require.NoError(t, err) req.Header.Set("miauserid", "user1") req.Header.Set("miausergroups", "user1,user2") - req.Header.Set(ContentTypeHeaderKey, "application/json") + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") client := &http.Client{} resp, err := client.Do(req) require.Equal(t, "user1", resp.Header.Get("someuserheader")) @@ -758,7 +761,7 @@ func TestEntrypoint(t *testing.T) { require.NoError(t, err) req.Header.Set("miauserid", "user1") req.Header.Set("miausergroups", "user1,user2") - req.Header.Set(ContentTypeHeaderKey, "application/json") + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") client := &http.Client{} resp, err := client.Do(req) @@ -795,7 +798,7 @@ func TestEntrypoint(t *testing.T) { req.Header.Set("miauserid", "user1") req.Header.Set("miausergroups", "user1,user2") - req.Header.Set(ContentTypeHeaderKey, "application/json") + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") client := &http.Client{} resp, err := client.Do(req) @@ -1672,7 +1675,7 @@ func TestSetupRouterStandaloneMode(t *testing.T) { BindingsCrudServiceURL: "http://crud:3030", AdditionalHeadersToProxy: "miauserid", } - opa := &OPAModuleConfig{ + opa := &core.OPAModuleConfig{ Name: "policies", Content: `package policies test_policy { true } @@ -1683,19 +1686,23 @@ filter_policy { } `, } - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/evalapi": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "test_policy"}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/evalapi": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "test_policy"}, }, }, }, - "/evalfilter": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "filter_policy", GenerateQuery: true, QueryOptions: QueryOptions{HeaderName: "my-query"}}, + "/evalfilter": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "filter_policy", + GenerateQuery: true, + QueryOptions: openapi.QueryOptions{HeaderName: "my-query"}, + }, }, }, }, @@ -1703,7 +1710,7 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, env) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) require.NoError(t, err, "unexpected error") router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) @@ -1823,7 +1830,7 @@ func TestSetupRouterMetrics(t *testing.T) { AdditionalHeadersToProxy: "miauserid", ExposeMetrics: true, } - opa := &OPAModuleConfig{ + opa := &core.OPAModuleConfig{ Name: "policies", Content: `package policies test_policy { true } @@ -1834,19 +1841,23 @@ filter_policy { } `, } - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/evalapi": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "test_policy"}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/evalapi": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{PolicyName: "test_policy"}, }, }, }, - "/evalfilter": PathVerbs{ - "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "filter_policy", GenerateQuery: true, QueryOptions: QueryOptions{HeaderName: "my-query"}}, + "/evalfilter": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "filter_policy", + GenerateQuery: true, + QueryOptions: openapi.QueryOptions{HeaderName: "my-query"}, + }, }, }, }, @@ -1854,7 +1865,7 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, env) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) require.NoError(t, err, "unexpected error") router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) @@ -1871,3 +1882,12 @@ filter_policy { require.Contains(t, string(responseBody), "go_gc_duration_seconds") }) } + +func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { + t.Helper() + + responseBody, err := io.ReadAll(w.Result().Body) + require.NoError(t, err) + + return responseBody +} diff --git a/opa_transport.go b/opa_transport.go index e60adfe5..d9700ac1 100644 --- a/opa_transport.go +++ b/opa_transport.go @@ -23,8 +23,11 @@ import ( "net/http" "strconv" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -36,8 +39,8 @@ type OPATransport struct { context context.Context logger *logrus.Entry request *http.Request - permission *RondConfig - partialResultsEvaluators PartialResultsEvaluators + permission *openapi.RondConfig + partialResultsEvaluators core.PartialResultsEvaluators env config.EnvironmentVariables } @@ -67,8 +70,8 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return resp, nil } - if !hasApplicationJSONContentType(resp.Header) { - t.logger.WithField("foundContentType", resp.Header.Get(ContentTypeHeaderKey)).Debug("found content type") + if !utils.HasApplicationJSONContentType(resp.Header) { + t.logger.WithField("foundContentType", resp.Header.Get(utils.ContentTypeHeaderKey)).Debug("found content type") t.responseWithError(resp, fmt.Errorf("content-type is not application/json"), http.StatusInternalServerError) return resp, nil } @@ -84,7 +87,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return resp, nil } - input, err := createRegoQueryInput(t.request, t.env, t.permission.Options.EnableResourcePermissionsMapOptimization, userInfo, decodedBody) + input, err := core.CreateRegoQueryInput(t.request, t.env, t.permission.Options.EnableResourcePermissionsMapOptimization, userInfo, decodedBody) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil @@ -100,7 +103,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return resp, nil } - bodyToProxy, err := evaluator.evaluate(t.logger) + bodyToProxy, err := evaluator.Evaluate(t.logger) if err != nil { t.responseWithError(resp, err, http.StatusForbidden) return resp, nil diff --git a/opa_transport_test.go b/opa_transport_test.go index 67dbc01b..275f0f0e 100644 --- a/opa_transport_test.go +++ b/opa_transport_test.go @@ -25,9 +25,11 @@ import ( "strings" "testing" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" @@ -85,6 +87,7 @@ func TestIs2xx(t *testing.T) { } func TestOPATransportResponseWithError(t *testing.T) { + envs := config.EnvironmentVariables{} logger, _ := test.NewNullLogger() req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) @@ -359,10 +362,10 @@ func TestOPATransportRoundTrip(t *testing.T) { req.Context(), logrus.NewEntry(logger), req, - &RondConfig{ - ResponseFlow: ResponseFlow{PolicyName: "my_policy"}, + &openapi.RondConfig{ + ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, }, - PartialResultsEvaluators{"my_policy": {}}, + core.PartialResultsEvaluators{"my_policy": {}}, envs, } resp, err := transport.RoundTrip(req) diff --git a/opamiddleware.go b/opamiddleware.go deleted file mode 100644 index 95dc78b4..00000000 --- a/opamiddleware.go +++ /dev/null @@ -1,191 +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 main - -import ( - "context" - "errors" - "fmt" - "net/http" - "os" - "path/filepath" - "strings" - - "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/utils" - - "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" - "github.com/sirupsen/logrus" -) - -var ( - ErrRequestFailed = errors.New("request failed") - ErrFileLoadFailed = errors.New("file loading failed") -) - -type OPAModuleConfigKey struct{} -type RouterInfoKey struct{} - -type OPAModuleConfig struct { - Name string - Content string -} - -func OPAMiddleware(opaModuleConfig *OPAModuleConfig, openAPISpec *OpenAPISpec, envs *config.EnvironmentVariables, policyEvaluators PartialResultsEvaluators) mux.MiddlewareFunc { - OASrouter := openAPISpec.PrepareOASRouter() - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { - next.ServeHTTP(w, r) - return - } - - path := r.URL.EscapedPath() - if envs.Standalone { - path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) - } - - logger := glogger.Get(r.Context()) - - permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) - if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { - fields := logrus.Fields{} - if err != nil { - fields["error"] = logrus.Fields{"message": err.Error()} - } - logger.WithFields(fields).Info("Proxying call to OAS Path even with no permission") - next.ServeHTTP(w, r) - return - } - - if err != nil || permission.RequestFlow.PolicyName == "" { - errorMessage := "User is not allowed to request the API" - statusCode := http.StatusForbidden - fields := logrus.Fields{ - "originalRequestPath": utils.SanitizeString(r.URL.Path), - "method": utils.SanitizeString(r.Method), - "allowPermission": utils.SanitizeString(permission.RequestFlow.PolicyName), - } - technicalError := "" - if err != nil { - technicalError = err.Error() - fields["error"] = logrus.Fields{"message": err.Error()} - errorMessage = "The request doesn't match any known API" - } - if errors.Is(err, ErrNotFoundOASDefinition) { - statusCode = http.StatusNotFound - } - logger.WithFields(fields).Errorf(errorMessage) - failResponseWithCode(w, statusCode, technicalError, errorMessage) - return - } - - ctx := WithXPermission( - WithOPAModuleConfig( - WithPartialResultsEvaluators( - WithRouterInfo(logger, r.Context(), r), - policyEvaluators, - ), - opaModuleConfig, - ), - &permission, - ) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} - -func loadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { - var regoModulePath string - //#nosec G104 -- Produces a false positive - filepath.Walk(rootDirectory, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if regoModulePath != "" { - return nil - } - - if filepath.Ext(path) == ".rego" { - regoModulePath = path - } - return nil - }) - - if regoModulePath == "" { - return nil, fmt.Errorf("no rego module found in directory") - } - fileContent, err := readFile(regoModulePath) - if err != nil { - return nil, fmt.Errorf("failed rego file read: %s", err.Error()) - } - - return &OPAModuleConfig{ - Name: filepath.Base(regoModulePath), - Content: string(fileContent), - }, nil -} - -func WithOPAModuleConfig(requestContext context.Context, permission *OPAModuleConfig) context.Context { - return context.WithValue(requestContext, OPAModuleConfigKey{}, permission) -} - -// GetOPAModuleConfig can be used by a request handler to get OPAModuleConfig instance from its context. -func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error) { - permission, ok := requestContext.Value(OPAModuleConfigKey{}).(*OPAModuleConfig) - if !ok { - return nil, fmt.Errorf("no opa module config found in request context") - } - - return permission, nil -} - -type RouterInfo struct { - MatchedPath string - RequestedPath string - Method string -} - -func WithRouterInfo(logger *logrus.Entry, requestContext context.Context, req *http.Request) context.Context { - pathTemplate := getPathTemplateOrDefaultToEmptyString(logger, req) - return context.WithValue(requestContext, RouterInfoKey{}, RouterInfo{ - MatchedPath: utils.SanitizeString(pathTemplate), - RequestedPath: utils.SanitizeString(req.URL.Path), - Method: utils.SanitizeString(req.Method), - }) -} - -func getPathTemplateOrDefaultToEmptyString(logger *logrus.Entry, req *http.Request) string { - var pathTemplate string - route := mux.CurrentRoute(req) - if route != nil { - var err error - if pathTemplate, err = route.GetPathTemplate(); err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("path template is empty") - return "" - } - } - return pathTemplate -} - -func GetRouterInfo(requestContext context.Context) (RouterInfo, error) { - routerInfo, ok := requestContext.Value(RouterInfoKey{}).(RouterInfo) - if !ok { - return RouterInfo{}, fmt.Errorf("no router info found") - } - return routerInfo, nil -} diff --git a/opamiddleware_test.go b/opamiddleware_test.go deleted file mode 100644 index ba3dc023..00000000 --- a/opamiddleware_test.go +++ /dev/null @@ -1,501 +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 main - -import ( - "context" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/gorilla/mux" - "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/require" -) - -var envs = config.EnvironmentVariables{} - -var partialEvaluators = PartialResultsEvaluators{} - -func TestOPAMiddleware(t *testing.T) { - t.Run(`strict mode failure`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -todo { true }`, - } - var openAPISpec *OpenAPISpec - openAPISpecContent, _ := os.ReadFile("./mocks/simplifiedMock.json") - _ = json.Unmarshal(openAPISpecContent, &openAPISpec) - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - - t.Run(`missing oas paths`, func(t *testing.T) { - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Fail() - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/not-existing-path", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") - require.Equal(t, &types.RequestError{ - Message: "The request doesn't match any known API", - Error: "not found oas definition: GET /not-existing-path", - StatusCode: http.StatusNotFound, - }, getJSONResponseBody[types.RequestError](t, w)) - require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") - }) - - t.Run(`missing method`, func(t *testing.T) { - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Fail() - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodDelete, "http://example.com/users/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") - require.Equal(t, &types.RequestError{ - Message: "The request doesn't match any known API", - Error: "not found oas definition: DELETE /users/", - StatusCode: http.StatusNotFound, - }, getJSONResponseBody[types.RequestError](t, w)) - require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey), "Unexpected content type.") - }) - - t.Run(`missing permission`, func(t *testing.T) { - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Fail() - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "http://example.com/no-permission", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") - }) - }) - - t.Run(`documentation request`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -foobar { true }`, - } - - t.Run(`ok - path is known on oas with no permission declared`, func(t *testing.T) { - openAPISpec, err := loadOASFile("./mocks/documentationPathMock.json") - require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "http://example.com/documentation/json", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run(`ok - path is missing on oas and request is equal to serviceTargetOASPath`, func(t *testing.T) { - openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/json", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run(`ok - path is NOT known on oas but is proxied anyway`, func(t *testing.T) { - openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/custom/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/custom/json", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - }) - - t.Run(`injects opa instance with correct query`, func(t *testing.T) { - openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - require.NoError(t, err) - - t.Run(`rego package doesn't contain expected permission`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -todo { true }`, - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, permission, &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}}) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run(`rego package contains expected permission`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -foobar { true }`, - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}}, permission) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run(`rego package contains composed permission`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -very_very_composed_permission { true }`, - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &RondConfig{RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/composed/permission/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run("injects correct permission", func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -very_very_composed_permission_with_eval { true }`, - } - - envs := config.EnvironmentVariables{ - Standalone: false, - PathPrefixStandalone: "/eval", // default value - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &RondConfig{RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - }) -} - -func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { - openAPISpec, err := loadOASFile("./mocks/simplifiedMock.json") - require.Nil(t, err) - - envs := config.EnvironmentVariables{ - Standalone: true, - PathPrefixStandalone: "/eval", // default value - } - - t.Run("injects correct path removing prefix", func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies - very_very_composed_permission { true }`, - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &RondConfig{RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) - - t.Run("injects correct path removing only one prefix", func(t *testing.T) { - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -very_very_composed_permission_with_eval { true }`, - } - - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) - builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &RondConfig{RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) - w.WriteHeader(http.StatusOK) - })) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/eval/composed/permission/", nil) - builtHandler.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") - }) -} - -func TestGetHeaderFunction(t *testing.T) { - headerKeyMocked := "exampleKey" - headerValueMocked := "value" - env := config.EnvironmentVariables{} - - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies - todo { get_header("ExAmPlEkEy", input.headers) == "value" }`, - } - queryString := "todo" - - t.Run("if header key exists", func(t *testing.T) { - headers := http.Header{} - headers.Add(headerKeyMocked, headerValueMocked) - input := map[string]interface{}{ - "headers": headers, - } - inputBytes, _ := json.Marshal(input) - - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) - require.NoError(t, err, "Unexpected error during creation of opaEvaluator") - - results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) - require.NoError(t, err, "Unexpected error during rego validation") - require.True(t, results.Allowed(), "The input is not allowed by rego") - - partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) - require.NoError(t, err, "Unexpected error during rego validation") - - require.Len(t, partialResults.Queries, 1, "Rego policy allows illegal input") - }) - - t.Run("if header key not exists", func(t *testing.T) { - input := map[string]interface{}{ - "headers": http.Header{}, - } - inputBytes, _ := json.Marshal(input) - - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) - require.NoError(t, err, "Unexpected error during creation of opaEvaluator") - - results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) - require.NoError(t, err, "Unexpected error during rego validation") - require.True(t, !results.Allowed(), "Rego policy allows illegal input") - - partialResults, err := opaEvaluator.PolicyEvaluator.Partial(context.TODO()) - require.NoError(t, err, "Unexpected error during rego validation") - - require.Len(t, partialResults.Queries, 0, "Rego policy allows illegal input") - }) -} - -func TestGetOPAModuleConfig(t *testing.T) { - t.Run(`GetOPAModuleConfig fails because no key has been passed`, func(t *testing.T) { - ctx := context.Background() - env, err := GetOPAModuleConfig(ctx) - require.True(t, err != nil, "An error was expected.") - t.Logf("Expected error: %s - env: %+v", err.Error(), env) - }) - - t.Run(`GetOPAModuleConfig returns OPAEvaluator from context`, func(t *testing.T) { - ctx := context.WithValue(context.Background(), OPAModuleConfigKey{}, &OPAModuleConfig{}) - opaEval, err := GetOPAModuleConfig(ctx) - require.True(t, err == nil, "Unexpected error.") - require.True(t, opaEval != nil, "OPA Module config not found.") - }) -} - -func TestRouterInfoContext(t *testing.T) { - nullLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(nullLogger) - - t.Run("GetRouterInfo fails because no key has been set", func(t *testing.T) { - ctx := context.Background() - routerInfo, err := GetRouterInfo(ctx) - require.EqualError(t, err, "no router info found") - require.Empty(t, routerInfo) - }) - - t.Run("WithRouterInfo not inside mux router - empty matched path", func(t *testing.T) { - ctx := context.Background() - req := httptest.NewRequest("GET", "/hello", nil) - ctx = WithRouterInfo(logger, ctx, req) - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "", - RequestedPath: "/hello", - Method: "GET", - }, routerInfo) - }) - - t.Run("WithRouterInfo without router path - matched path is empty", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.NewRoute().HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "", - RequestedPath: "/hello", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) - - t.Run("correctly get router info", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.HandleFunc("/hello/{name}", func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "/hello/{name}", - RequestedPath: "/hello/my-username", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello/my-username", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) - - t.Run("correctly get router info with path prefix", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.PathPrefix("/hello/").HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "/hello/", - RequestedPath: "/hello/my-username", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello/my-username", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) -} - -func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { - t.Helper() - - responseBody, err := io.ReadAll(w.Result().Body) - require.NoError(t, err) - - return responseBody -} - -func getJSONResponseBody[T any](t *testing.T, w *httptest.ResponseRecorder) *T { - t.Helper() - - responseBody := getResponseBody(t, w) - out := new(T) - if err := json.Unmarshal(responseBody, out); err != nil { - require.Error(t, err, "fails to unmarshal") - } - return out -} diff --git a/openapi/opamiddleware.go b/openapi/opamiddleware.go new file mode 100644 index 00000000..9ca8d82e --- /dev/null +++ b/openapi/opamiddleware.go @@ -0,0 +1,64 @@ +// 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 openapi + +import ( + "context" + "fmt" + "net/http" + + "github.com/rond-authz/rond/internal/utils" + + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" +) + +type RouterInfoKey struct{} + +type RouterInfo struct { + MatchedPath string + RequestedPath string + Method string +} + +func WithRouterInfo(logger *logrus.Entry, requestContext context.Context, req *http.Request) context.Context { + pathTemplate := getPathTemplateOrDefaultToEmptyString(logger, req) + return context.WithValue(requestContext, RouterInfoKey{}, RouterInfo{ + MatchedPath: utils.SanitizeString(pathTemplate), + RequestedPath: utils.SanitizeString(req.URL.Path), + Method: utils.SanitizeString(req.Method), + }) +} + +func getPathTemplateOrDefaultToEmptyString(logger *logrus.Entry, req *http.Request) string { + var pathTemplate string + route := mux.CurrentRoute(req) + if route != nil { + var err error + if pathTemplate, err = route.GetPathTemplate(); err != nil { + logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("path template is empty") + return "" + } + } + return pathTemplate +} + +func GetRouterInfo(requestContext context.Context) (RouterInfo, error) { + routerInfo, ok := requestContext.Value(RouterInfoKey{}).(RouterInfo) + if !ok { + return RouterInfo{}, fmt.Errorf("no router info found") + } + return routerInfo, nil +} diff --git a/openapi/opamiddleware_test.go b/openapi/opamiddleware_test.go new file mode 100644 index 00000000..a8c877cd --- /dev/null +++ b/openapi/opamiddleware_test.go @@ -0,0 +1,140 @@ +// 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 openapi + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + "github.com/rond-authz/rond/internal/config" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +var envs = config.EnvironmentVariables{} + +func TestRouterInfoContext(t *testing.T) { + nullLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(nullLogger) + + t.Run("GetRouterInfo fails because no key has been set", func(t *testing.T) { + ctx := context.Background() + routerInfo, err := GetRouterInfo(ctx) + require.EqualError(t, err, "no router info found") + require.Empty(t, routerInfo) + }) + + t.Run("WithRouterInfo not inside mux router - empty matched path", func(t *testing.T) { + ctx := context.Background() + req := httptest.NewRequest("GET", "/hello", nil) + ctx = WithRouterInfo(logger, ctx, req) + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "", + RequestedPath: "/hello", + Method: "GET", + }, routerInfo) + }) + + t.Run("WithRouterInfo without router path - matched path is empty", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.NewRoute().HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "", + RequestedPath: "/hello", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) + + t.Run("correctly get router info", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.HandleFunc("/hello/{name}", func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/hello/{name}", + RequestedPath: "/hello/my-username", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello/my-username", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) + + t.Run("correctly get router info with path prefix", func(t *testing.T) { + ctx := context.Background() + router := mux.NewRouter() + + router.PathPrefix("/hello/").HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := WithRouterInfo(logger, ctx, req) + + routerInfo, err := GetRouterInfo(ctx) + require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/hello/", + RequestedPath: "/hello/my-username", + Method: "GET", + }, routerInfo) + + w.Write([]byte("ok")) + }) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/hello/my-username", nil) + router.ServeHTTP(w, req) + + require.Equal(t, 200, w.Result().StatusCode) + }) +} + +func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { + t.Helper() + + responseBody, err := io.ReadAll(w.Result().Body) + require.NoError(t, err) + + return responseBody +} diff --git a/openapi_utils.go b/openapi/openapi_utils.go similarity index 83% rename from openapi_utils.go rename to openapi/openapi_utils.go index 3bf4b63b..3e681d2a 100644 --- a/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package openapi import ( "context" @@ -22,20 +22,20 @@ import ( "io" "net/http" "net/http/httptest" - "net/url" - "os" + "regexp" "strconv" "strings" "time" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/uptrace/bunrouter" ) +const HTTPScheme = "http" + var AllHTTPMethod = "all" var OasSupportedHTTPMethods = []string{ @@ -46,6 +46,9 @@ var OasSupportedHTTPMethods = []string{ http.MethodDelete, http.MethodHead, } +var ( + ErrRequestFailed = errors.New("request failed") +) var ErrNotFoundOASDefinition = errors.New("not found oas definition") @@ -114,41 +117,6 @@ type OpenAPISpec struct { Paths OpenAPIPaths `json:"paths"` } -type Input struct { - Request InputRequest `json:"request"` - Response InputResponse `json:"response"` - ClientType string `json:"clientType,omitempty"` - User InputUser `json:"user"` -} -type InputRequest struct { - Body interface{} `json:"body,omitempty"` - Headers http.Header `json:"headers,omitempty"` - Query url.Values `json:"query,omitempty"` - PathParams map[string]string `json:"pathParams,omitempty"` - Method string `json:"method"` - Path string `json:"path"` -} - -type InputResponse struct { - Body interface{} `json:"body,omitempty"` -} - -type PermissionOnResourceKey string - -type PermissionsOnResourceMap map[PermissionOnResourceKey]bool - -func buildPermissionOnResourceKey(permission string, resourceType string, resourceId string) PermissionOnResourceKey { - return PermissionOnResourceKey(fmt.Sprintf("%s:%s:%s", permission, resourceType, resourceId)) -} - -type InputUser struct { - Properties map[string]interface{} `json:"properties,omitempty"` - Groups []string `json:"groups,omitempty"` - Bindings []types.Binding `json:"bindings,omitempty"` - Roles []types.Role `json:"roles,omitempty"` - ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` -} - func cleanWildcard(path string) string { if strings.HasSuffix(path, "*") { // is a wildcard parameter that matches everything and must always be at the end of the route @@ -193,7 +161,7 @@ func (oas *OpenAPISpec) PrepareOASRouter() *bunrouter.CompatRouter { routeMap := oas.createRoutesMap() for OASPath, OASContent := range oas.Paths { - OASPathCleaned := convertPathVariablesToColons(cleanWildcard(OASPath)) + OASPathCleaned := ConvertPathVariablesToColons(cleanWildcard(OASPath)) for method, methodContent := range OASContent { scopedMethod := strings.ToUpper(method) @@ -314,27 +282,18 @@ func fetchOpenAPI(url string) (*OpenAPISpec, error) { return deserializeSpec(bodyBytes, ErrRequestFailed) } -func readFile(path string) ([]byte, error) { - //#nosec G304 -- This is an expected behaviour - fileContentByte, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("%w: %s", ErrFileLoadFailed, err.Error()) - } - return fileContentByte, nil -} - -func loadOASFile(APIPermissionsFilePath string) (*OpenAPISpec, error) { - fileContentByte, err := readFile(APIPermissionsFilePath) +func LoadOASFile(APIPermissionsFilePath string) (*OpenAPISpec, error) { + fileContentByte, err := utils.ReadFile(APIPermissionsFilePath) if err != nil { return nil, err } - return deserializeSpec(fileContentByte, ErrFileLoadFailed) + return deserializeSpec(fileContentByte, utils.ErrFileLoadFailed) } -func loadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariables) (*OpenAPISpec, error) { +func LoadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariables) (*OpenAPISpec, error) { if env.APIPermissionsFilePath != "" { log.WithField("oasFilePath", env.APIPermissionsFilePath).Debug("Attempt to load OAS from file") - oas, err := loadOASFile(env.APIPermissionsFilePath) + oas, err := LoadOASFile(env.APIPermissionsFilePath) if err != nil { log.WithFields(logrus.Fields{ "APIPermissionsFilePath": env.APIPermissionsFilePath, @@ -382,3 +341,15 @@ func GetXPermission(requestContext context.Context) (*RondConfig, error) { return permission, nil } + +var matchColons = regexp.MustCompile(`\/:(\w+)`) + +func ConvertPathVariablesToBrackets(path string) string { + return matchColons.ReplaceAllString(path, "/{$1}") +} + +var matchBrackets = regexp.MustCompile(`\/{(\w+)}`) + +func ConvertPathVariablesToColons(path string) string { + return matchBrackets.ReplaceAllString(path, "/:$1") +} diff --git a/openapi_utils_test.go b/openapi/openapi_utils_test.go similarity index 95% rename from openapi_utils_test.go rename to openapi/openapi_utils_test.go index 931a2098..b2147748 100644 --- a/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package openapi import ( "context" @@ -133,7 +133,7 @@ func TestFetchOpenAPI(t *testing.T) { func TestLoadOASFile(t *testing.T) { t.Run("get oas config from file", func(t *testing.T) { - openAPIFile, err := loadOASFile("./mocks/pathsConfig.json") + openAPIFile, err := LoadOASFile("./mocks/pathsConfig.json") require.True(t, err == nil, "unexpected error") require.True(t, openAPIFile != nil, "unexpected nil result") require.Equal(t, OpenAPIPaths{ @@ -162,7 +162,7 @@ func TestLoadOASFile(t *testing.T) { }) t.Run("fail for invalid filePath", func(t *testing.T) { - _, err := loadOASFile("./notExistingFilePath.json") + _, err := LoadOASFile("./notExistingFilePath.json") t.Logf("Expected error occurred: %s", err.Error()) require.True(t, err != nil, "failed documentation file read") @@ -178,7 +178,7 @@ func TestLoadOAS(t *testing.T) { TargetServiceOASPath: "/documentation/json", APIPermissionsFilePath: "./mocks/pathsConfig.json", } - openApiSpec, err := loadOASFromFileOrNetwork(log, envs) + openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) require.True(t, err == nil, "unexpected error") require.True(t, openApiSpec != nil, "unexpected nil result") require.Equal(t, OpenAPIPaths{ @@ -218,7 +218,7 @@ func TestLoadOAS(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - openApiSpec, err := loadOASFromFileOrNetwork(log, envs) + openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) require.True(t, gock.IsDone(), "Mock has not been invoked") require.NoError(t, err, "unexpected error") require.NotNil(t, openApiSpec, "unexpected nil result") @@ -265,7 +265,7 @@ func TestLoadOAS(t *testing.T) { envs := config.EnvironmentVariables{ TargetServiceHost: "localhost:3000", } - _, err := loadOASFromFileOrNetwork(log, envs) + _, err := LoadOASFromFileOrNetwork(log, envs) t.Logf("Expected error occurred: %s", err.Error()) require.True(t, err != nil, fmt.Errorf("missing environment variables one of %s or %s is required", config.TargetServiceOASPathEnvKey, config.APIPermissionsFilePathEnvKey)) @@ -412,22 +412,6 @@ func TestGetXPermission(t *testing.T) { }) } -func TestGetPolicyEvaluators(t *testing.T) { - t.Run(`GetPolicyEvaluators fails because no key has been passed`, func(t *testing.T) { - ctx := context.Background() - env, err := GetPartialResultsEvaluators(ctx) - require.True(t, err != nil, "An error was expected.") - t.Logf("Expected error: %s - env: %+v", err.Error(), env) - }) - - t.Run(`GetPartialResultsEvaluators returns PartialResultsEvaluators from context`, func(t *testing.T) { - ctx := context.WithValue(context.Background(), PartialResultsEvaluatorConfigKey{}, PartialResultsEvaluators{}) - opaEval, err := GetPartialResultsEvaluators(ctx) - require.True(t, err == nil, "Unexpected error.") - require.True(t, opaEval != nil, "OPA Module config not found.") - }) -} - func TestAdaptOASSpec(t *testing.T) { testCases := []struct { name string @@ -683,3 +667,11 @@ func TestAdaptOASSpec(t *testing.T) { }) } } + +func prepareOASFromFile(t *testing.T, filePath string) *OpenAPISpec { + t.Helper() + + oas, err := LoadOASFile(filePath) + require.NoError(t, err) + return oas +} diff --git a/router.go b/router.go index 6a0eb50e..bc1f7de0 100644 --- a/router.go +++ b/router.go @@ -15,18 +15,22 @@ package main import ( + "errors" "fmt" "net/http" "path" - "regexp" "sort" "strings" swagger "github.com/davidebianchi/gswagger" + "github.com/mia-platform/glogger/v2" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" + "github.com/sirupsen/logrus" "github.com/gorilla/mux" ) @@ -103,7 +107,7 @@ func addStandaloneRoutes(router *swagger.Router) error { return nil } -func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVariables) { +func setupRoutes(router *mux.Router, oas *openapi.OpenAPISpec, env config.EnvironmentVariables) { var documentationPermission string documentationPathInOAS := oas.Paths[env.TargetServiceOASPath] if documentationPathInOAS != nil { @@ -120,8 +124,8 @@ func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVar for path, pathMethods := range oas.Paths { paths = append(paths, path) for method := range pathMethods { - if method == AllHTTPMethod { - methods[path] = OasSupportedHTTPMethods + if method == openapi.AllHTTPMethod { + methods[path] = openapi.OasSupportedHTTPMethods continue } if methods[path] == nil { @@ -143,17 +147,17 @@ func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVar } if strings.Contains(pathToRegister, "*") { pathWithoutAsterisk := strings.ReplaceAll(pathToRegister, "*", "") - router.PathPrefix(convertPathVariablesToBrackets(pathWithoutAsterisk)).HandlerFunc(rbacHandler).Methods(methods[path]...) + router.PathPrefix(openapi.ConvertPathVariablesToBrackets(pathWithoutAsterisk)).HandlerFunc(rbacHandler).Methods(methods[path]...) continue } if path == env.TargetServiceOASPath && documentationPermission == "" { - router.HandleFunc(convertPathVariablesToBrackets(pathToRegister), alwaysProxyHandler).Methods(http.MethodGet) + router.HandleFunc(openapi.ConvertPathVariablesToBrackets(pathToRegister), alwaysProxyHandler).Methods(http.MethodGet) continue } - router.HandleFunc(convertPathVariablesToBrackets(pathToRegister), rbacHandler).Methods(methods[path]...) + router.HandleFunc(openapi.ConvertPathVariablesToBrackets(pathToRegister), rbacHandler).Methods(methods[path]...) } if documentationPathInOAS == nil { - router.HandleFunc(convertPathVariablesToBrackets(env.TargetServiceOASPath), alwaysProxyHandler) + router.HandleFunc(openapi.ConvertPathVariablesToBrackets(env.TargetServiceOASPath), alwaysProxyHandler) } // FIXME: All the routes don't inserted above are anyway handled by rbacHandler. // Maybe the code above can be cleaned. @@ -166,14 +170,67 @@ func setupRoutes(router *mux.Router, oas *OpenAPISpec, env config.EnvironmentVar router.PathPrefix(fallbackRoute).HandlerFunc(rbacHandler) } -var matchColons = regexp.MustCompile(`\/:(\w+)`) +func OPAMiddleware(opaModuleConfig *core.OPAModuleConfig, openAPISpec *openapi.OpenAPISpec, envs *config.EnvironmentVariables, policyEvaluators core.PartialResultsEvaluators) mux.MiddlewareFunc { + OASrouter := openAPISpec.PrepareOASRouter() -func convertPathVariablesToBrackets(path string) string { - return matchColons.ReplaceAllString(path, "/{$1}") -} + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { + next.ServeHTTP(w, r) + return + } + + path := r.URL.EscapedPath() + if envs.Standalone { + path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) + } -var matchBrackets = regexp.MustCompile(`\/{(\w+)}`) + logger := glogger.Get(r.Context()) + + permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) + if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { + fields := logrus.Fields{} + if err != nil { + fields["error"] = logrus.Fields{"message": err.Error()} + } + logger.WithFields(fields).Info("Proxying call to OAS Path even with no permission") + next.ServeHTTP(w, r) + return + } + + if err != nil || permission.RequestFlow.PolicyName == "" { + errorMessage := "User is not allowed to request the API" + statusCode := http.StatusForbidden + fields := logrus.Fields{ + "originalRequestPath": utils.SanitizeString(r.URL.Path), + "method": utils.SanitizeString(r.Method), + "allowPermission": utils.SanitizeString(permission.RequestFlow.PolicyName), + } + technicalError := "" + if err != nil { + technicalError = err.Error() + fields["error"] = logrus.Fields{"message": err.Error()} + errorMessage = "The request doesn't match any known API" + } + if errors.Is(err, openapi.ErrNotFoundOASDefinition) { + statusCode = http.StatusNotFound + } + logger.WithFields(fields).Errorf(errorMessage) + failResponseWithCode(w, statusCode, technicalError, errorMessage) + return + } -func convertPathVariablesToColons(path string) string { - return matchBrackets.ReplaceAllString(path, "/:$1") + ctx := openapi.WithXPermission( + core.WithOPAModuleConfig( + core.WithPartialResultsEvaluators( + openapi.WithRouterInfo(logger, r.Context(), r), + policyEvaluators, + ), + opaModuleConfig, + ), + &permission, + ) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } } diff --git a/router_test.go b/router_test.go index f649ef5d..269dacef 100644 --- a/router_test.go +++ b/router_test.go @@ -16,16 +16,21 @@ package main import ( "context" + "encoding/json" "net/http" "net/http/httptest" "net/url" + "os" "sort" "testing" "github.com/mia-platform/glogger/v2" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" @@ -40,19 +45,19 @@ func TestSetupRoutes(t *testing.T) { } t.Run("expect to register route correctly", func(t *testing.T) { router := mux.NewRouter() - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/foo": PathVerbs{}, - "/bar": PathVerbs{}, - "/foo/bar": PathVerbs{}, - "/-/ready": PathVerbs{}, - "/-/healthz": PathVerbs{}, - "/-/check-up": PathVerbs{}, - "/-/metrics": PathVerbs{}, - "/-/rond/metrics": PathVerbs{}, - "/-/rbac-healthz": PathVerbs{}, - "/-/rbac-ready": PathVerbs{}, - "/-/rbac-check-up": PathVerbs{}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/foo": openapi.PathVerbs{}, + "/bar": openapi.PathVerbs{}, + "/foo/bar": openapi.PathVerbs{}, + "/-/ready": openapi.PathVerbs{}, + "/-/healthz": openapi.PathVerbs{}, + "/-/check-up": openapi.PathVerbs{}, + "/-/metrics": openapi.PathVerbs{}, + "/-/rond/metrics": openapi.PathVerbs{}, + "/-/rbac-healthz": openapi.PathVerbs{}, + "/-/rbac-ready": openapi.PathVerbs{}, + "/-/rbac-check-up": openapi.PathVerbs{}, }, } expectedPaths := []string{"/", "/-/check-up", "/-/healthz", "/-/metrics", "/-/ready", "/bar", "/documentation/json", "/foo", "/foo/bar"} @@ -76,16 +81,16 @@ func TestSetupRoutes(t *testing.T) { t.Run("expect to register nested route correctly", func(t *testing.T) { router := mux.NewRouter() - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/-/ready": PathVerbs{}, - "/-/healthz": PathVerbs{}, - "/-/check-up": PathVerbs{}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/-/ready": openapi.PathVerbs{}, + "/-/healthz": openapi.PathVerbs{}, + "/-/check-up": openapi.PathVerbs{}, // General route - "/foo/*": PathVerbs{}, - "/foo/bar/*": PathVerbs{}, - "/foo/bar/nested": PathVerbs{}, - "/foo/bar/:barId": PathVerbs{}, + "/foo/*": openapi.PathVerbs{}, + "/foo/bar/*": openapi.PathVerbs{}, + "/foo/bar/nested": openapi.PathVerbs{}, + "/foo/bar/:barId": openapi.PathVerbs{}, }, } expectedPaths := []string{"/", "/-/ready", "/-/healthz", "/-/check-up", "/foo/", "/foo/bar/", "/foo/bar/nested", "/foo/bar/{barId}", "/documentation/json"} @@ -115,13 +120,13 @@ func TestSetupRoutes(t *testing.T) { PathPrefixStandalone: "/validate", } router := mux.NewRouter() - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/documentation/json": PathVerbs{}, - "/foo/*": PathVerbs{}, - "/foo/bar/*": PathVerbs{}, - "/foo/bar/nested": PathVerbs{}, - "/foo/bar/:barId": PathVerbs{}, + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/documentation/json": openapi.PathVerbs{}, + "/foo/*": openapi.PathVerbs{}, + "/foo/bar/*": openapi.PathVerbs{}, + "/foo/bar/nested": openapi.PathVerbs{}, + "/foo/bar/:barId": openapi.PathVerbs{}, }, } expectedPaths := []string{"/validate/", "/validate/documentation/json", "/validate/foo/", "/validate/foo/bar/", "/validate/foo/bar/nested", "/validate/foo/bar/{barId}"} @@ -163,7 +168,7 @@ func TestConvertPathVariables(t *testing.T) { t.Run("convert correctly paths", func(t *testing.T) { for _, path := range listOfPaths { - convertedPath := convertPathVariablesToBrackets(path.Path) + convertedPath := openapi.ConvertPathVariablesToBrackets(path.Path) require.Equal(t, path.ConvertedPath, convertedPath, "Path not converted correctly.") } }) @@ -186,7 +191,7 @@ func TestConvertPathVariables2(t *testing.T) { t.Run("convert correctly paths", func(t *testing.T) { for _, path := range listOfPaths { - convertedPath := convertPathVariablesToColons(path.Path) + convertedPath := openapi.ConvertPathVariablesToColons(path.Path) require.Equal(t, path.ConvertedPath, convertedPath, "Path not converted correctly.") } }) @@ -197,25 +202,25 @@ func createContext( originalCtx context.Context, env config.EnvironmentVariables, mongoClient *mocks.MongoClientMock, - permission *RondConfig, - opaModuleConfig *OPAModuleConfig, - partialResultEvaluators PartialResultsEvaluators, + permission *openapi.RondConfig, + opaModuleConfig *core.OPAModuleConfig, + partialResultEvaluators core.PartialResultsEvaluators, ) context.Context { t.Helper() var partialContext context.Context partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) - partialContext = context.WithValue(partialContext, XPermissionKey{}, permission) - partialContext = context.WithValue(partialContext, OPAModuleConfigKey{}, opaModuleConfig) + partialContext = context.WithValue(partialContext, openapi.XPermissionKey{}, permission) + partialContext = context.WithValue(partialContext, core.OPAModuleConfigKey{}, opaModuleConfig) if mongoClient != nil { partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) } - partialContext = context.WithValue(partialContext, PartialResultsEvaluatorConfigKey{}, partialResultEvaluators) + partialContext = context.WithValue(partialContext, core.PartialResultsEvaluatorConfigKey{}, partialResultEvaluators) log, _ := test.NewNullLogger() partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(log)) - partialContext = context.WithValue(partialContext, RouterInfoKey{}, RouterInfo{ + partialContext = context.WithValue(partialContext, openapi.RouterInfoKey{}, openapi.RouterInfo{ MatchedPath: "/matched/path", RequestedPath: "/requested/path", Method: "GET", @@ -226,30 +231,31 @@ func createContext( return partialContext } -var mockOPAModule = &OPAModuleConfig{ +var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { true }`, } -var mockXPermission = &RondConfig{RequestFlow: RequestFlow{PolicyName: "todo"}} +var mockXPermission = &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} -var mockRondConfigWithQueryGen = &RondConfig{ - RequestFlow: RequestFlow{ +var mockRondConfigWithQueryGen = &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: openapi.QueryOptions{ HeaderName: "rowfilterquery", }, }, } func TestSetupRoutesIntegration(t *testing.T) { + envs := config.EnvironmentVariables{} oas := prepareOASFromFile(t, "./mocks/simplifiedMock.json") log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) - mockPartialEvaluators, _ := setupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) t.Run("invokes known API", func(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -325,12 +331,12 @@ func TestSetupRoutesIntegration(t *testing.T) { }) t.Run("blocks request on not allowed policy evaluation", func(t *testing.T) { - var mockOPAModule = &OPAModuleConfig{ + var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { false }`, } - mockPartialEvaluators, _ := setupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) router := mux.NewRouter() setupRoutes(router, oas, envs) @@ -358,10 +364,10 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("blocks request on policy evaluation error", func(t *testing.T) { - var mockOPAModule = &OPAModuleConfig{ + var mockOPAModule = &core.OPAModuleConfig{ Content: "FAILING POLICY!!!!", } - mockPartialEvaluators, _ := setupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) router := mux.NewRouter() setupRoutes(router, oas, envs) @@ -463,14 +469,305 @@ func TestSetupRoutesIntegration(t *testing.T) { }) } +func TestOPAMiddleware(t *testing.T) { + var envs = config.EnvironmentVariables{} + var partialEvaluators = core.PartialResultsEvaluators{} + + t.Run(`strict mode failure`, func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +todo { true }`, + } + var openAPISpec *openapi.OpenAPISpec + openAPISpecContent, _ := os.ReadFile("./mocks/simplifiedMock.json") + _ = json.Unmarshal(openAPISpecContent, &openAPISpec) + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + + t.Run(`missing oas paths`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/not-existing-path", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ + Message: "The request doesn't match any known API", + Error: "not found oas definition: GET /not-existing-path", + StatusCode: http.StatusNotFound, + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") + }) + + t.Run(`missing method`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodDelete, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ + Message: "The request doesn't match any known API", + Error: "not found oas definition: DELETE /users/", + StatusCode: http.StatusNotFound, + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") + }) + + t.Run(`missing permission`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "http://example.com/no-permission", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") + }) + }) + + t.Run(`documentation request`, func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +foobar { true }`, + } + + t.Run(`ok - path is known on oas with no permission declared`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("./mocks/documentationPathMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "http://example.com/documentation/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`ok - path is missing on oas and request is equal to serviceTargetOASPath`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("./mocks/simplifiedMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`ok - path is NOT known on oas but is proxied anyway`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("./mocks/simplifiedMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/custom/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/custom/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + }) + + t.Run(`injects opa instance with correct query`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("./mocks/simplifiedMock.json") + require.NoError(t, err) + + t.Run(`rego package doesn't contain expected permission`, func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +todo { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, permission, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`rego package contains expected permission`, func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +foobar { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`rego package contains composed permission`, func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run("injects correct permission", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission_with_eval { true }`, + } + + envs := config.EnvironmentVariables{ + Standalone: false, + PathPrefixStandalone: "/eval", // default value + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + }) +} + +func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("./mocks/simplifiedMock.json") + require.Nil(t, err) + + envs := config.EnvironmentVariables{ + Standalone: true, + PathPrefixStandalone: "/eval", // default value + } + var partialEvaluators = core.PartialResultsEvaluators{} + + t.Run("injects correct path removing prefix", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run("injects correct path removing only one prefix", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission_with_eval { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) +} + func TestRoutesToNotProxy(t *testing.T) { require.Equal(t, routesToNotProxy, []string{"/-/rbac-healthz", "/-/rbac-ready", "/-/rbac-check-up", "/-/rond/metrics"}) } -func prepareOASFromFile(t *testing.T, filePath string) *OpenAPISpec { +func prepareOASFromFile(t *testing.T, filePath string) *openapi.OpenAPISpec { t.Helper() - oas, err := loadOASFile(filePath) + oas, err := openapi.LoadOASFile(filePath) require.NoError(t, err) return oas } + +func getJSONResponseBody[T any](t *testing.T, w *httptest.ResponseRecorder) *T { + t.Helper() + + responseBody := getResponseBody(t, w) + out := new(T) + if err := json.Unmarshal(responseBody, out); err != nil { + require.Error(t, err, "fails to unmarshal") + } + return out +} diff --git a/statusroutes.go b/statusroutes.go index 1168c690..05539c63 100644 --- a/statusroutes.go +++ b/statusroutes.go @@ -20,6 +20,7 @@ import ( "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" + "github.com/rond-authz/rond/internal/utils" "github.com/sirupsen/logrus" ) @@ -31,7 +32,7 @@ type StatusResponse struct { } func handleStatusRoutes(w http.ResponseWriter, serviceName, serviceVersion string) (*StatusResponse, []byte) { - w.Header().Add(ContentTypeHeaderKey, JSONContentTypeHeader) + w.Header().Add(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) status := StatusResponse{ Status: "OK", Name: serviceName, diff --git a/statusroutes_test.go b/statusroutes_test.go index cf87fb24..1832f3d6 100644 --- a/statusroutes_test.go +++ b/statusroutes_test.go @@ -23,8 +23,10 @@ import ( "testing" "github.com/mia-platform/glogger/v2" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/openapi" "github.com/gorilla/mux" "github.com/sirupsen/logrus" @@ -88,20 +90,21 @@ func TestStatusRoutes(testCase *testing.T) { } func TestStatusRoutesIntegration(t *testing.T) { + envs := config.EnvironmentVariables{} log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) - opa := &OPAModuleConfig{ + opa := &core.OPAModuleConfig{ Name: "policies", Content: `package policies test_policy { true } `, } - oas := &OpenAPISpec{ - Paths: OpenAPIPaths{ - "/evalapi": PathVerbs{ - "get": VerbConfig{ - PermissionV1: &XPermission{ + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/evalapi": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV1: &openapi.XPermission{ AllowPermission: "test_policy", }, }, @@ -110,7 +113,7 @@ test_policy { true } } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := setupEvaluators(ctx, mongoClient, oas, opa, envs) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, envs) require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { diff --git a/utilities.go b/utilities.go index 1242bb5f..7d731181 100644 --- a/utilities.go +++ b/utilities.go @@ -17,24 +17,17 @@ package main import ( "encoding/json" "net/http" - "strings" + "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" ) -const ContentTypeHeaderKey = "content-type" -const JSONContentTypeHeader = "application/json" - -func hasApplicationJSONContentType(headers http.Header) bool { - return strings.HasPrefix(headers.Get(ContentTypeHeaderKey), JSONContentTypeHeader) -} - func failResponse(w http.ResponseWriter, technicalError, businessError string) { failResponseWithCode(w, http.StatusInternalServerError, technicalError, businessError) } func failResponseWithCode(w http.ResponseWriter, statusCode int, technicalError, businessError string) { - w.Header().Set(ContentTypeHeaderKey, JSONContentTypeHeader) + w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) w.WriteHeader(statusCode) content, err := json.Marshal(types.RequestError{ StatusCode: statusCode, @@ -48,12 +41,3 @@ func failResponseWithCode(w http.ResponseWriter, statusCode int, technicalError, //#nosec G104 -- Intended to avoid disruptive code changes w.Write(content) } - -func unmarshalHeader(headers http.Header, headerKey string, v interface{}) (bool, error) { - headerValueStringified := headers.Get(headerKey) - if headerValueStringified != "" { - err := json.Unmarshal([]byte(headerValueStringified), &v) - return err == nil, err - } - return false, nil -} diff --git a/utilities_test.go b/utilities_test.go index 029a785f..31531b26 100644 --- a/utilities_test.go +++ b/utilities_test.go @@ -21,59 +21,19 @@ import ( "net/http/httptest" "testing" + "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" "github.com/stretchr/testify/require" ) -func TestUnmarshalHeader(t *testing.T) { - userPropertiesHeaderKey := "miauserproperties" - mockedUserProperties := map[string]interface{}{ - "my": "other", - "key": []string{"is", "not"}, - } - mockedUserPropertiesStringified, err := json.Marshal(mockedUserProperties) - require.NoError(t, err) - - t.Run("header not exists", func(t *testing.T) { - headers := http.Header{} - var userProperties map[string]interface{} - - ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - - require.True(t, !ok, "Unmarshal not existing header") - require.NoError(t, err, "Unexpected error if doesn't exist header") - }) - - t.Run("header exists but the unmarshalling fails", func(t *testing.T) { - headers := http.Header{} - headers.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) - var userProperties string - - ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - require.False(t, ok, "Unexpected success during unmarshalling") - var unmarshalErr = &json.UnmarshalTypeError{} - require.ErrorAs(t, err, &unmarshalErr, "Unexpected error on unmarshalling") - }) - - t.Run("header exists and unmarshalling finishes correctly", func(t *testing.T) { - headers := http.Header{} - headers.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) - var userProperties map[string]interface{} - - ok, err := unmarshalHeader(headers, userPropertiesHeaderKey, &userProperties) - require.True(t, ok, "Unexpected failure") - require.NoError(t, err, "Unexpected error") - }) -} - func TestFailResponseWithCode(t *testing.T) { w := httptest.NewRecorder() failResponseWithCode(w, http.StatusInternalServerError, "The Error", "The Message") require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) - require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey)) + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey)) bodyBytes, err := io.ReadAll(w.Body) require.NoError(t, err) From 0a48a757a9c0a6d77283631bf4b343ae44abcb2a Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 15:12:53 +0100 Subject: [PATCH 030/172] fix: missing fn --- main_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main_test.go b/main_test.go index f4386a4b..ffd41c91 100644 --- a/main_test.go +++ b/main_test.go @@ -1882,3 +1882,12 @@ filter_policy { require.Contains(t, string(responseBody), "go_gc_duration_seconds") }) } + +func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { + t.Helper() + + responseBody, err := io.ReadAll(w.Result().Body) + require.NoError(t, err) + + return responseBody +} From 57591f68f3773130f7d7158dfbb51feb3d1a97bb Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 15:15:50 +0100 Subject: [PATCH 031/172] fix: mocks --- core/opaevaluator_test.go | 8 ++++---- openapi/openapi_utils_test.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index fc623472..1d342883 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -156,8 +156,8 @@ func TestCreatePolicyEvaluators(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) envs := config.EnvironmentVariables{ - APIPermissionsFilePath: "./mocks/simplifiedMock.json", - OPAModulesDirectory: "./mocks/rego-policies", + APIPermissionsFilePath: "../mocks/simplifiedMock.json", + OPAModulesDirectory: "../mocks/rego-policies", } openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) require.NoError(t, err, "unexpected error") @@ -175,8 +175,8 @@ func TestCreatePolicyEvaluators(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) envs := config.EnvironmentVariables{ - APIPermissionsFilePath: "./mocks/pathsConfigAllInclusive.json", - OPAModulesDirectory: "./mocks/rego-policies", + APIPermissionsFilePath: "../mocks/pathsConfigAllInclusive.json", + OPAModulesDirectory: "../mocks/rego-policies", } openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) require.NoError(t, err, "unexpected error") diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index b2147748..ff6ebb71 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -33,7 +33,7 @@ func TestFetchOpenAPI(t *testing.T) { gock.New("http://localhost:3000"). Get("/documentation/json"). Reply(200). - File("./mocks/simplifiedMock.json") + File("../mocks/simplifiedMock.json") url := "http://localhost:3000/documentation/json" @@ -133,7 +133,7 @@ func TestFetchOpenAPI(t *testing.T) { func TestLoadOASFile(t *testing.T) { t.Run("get oas config from file", func(t *testing.T) { - openAPIFile, err := LoadOASFile("./mocks/pathsConfig.json") + openAPIFile, err := LoadOASFile("../mocks/pathsConfig.json") require.True(t, err == nil, "unexpected error") require.True(t, openAPIFile != nil, "unexpected nil result") require.Equal(t, OpenAPIPaths{ @@ -176,7 +176,7 @@ func TestLoadOAS(t *testing.T) { envs := config.EnvironmentVariables{ TargetServiceHost: "localhost:3000", TargetServiceOASPath: "/documentation/json", - APIPermissionsFilePath: "./mocks/pathsConfig.json", + APIPermissionsFilePath: "../mocks/pathsConfig.json", } openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) require.True(t, err == nil, "unexpected error") @@ -216,7 +216,7 @@ func TestLoadOAS(t *testing.T) { gock.New("http://localhost:3000"). Get("/documentation/json"). Reply(200). - File("./mocks/simplifiedMock.json") + File("../mocks/simplifiedMock.json") openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) require.True(t, gock.IsDone(), "Mock has not been invoked") @@ -274,7 +274,7 @@ func TestLoadOAS(t *testing.T) { func TestFindPermission(t *testing.T) { t.Run("nested cases", func(t *testing.T) { - oas := prepareOASFromFile(t, "./mocks/nestedPathsConfig.json") + oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") OASRouter := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/not/existing/route", "GET") @@ -383,7 +383,7 @@ func TestFindPermission(t *testing.T) { }) t.Run("encoded cases", func(t *testing.T) { - oas := prepareOASFromFile(t, "./mocks/mockForEncodedTest.json") + oas := prepareOASFromFile(t, "../mocks/mockForEncodedTest.json") OASRouter := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") From ecb34fbe4950eec6ec0d2a923060d7d33947095b Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 15:16:36 +0100 Subject: [PATCH 032/172] rem: useless file --- core/core.go | 103 ------------------------------------------- core/opaevaluator.go | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 103 deletions(-) delete mode 100644 core/core.go diff --git a/core/core.go b/core/core.go deleted file mode 100644 index a519dac9..00000000 --- a/core/core.go +++ /dev/null @@ -1,103 +0,0 @@ -package core - -import ( - "context" - "fmt" - "net/http" - "net/url" - "os" - "path/filepath" - - "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/types" -) - -// type RondCore struct { -// partialEvaluators -// } -type OPAModuleConfigKey struct{} - -type OPAModuleConfig struct { - Name string - Content string -} - -func WithOPAModuleConfig(requestContext context.Context, permission *OPAModuleConfig) context.Context { - return context.WithValue(requestContext, OPAModuleConfigKey{}, permission) -} - -// GetOPAModuleConfig can be used by a request handler to get OPAModuleConfig instance from its context. -func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error) { - permission, ok := requestContext.Value(OPAModuleConfigKey{}).(*OPAModuleConfig) - if !ok { - return nil, fmt.Errorf("no opa module config found in request context") - } - - return permission, nil -} - -type Input struct { - Request InputRequest `json:"request"` - Response InputResponse `json:"response"` - ClientType string `json:"clientType,omitempty"` - User InputUser `json:"user"` -} -type InputRequest struct { - Body interface{} `json:"body,omitempty"` - Headers http.Header `json:"headers,omitempty"` - Query url.Values `json:"query,omitempty"` - PathParams map[string]string `json:"pathParams,omitempty"` - Method string `json:"method"` - Path string `json:"path"` -} - -type InputResponse struct { - Body interface{} `json:"body,omitempty"` -} - -type InputUser struct { - Properties map[string]interface{} `json:"properties,omitempty"` - Groups []string `json:"groups,omitempty"` - Bindings []types.Binding `json:"bindings,omitempty"` - Roles []types.Role `json:"roles,omitempty"` - ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` -} - -type PermissionOnResourceKey string - -type PermissionsOnResourceMap map[PermissionOnResourceKey]bool - -func buildPermissionOnResourceKey(permission string, resourceType string, resourceId string) PermissionOnResourceKey { - return PermissionOnResourceKey(fmt.Sprintf("%s:%s:%s", permission, resourceType, resourceId)) -} - -func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { - var regoModulePath string - //#nosec G104 -- Produces a false positive - filepath.Walk(rootDirectory, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if regoModulePath != "" { - return nil - } - - if filepath.Ext(path) == ".rego" { - regoModulePath = path - } - return nil - }) - - if regoModulePath == "" { - return nil, fmt.Errorf("no rego module found in directory") - } - fileContent, err := utils.ReadFile(regoModulePath) - if err != nil { - return nil, fmt.Errorf("failed rego file read: %s", err.Error()) - } - - return &OPAModuleConfig{ - Name: filepath.Base(regoModulePath), - Content: string(fileContent), - }, nil -} diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 1807b77e..4e5a90aa 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -21,7 +21,9 @@ import ( "fmt" "io" "net/http" + "net/url" "os" + "path/filepath" "strings" "time" @@ -473,3 +475,90 @@ func GetPartialResultsEvaluators(requestContext context.Context) (PartialResults return evaluators, nil } + +type OPAModuleConfigKey struct{} + +type OPAModuleConfig struct { + Name string + Content string +} + +func WithOPAModuleConfig(requestContext context.Context, permission *OPAModuleConfig) context.Context { + return context.WithValue(requestContext, OPAModuleConfigKey{}, permission) +} + +// GetOPAModuleConfig can be used by a request handler to get OPAModuleConfig instance from its context. +func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error) { + permission, ok := requestContext.Value(OPAModuleConfigKey{}).(*OPAModuleConfig) + if !ok { + return nil, fmt.Errorf("no opa module config found in request context") + } + + return permission, nil +} + +type Input struct { + Request InputRequest `json:"request"` + Response InputResponse `json:"response"` + ClientType string `json:"clientType,omitempty"` + User InputUser `json:"user"` +} +type InputRequest struct { + Body interface{} `json:"body,omitempty"` + Headers http.Header `json:"headers,omitempty"` + Query url.Values `json:"query,omitempty"` + PathParams map[string]string `json:"pathParams,omitempty"` + Method string `json:"method"` + Path string `json:"path"` +} + +type InputResponse struct { + Body interface{} `json:"body,omitempty"` +} + +type InputUser struct { + Properties map[string]interface{} `json:"properties,omitempty"` + Groups []string `json:"groups,omitempty"` + Bindings []types.Binding `json:"bindings,omitempty"` + Roles []types.Role `json:"roles,omitempty"` + ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` +} + +type PermissionOnResourceKey string + +type PermissionsOnResourceMap map[PermissionOnResourceKey]bool + +func buildPermissionOnResourceKey(permission string, resourceType string, resourceId string) PermissionOnResourceKey { + return PermissionOnResourceKey(fmt.Sprintf("%s:%s:%s", permission, resourceType, resourceId)) +} + +func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { + var regoModulePath string + //#nosec G104 -- Produces a false positive + filepath.Walk(rootDirectory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if regoModulePath != "" { + return nil + } + + if filepath.Ext(path) == ".rego" { + regoModulePath = path + } + return nil + }) + + if regoModulePath == "" { + return nil, fmt.Errorf("no rego module found in directory") + } + fileContent, err := utils.ReadFile(regoModulePath) + if err != nil { + return nil, fmt.Errorf("failed rego file read: %s", err.Error()) + } + + return &OPAModuleConfig{ + Name: filepath.Base(regoModulePath), + Content: string(fileContent), + }, nil +} From 5510ddca55a265ac97ff139f2e484d75e9921e68 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 15:32:54 +0100 Subject: [PATCH 033/172] fix: benchmark missing context info --- .gitignore | 3 ++- handler_test.go | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 7b9b3a7a..3355b469 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ *.env *.log* rond -coverage.out \ No newline at end of file +coverage.out +output.txt diff --git a/handler_test.go b/handler_test.go index 3e5b341d..533553ed 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1969,8 +1969,10 @@ func BenchmarkEvaluateRequest(b *testing.B) { UserGroupsHeader: "miausergroups", UserIdHeader: "miauserid", } - nilLogger, _ := test.NewNullLogger() + // nilLogger, _ := test.NewNullLogger() + nilLogger := logrus.New() + logger := logrus.NewEntry(nilLogger) b.ResetTimer() for n := 0; n < b.N; n++ { @@ -1978,17 +1980,24 @@ func BenchmarkEvaluateRequest(b *testing.B) { originalRequest := httptest.NewRequest(http.MethodGet, "/projects/project123", nil) req := originalRequest.WithContext( glogger.WithLogger( - context.WithValue( + metrics.WithValue( context.WithValue( - openapi.WithXPermission( - core.WithOPAModuleConfig(originalRequest.Context(), moduleConfig), - permission, + openapi.WithRouterInfo( + logger, + context.WithValue( + openapi.WithXPermission( + core.WithOPAModuleConfig(originalRequest.Context(), moduleConfig), + permission, + ), + types.MongoClientContextKey{}, testmongoMock, + ), + httptest.NewRequest(http.MethodGet, "/", nil), ), - types.MongoClientContextKey{}, testmongoMock, + config.EnvKey{}, envs, ), - config.EnvKey{}, envs, + metrics.SetupMetrics(""), ), - logrus.NewEntry(nilLogger), + logger, ), ) req.Header.Set("miausergroups", "area_rocket") From 28d7dcfe4e85898af88437b0c797aba1feaa9731 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:17:11 +0100 Subject: [PATCH 034/172] rename file --- openapi/{opamiddleware.go => routerinfo_middleware.go} | 0 openapi/{opamiddleware_test.go => routerinfo_middleware_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename openapi/{opamiddleware.go => routerinfo_middleware.go} (100%) rename openapi/{opamiddleware_test.go => routerinfo_middleware_test.go} (100%) diff --git a/openapi/opamiddleware.go b/openapi/routerinfo_middleware.go similarity index 100% rename from openapi/opamiddleware.go rename to openapi/routerinfo_middleware.go diff --git a/openapi/opamiddleware_test.go b/openapi/routerinfo_middleware_test.go similarity index 100% rename from openapi/opamiddleware_test.go rename to openapi/routerinfo_middleware_test.go From ec65218652756cf06b63d6d14636d1e41d67e551 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:36:07 +0100 Subject: [PATCH 035/172] refactor: moved opamiddleware in core package --- core/opamiddleware.go | 86 +++++++ core/opamiddleware_test.go | 329 ++++++++++++++++++++++++++ handler.go | 20 +- internal/utils/http.go | 22 ++ internal/utils/http_test.go | 25 ++ main.go | 2 +- openapi/routerinfo_middleware_test.go | 10 - router.go | 69 ------ standalone_apis.go | 36 +-- utilities.go | 43 ---- utilities_test.go | 50 ---- 11 files changed, 491 insertions(+), 201 deletions(-) create mode 100644 core/opamiddleware.go create mode 100644 core/opamiddleware_test.go delete mode 100644 utilities.go delete mode 100644 utilities_test.go diff --git a/core/opamiddleware.go b/core/opamiddleware.go new file mode 100644 index 00000000..7de4b2e9 --- /dev/null +++ b/core/opamiddleware.go @@ -0,0 +1,86 @@ +package core + +import ( + "errors" + "net/http" + "strings" + + "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" + + "github.com/gorilla/mux" + "github.com/mia-platform/glogger/v2" + "github.com/sirupsen/logrus" +) + +func OPAMiddleware( + opaModuleConfig *OPAModuleConfig, + openAPISpec *openapi.OpenAPISpec, + envs *config.EnvironmentVariables, + policyEvaluators PartialResultsEvaluators, + routesToNotProxy []string, +) mux.MiddlewareFunc { + OASrouter := openAPISpec.PrepareOASRouter() + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { + next.ServeHTTP(w, r) + return + } + + path := r.URL.EscapedPath() + if envs.Standalone { + path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) + } + + logger := glogger.Get(r.Context()) + + permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) + if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { + fields := logrus.Fields{} + if err != nil { + fields["error"] = logrus.Fields{"message": err.Error()} + } + logger.WithFields(fields).Info("Proxying call to OAS Path even with no permission") + next.ServeHTTP(w, r) + return + } + + if err != nil || permission.RequestFlow.PolicyName == "" { + errorMessage := "User is not allowed to request the API" + statusCode := http.StatusForbidden + fields := logrus.Fields{ + "originalRequestPath": utils.SanitizeString(r.URL.Path), + "method": utils.SanitizeString(r.Method), + "allowPermission": utils.SanitizeString(permission.RequestFlow.PolicyName), + } + technicalError := "" + if err != nil { + technicalError = err.Error() + fields["error"] = logrus.Fields{"message": err.Error()} + errorMessage = "The request doesn't match any known API" + } + if errors.Is(err, openapi.ErrNotFoundOASDefinition) { + statusCode = http.StatusNotFound + } + logger.WithFields(fields).Errorf(errorMessage) + utils.FailResponseWithCode(w, statusCode, technicalError, errorMessage) + return + } + + ctx := openapi.WithXPermission( + WithOPAModuleConfig( + WithPartialResultsEvaluators( + openapi.WithRouterInfo(logger, r.Context(), r), + policyEvaluators, + ), + opaModuleConfig, + ), + &permission, + ) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} diff --git a/core/opamiddleware_test.go b/core/opamiddleware_test.go new file mode 100644 index 00000000..93dee484 --- /dev/null +++ b/core/opamiddleware_test.go @@ -0,0 +1,329 @@ +// 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 core + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" + + "github.com/stretchr/testify/require" +) + +func TestOPAMiddleware(t *testing.T) { + var envs = config.EnvironmentVariables{} + var partialEvaluators = PartialResultsEvaluators{} + routesNotToProxy := make([]string, 0) + + t.Run(`strict mode failure`, func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +todo { true }`, + } + var openAPISpec *openapi.OpenAPISpec + openAPISpecContent, err := os.ReadFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + err = json.Unmarshal(openAPISpecContent, &openAPISpec) + require.NoError(t, err) + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + + t.Run(`missing oas paths`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/not-existing-path", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ + Message: "The request doesn't match any known API", + Error: "not found oas definition: GET /not-existing-path", + StatusCode: http.StatusNotFound, + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") + }) + + t.Run(`missing method`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodDelete, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusNotFound, w.Result().StatusCode, "Unexpected status code.") + require.Equal(t, &types.RequestError{ + Message: "The request doesn't match any known API", + Error: "not found oas definition: DELETE /users/", + StatusCode: http.StatusNotFound, + }, getJSONResponseBody[types.RequestError](t, w)) + require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") + }) + + t.Run(`missing permission`, func(t *testing.T) { + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fail() + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "http://example.com/no-permission", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") + }) + }) + + t.Run(`documentation request`, func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +foobar { true }`, + } + + t.Run(`ok - path is known on oas with no permission declared`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("../mocks/documentationPathMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "http://example.com/documentation/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`ok - path is missing on oas and request is equal to serviceTargetOASPath`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`ok - path is NOT known on oas but is proxied anyway`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + var envs = config.EnvironmentVariables{ + TargetServiceOASPath: "/documentation/custom/json", + } + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/documentation/custom/json", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + }) + + t.Run(`injects opa instance with correct query`, func(t *testing.T) { + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + + t.Run(`rego package doesn't contain expected permission`, func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +todo { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, permission, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`rego package contains expected permission`, func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +foobar { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/users/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run(`rego package contains composed permission`, func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run("injects correct permission", func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission_with_eval { true }`, + } + + envs := config.EnvironmentVariables{ + Standalone: false, + PathPrefixStandalone: "/eval", // default value + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + }) +} + +func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { + var partialEvaluators = PartialResultsEvaluators{} + var routesNotToProxy = []string{} + + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.Nil(t, err) + + envs := config.EnvironmentVariables{ + Standalone: true, + PathPrefixStandalone: "/eval", // default value + } + + t.Run("injects correct path removing prefix", func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) + + t.Run("injects correct path removing only one prefix", func(t *testing.T) { + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies +very_very_composed_permission_with_eval { true }`, + } + + middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + permission, err := openapi.GetXPermission(r.Context()) + require.True(t, err == nil, "Unexpected error") + require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + w.WriteHeader(http.StatusOK) + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/eval/eval/composed/permission/", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) +} + +func getJSONResponseBody[T any](t *testing.T, w *httptest.ResponseRecorder) *T { + t.Helper() + + responseBody, err := io.ReadAll(w.Result().Body) + require.NoError(t, err) + + out := new(T) + if err := json.Unmarshal(responseBody, out); err != nil { + require.Error(t, err, "fails to unmarshal") + } + return out +} diff --git a/handler.go b/handler.go index a2c18d34..2d2e8094 100644 --- a/handler.go +++ b/handler.go @@ -69,20 +69,20 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { env, err := config.GetEnv(requestContext) if err != nil { logger.WithError(err).Error("no env found in context") - failResponse(w, "No environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "No environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) return } permission, err := openapi.GetXPermission(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no policy permission found in context") - failResponse(w, "no policy permission found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no policy permission found in context", GENERIC_BUSINESS_ERROR_MESSAGE) return } partialResultEvaluators, err := core.GetPartialResultsEvaluators(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no partialResult evaluators found in context") - failResponse(w, "no partialResult evaluators found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no partialResult evaluators found in context", GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -105,14 +105,14 @@ func EvaluateRequest( userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, env) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed user bindings and roles retrieving") - failResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", GENERIC_BUSINESS_ERROR_MESSAGE) return err } input, err := core.CreateRegoQueryInput(req, env, permission.Options.EnableResourcePermissionsMapOptimization, userInfo, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed rego query input creation") - failResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", GENERIC_BUSINESS_ERROR_MESSAGE) return err } @@ -121,14 +121,14 @@ func EvaluateRequest( evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, input, env) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot find policy evaluator") - failResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", GENERIC_BUSINESS_ERROR_MESSAGE) return err } } else { evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, env, permission.RequestFlow.PolicyName, input, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot create evaluator") - failResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", NO_PERMISSIONS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", NO_PERMISSIONS_ERROR_MESSAGE) return err } } @@ -149,7 +149,7 @@ func EvaluateRequest( "policyName": permission.RequestFlow.PolicyName, "message": err.Error(), }).Error("RBAC policy evaluation failed") - failResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", NO_PERMISSIONS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", NO_PERMISSIONS_ERROR_MESSAGE) return err } var queryToProxy = []byte{} @@ -157,7 +157,7 @@ func EvaluateRequest( queryToProxy, err = json.Marshal(query) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("Error while marshaling row filter query") - failResponseWithCode(w, http.StatusForbidden, "Error while marshaling row filter query", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "Error while marshaling row filter query", GENERIC_BUSINESS_ERROR_MESSAGE) return err } } @@ -216,7 +216,7 @@ func alwaysProxyHandler(w http.ResponseWriter, req *http.Request) { env, err := config.GetEnv(requestContext) if err != nil { glogger.Get(requestContext).WithError(err).Error("no env found in context") - failResponse(w, "no environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) return } ReverseProxyOrResponse(logger, env, w, req, nil, nil) diff --git a/internal/utils/http.go b/internal/utils/http.go index f2598bbf..2e904e0e 100644 --- a/internal/utils/http.go +++ b/internal/utils/http.go @@ -18,6 +18,8 @@ import ( "encoding/json" "net/http" "strings" + + "github.com/rond-authz/rond/internal/types" ) const ContentTypeHeaderKey = "content-type" @@ -35,3 +37,23 @@ func UnmarshalHeader(headers http.Header, headerKey string, v interface{}) (bool func HasApplicationJSONContentType(headers http.Header) bool { return strings.HasPrefix(headers.Get(ContentTypeHeaderKey), JSONContentTypeHeader) } + +func FailResponse(w http.ResponseWriter, technicalError, businessError string) { + FailResponseWithCode(w, http.StatusInternalServerError, technicalError, businessError) +} + +func FailResponseWithCode(w http.ResponseWriter, statusCode int, technicalError, businessError string) { + w.Header().Set(ContentTypeHeaderKey, JSONContentTypeHeader) + w.WriteHeader(statusCode) + content, err := json.Marshal(types.RequestError{ + StatusCode: statusCode, + Error: technicalError, + Message: businessError, + }) + if err != nil { + return + } + + //#nosec G104 -- Intended to avoid disruptive code changes + w.Write(content) +} diff --git a/internal/utils/http_test.go b/internal/utils/http_test.go index e2470b67..08362ead 100644 --- a/internal/utils/http_test.go +++ b/internal/utils/http_test.go @@ -2,9 +2,12 @@ package utils import ( "encoding/json" + "io" "net/http" + "net/http/httptest" "testing" + "github.com/rond-authz/rond/internal/types" "github.com/stretchr/testify/require" ) @@ -48,3 +51,25 @@ func TestUnmarshalHeader(t *testing.T) { require.NoError(t, err, "Unexpected error") }) } + +func TestFailResponseWithCode(t *testing.T) { + w := httptest.NewRecorder() + + FailResponseWithCode(w, http.StatusInternalServerError, "The Error", "The Message") + require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) + + require.Equal(t, JSONContentTypeHeader, w.Result().Header.Get(ContentTypeHeaderKey)) + + bodyBytes, err := io.ReadAll(w.Body) + require.NoError(t, err) + + var response types.RequestError + err = json.Unmarshal(bodyBytes, &response) + require.NoError(t, err) + + require.Equal(t, types.RequestError{ + StatusCode: http.StatusInternalServerError, + Error: "The Error", + Message: "The Message", + }, response) +} diff --git a/main.go b/main.go index 5c4deeb7..f0b90740 100644 --- a/main.go +++ b/main.go @@ -201,7 +201,7 @@ func setupRouter( } } - evalRouter.Use(OPAMiddleware(opaModuleConfig, oas, &env, policiesEvaluators)) + evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, &env, policiesEvaluators, routesToNotProxy)) if mongoClient != nil { evalRouter.Use(mongoclient.MongoClientInjectorMiddleware(mongoClient)) diff --git a/openapi/routerinfo_middleware_test.go b/openapi/routerinfo_middleware_test.go index a8c877cd..2418d4a4 100644 --- a/openapi/routerinfo_middleware_test.go +++ b/openapi/routerinfo_middleware_test.go @@ -16,7 +16,6 @@ package openapi import ( "context" - "io" "net/http" "net/http/httptest" "testing" @@ -129,12 +128,3 @@ func TestRouterInfoContext(t *testing.T) { require.Equal(t, 200, w.Result().StatusCode) }) } - -func getResponseBody(t *testing.T, w *httptest.ResponseRecorder) []byte { - t.Helper() - - responseBody, err := io.ReadAll(w.Result().Body) - require.NoError(t, err) - - return responseBody -} diff --git a/router.go b/router.go index d2021f4e..7a51c33a 100644 --- a/router.go +++ b/router.go @@ -15,7 +15,6 @@ package main import ( - "errors" "fmt" "net/http" "path" @@ -23,14 +22,11 @@ import ( "strings" swagger "github.com/davidebianchi/gswagger" - "github.com/mia-platform/glogger/v2" - "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" "github.com/gorilla/mux" ) @@ -153,68 +149,3 @@ func setupRoutes(router *mux.Router, oas *openapi.OpenAPISpec, env config.Enviro } router.PathPrefix(fallbackRoute).HandlerFunc(rbacHandler) } - -func OPAMiddleware(opaModuleConfig *core.OPAModuleConfig, openAPISpec *openapi.OpenAPISpec, envs *config.EnvironmentVariables, policyEvaluators core.PartialResultsEvaluators) mux.MiddlewareFunc { - OASrouter := openAPISpec.PrepareOASRouter() - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { - next.ServeHTTP(w, r) - return - } - - path := r.URL.EscapedPath() - if envs.Standalone { - path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) - } - - logger := glogger.Get(r.Context()) - - permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) - if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { - fields := logrus.Fields{} - if err != nil { - fields["error"] = logrus.Fields{"message": err.Error()} - } - logger.WithFields(fields).Info("Proxying call to OAS Path even with no permission") - next.ServeHTTP(w, r) - return - } - - if err != nil || permission.RequestFlow.PolicyName == "" { - errorMessage := "User is not allowed to request the API" - statusCode := http.StatusForbidden - fields := logrus.Fields{ - "originalRequestPath": utils.SanitizeString(r.URL.Path), - "method": utils.SanitizeString(r.Method), - "allowPermission": utils.SanitizeString(permission.RequestFlow.PolicyName), - } - technicalError := "" - if err != nil { - technicalError = err.Error() - fields["error"] = logrus.Fields{"message": err.Error()} - errorMessage = "The request doesn't match any known API" - } - if errors.Is(err, openapi.ErrNotFoundOASDefinition) { - statusCode = http.StatusNotFound - } - logger.WithFields(fields).Errorf(errorMessage) - failResponseWithCode(w, statusCode, technicalError, errorMessage) - return - } - - ctx := openapi.WithXPermission( - core.WithOPAModuleConfig( - core.WithPartialResultsEvaluators( - openapi.WithRouterInfo(logger, r.Context(), r), - policyEvaluators, - ), - opaModuleConfig, - ), - &permission, - ) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} diff --git a/standalone_apis.go b/standalone_apis.go index 5c49c13b..a6245603 100644 --- a/standalone_apis.go +++ b/standalone_apis.go @@ -47,23 +47,23 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { logger := glogger.Get(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } reqBody := RevokeRequestBody{} if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } resourceType := mux.Vars(r)["resourceType"] if resourceType != "" && len(reqBody.ResourceIDs) == 0 { - failResponseWithCode(w, http.StatusBadRequest, "empty resources list", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "empty resources list", GENERIC_BUSINESS_ERROR_MESSAGE) return } if len(reqBody.Subjects) == 0 && len(reqBody.Groups) == 0 { - failResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists", GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -72,20 +72,20 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { client, err := crudclient.New(env.BindingsCrudServiceURL) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud setup") - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } query, err := buildQuery(resourceType, reqBody.ResourceIDs, reqBody.Subjects, reqBody.Groups) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed find query crud setup") - failResponseWithCode(w, http.StatusInternalServerError, "failed find query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed find query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) return } if err := client.Get(r.Context(), fmt.Sprintf("_q=%s&_l=%d", string(query), BINDINGS_MAX_PAGE_SIZE), &bindings); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - failResponseWithCode(w, http.StatusInternalServerError, "failed crud request for finding bindings", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for finding bindings", GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -98,7 +98,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { query, err := buildQueryForBindingsToDelete(bindingsToDelete) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed delete query crud setup") - failResponseWithCode(w, http.StatusInternalServerError, "failed delete query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed delete query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -109,7 +109,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { if err := client.Delete(r.Context(), fmt.Sprintf("_q=%s", string(query)), &deleteCrudResponse); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - failResponseWithCode(w, http.StatusInternalServerError, "failed crud request for deleting unused bindings", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for deleting unused bindings", GENERIC_BUSINESS_ERROR_MESSAGE) return } logger.WithField("deletedBindings", deleteCrudResponse).Debug("binding deletion finished") @@ -120,7 +120,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { if err := client.PatchBulk(r.Context(), body, &patchCrudResponse); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - failResponseWithCode( + utils.FailResponseWithCode( w, http.StatusInternalServerError, fmt.Sprintf("failed crud request to modify existing bindings. removed bindings: %d", deleteCrudResponse), @@ -138,7 +138,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { responseBytes, err := json.Marshal(response) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed response body") - failResponseWithCode( + utils.FailResponseWithCode( w, http.StatusInternalServerError, fmt.Sprintf("failed response body creation. removed bindings: %d, modified bindings: %d", deleteCrudResponse, patchCrudResponse), @@ -165,31 +165,31 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { logger := glogger.Get(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } reqBody := GrantRequestBody{} if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } resourceType := mux.Vars(r)["resourceType"] if resourceType != "" && reqBody.ResourceID == "" { - failResponseWithCode(w, http.StatusBadRequest, "missing resource id", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "missing resource id", GENERIC_BUSINESS_ERROR_MESSAGE) return } if len(reqBody.Groups) == 0 && len(reqBody.Permissions) == 0 && len(reqBody.Subjects) == 0 && len(reqBody.Roles) == 0 { - failResponseWithCode(w, http.StatusBadRequest, "missing body fields, one of groups, permissions, subjects or roles is required", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "missing body fields, one of groups, permissions, subjects or roles is required", GENERIC_BUSINESS_ERROR_MESSAGE) return } client, err := crudclient.New(env.BindingsCrudServiceURL) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud setup") - failResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -210,7 +210,7 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { var bindingIDCreated types.BindingCreateResponse if err := client.Post(r.Context(), &bindingToCreate, &bindingIDCreated); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - failResponseWithCode(w, http.StatusInternalServerError, "failed crud request for creating bindings", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for creating bindings", GENERIC_BUSINESS_ERROR_MESSAGE) return } logger.WithFields(logrus.Fields{ @@ -225,7 +225,7 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { responseBytes, err := json.Marshal(response) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed response body") - failResponseWithCode( + utils.FailResponseWithCode( w, http.StatusInternalServerError, "failed response body creation", diff --git a/utilities.go b/utilities.go deleted file mode 100644 index 7d731181..00000000 --- a/utilities.go +++ /dev/null @@ -1,43 +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 main - -import ( - "encoding/json" - "net/http" - - "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/types" -) - -func failResponse(w http.ResponseWriter, technicalError, businessError string) { - failResponseWithCode(w, http.StatusInternalServerError, technicalError, businessError) -} - -func failResponseWithCode(w http.ResponseWriter, statusCode int, technicalError, businessError string) { - w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) - w.WriteHeader(statusCode) - content, err := json.Marshal(types.RequestError{ - StatusCode: statusCode, - Error: technicalError, - Message: businessError, - }) - if err != nil { - return - } - - //#nosec G104 -- Intended to avoid disruptive code changes - w.Write(content) -} diff --git a/utilities_test.go b/utilities_test.go deleted file mode 100644 index 31531b26..00000000 --- a/utilities_test.go +++ /dev/null @@ -1,50 +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 main - -import ( - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/types" - - "github.com/stretchr/testify/require" -) - -func TestFailResponseWithCode(t *testing.T) { - w := httptest.NewRecorder() - - failResponseWithCode(w, http.StatusInternalServerError, "The Error", "The Message") - require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) - - require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey)) - - bodyBytes, err := io.ReadAll(w.Body) - require.NoError(t, err) - - var response types.RequestError - err = json.Unmarshal(bodyBytes, &response) - require.NoError(t, err) - - require.Equal(t, types.RequestError{ - StatusCode: http.StatusInternalServerError, - Error: "The Error", - Message: "The Message", - }, response) -} From 904da863c864ec79ff012f32e863d3357d0223cf Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:44:51 +0100 Subject: [PATCH 036/172] moved opa transport into core --- opa_transport.go => core/opa_transport.go | 31 ++++++++++++---- .../opa_transport_test.go | 10 +++--- handler.go | 26 +++++++------- handler_test.go | 4 +-- internal/utils/general.go | 3 ++ standalone_apis.go | 36 +++++++++---------- 6 files changed, 65 insertions(+), 45 deletions(-) rename opa_transport.go => core/opa_transport.go (85%) rename opa_transport_test.go => core/opa_transport_test.go (98%) diff --git a/opa_transport.go b/core/opa_transport.go similarity index 85% rename from opa_transport.go rename to core/opa_transport.go index d9700ac1..320bb6a1 100644 --- a/opa_transport.go +++ b/core/opa_transport.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package core import ( "bytes" @@ -23,7 +23,6 @@ import ( "net/http" "strconv" - "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" @@ -40,10 +39,30 @@ type OPATransport struct { logger *logrus.Entry request *http.Request permission *openapi.RondConfig - partialResultsEvaluators core.PartialResultsEvaluators + partialResultsEvaluators PartialResultsEvaluators env config.EnvironmentVariables } +func NewOPATransport( + defaultTransport http.RoundTripper, + context context.Context, + logger *logrus.Entry, + req *http.Request, + permission *openapi.RondConfig, + partialResultsEvaluators PartialResultsEvaluators, + env config.EnvironmentVariables, +) *OPATransport { + return &OPATransport{ + http.DefaultTransport, + req.Context(), + logger, + req, + permission, + partialResultsEvaluators, + env, + } +} + func is2XX(statusCode int) bool { return statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices } @@ -87,7 +106,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return resp, nil } - input, err := core.CreateRegoQueryInput(t.request, t.env, t.permission.Options.EnableResourcePermissionsMapOptimization, userInfo, decodedBody) + input, err := CreateRegoQueryInput(t.request, t.env, t.permission.Options.EnableResourcePermissionsMapOptimization, userInfo, decodedBody) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil @@ -120,9 +139,9 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er func (t *OPATransport) responseWithError(resp *http.Response, err error, statusCode int) { t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("error while evaluating column filter query") - message := NO_PERMISSIONS_ERROR_MESSAGE + message := utils.NO_PERMISSIONS_ERROR_MESSAGE if statusCode != http.StatusForbidden { - message = GENERIC_BUSINESS_ERROR_MESSAGE + message = utils.GENERIC_BUSINESS_ERROR_MESSAGE } content, _ := json.Marshal(types.RequestError{ StatusCode: statusCode, diff --git a/opa_transport_test.go b/core/opa_transport_test.go similarity index 98% rename from opa_transport_test.go rename to core/opa_transport_test.go index 275f0f0e..18310db1 100644 --- a/opa_transport_test.go +++ b/core/opa_transport_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package core import ( "bytes" @@ -25,10 +25,10 @@ import ( "strings" "testing" - "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -116,7 +116,7 @@ func TestOPATransportResponseWithError(t *testing.T) { require.Nil(t, err) expectedBytes, err := json.Marshal(types.RequestError{ StatusCode: http.StatusInternalServerError, - Message: GENERIC_BUSINESS_ERROR_MESSAGE, + Message: utils.GENERIC_BUSINESS_ERROR_MESSAGE, Error: "some error", }) require.Nil(t, err) @@ -138,7 +138,7 @@ func TestOPATransportResponseWithError(t *testing.T) { require.Nil(t, err) expectedBytes, err := json.Marshal(types.RequestError{ StatusCode: http.StatusForbidden, - Message: NO_PERMISSIONS_ERROR_MESSAGE, + Message: utils.NO_PERMISSIONS_ERROR_MESSAGE, Error: "some error", }) require.Nil(t, err) @@ -365,7 +365,7 @@ func TestOPATransportRoundTrip(t *testing.T) { &openapi.RondConfig{ ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, }, - core.PartialResultsEvaluators{"my_policy": {}}, + PartialResultsEvaluators{"my_policy": {}}, envs, } resp, err := transport.RoundTrip(req) diff --git a/handler.go b/handler.go index 2d2e8094..5efd15fe 100644 --- a/handler.go +++ b/handler.go @@ -33,8 +33,6 @@ import ( const URL_SCHEME = "http" const BASE_ROW_FILTER_HEADER_KEY = "acl_rows" -const GENERIC_BUSINESS_ERROR_MESSAGE = "Internal server error, please try again later" -const NO_PERMISSIONS_ERROR_MESSAGE = "You do not have permissions to access this feature, contact the administrator for more information." func ReverseProxyOrResponse( logger *logrus.Entry, @@ -69,20 +67,20 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { env, err := config.GetEnv(requestContext) if err != nil { logger.WithError(err).Error("no env found in context") - utils.FailResponse(w, "No environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "No environment found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } permission, err := openapi.GetXPermission(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no policy permission found in context") - utils.FailResponse(w, "no policy permission found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no policy permission found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } partialResultEvaluators, err := core.GetPartialResultsEvaluators(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no partialResult evaluators found in context") - utils.FailResponse(w, "no partialResult evaluators found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no partialResult evaluators found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -105,14 +103,14 @@ func EvaluateRequest( userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, env) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed user bindings and roles retrieving") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } input, err := core.CreateRegoQueryInput(req, env, permission.Options.EnableResourcePermissionsMapOptimization, userInfo, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed rego query input creation") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } @@ -121,14 +119,14 @@ func EvaluateRequest( evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, input, env) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot find policy evaluator") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } } else { evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, env, permission.RequestFlow.PolicyName, input, nil) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot create evaluator") - utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", NO_PERMISSIONS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) return err } } @@ -149,7 +147,7 @@ func EvaluateRequest( "policyName": permission.RequestFlow.PolicyName, "message": err.Error(), }).Error("RBAC policy evaluation failed") - utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", NO_PERMISSIONS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) return err } var queryToProxy = []byte{} @@ -157,7 +155,7 @@ func EvaluateRequest( queryToProxy, err = json.Marshal(query) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("Error while marshaling row filter query") - utils.FailResponseWithCode(w, http.StatusForbidden, "Error while marshaling row filter query", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusForbidden, "Error while marshaling row filter query", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } } @@ -198,7 +196,7 @@ func ReverseProxy( proxy.ServeHTTP(w, req) return } - proxy.Transport = &OPATransport{ + proxy.Transport = core.NewOPATransport( http.DefaultTransport, req.Context(), logger, @@ -206,7 +204,7 @@ func ReverseProxy( permission, partialResultsEvaluators, env, - } + ) proxy.ServeHTTP(w, req) } @@ -216,7 +214,7 @@ func alwaysProxyHandler(w http.ResponseWriter, req *http.Request) { env, err := config.GetEnv(requestContext) if err != nil { glogger.Get(requestContext).WithError(err).Error("no env found in context") - utils.FailResponse(w, "no environment found in context", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponse(w, "no environment found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } ReverseProxyOrResponse(logger, env, w, req, nil, nil) diff --git a/handler_test.go b/handler_test.go index 533553ed..ae5ce7d0 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1351,7 +1351,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) rbacHandler(w, r) - testutils.AssertResponseFullErrorMessages(t, w, http.StatusInternalServerError, "user bindings retrieval failed", GENERIC_BUSINESS_ERROR_MESSAGE) + testutils.AssertResponseFullErrorMessages(t, w, http.StatusInternalServerError, "user bindings retrieval failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) require.True(t, !invoked, "Handler was not invoked.") require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode, "Unexpected status code.") }) @@ -1442,7 +1442,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { r.Header.Set(userIdHeaderKey, "miauserid") rbacHandler(w, r) - testutils.AssertResponseFullErrorMessages(t, w, http.StatusForbidden, "RBAC policy evaluation failed", NO_PERMISSIONS_ERROR_MESSAGE) + testutils.AssertResponseFullErrorMessages(t, w, http.StatusForbidden, "RBAC policy evaluation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) diff --git a/internal/utils/general.go b/internal/utils/general.go index f300b307..52386e7b 100644 --- a/internal/utils/general.go +++ b/internal/utils/general.go @@ -23,6 +23,9 @@ import ( "github.com/samber/lo" ) +const GENERIC_BUSINESS_ERROR_MESSAGE = "Internal server error, please try again later" +const NO_PERMISSIONS_ERROR_MESSAGE = "You do not have permissions to access this feature, contact the administrator for more information." + var ErrFileLoadFailed = errors.New("file loading failed") var Contains = lo.Contains[string] diff --git a/standalone_apis.go b/standalone_apis.go index a6245603..9b226acc 100644 --- a/standalone_apis.go +++ b/standalone_apis.go @@ -47,23 +47,23 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { logger := glogger.Get(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } reqBody := RevokeRequestBody{} if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } resourceType := mux.Vars(r)["resourceType"] if resourceType != "" && len(reqBody.ResourceIDs) == 0 { - utils.FailResponseWithCode(w, http.StatusBadRequest, "empty resources list", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "empty resources list", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } if len(reqBody.Subjects) == 0 && len(reqBody.Groups) == 0 { - utils.FailResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -72,20 +72,20 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { client, err := crudclient.New(env.BindingsCrudServiceURL) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud setup") - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } query, err := buildQuery(resourceType, reqBody.ResourceIDs, reqBody.Subjects, reqBody.Groups) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed find query crud setup") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed find query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed find query crud setup", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } if err := client.Get(r.Context(), fmt.Sprintf("_q=%s&_l=%d", string(query), BINDINGS_MAX_PAGE_SIZE), &bindings); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for finding bindings", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for finding bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -98,7 +98,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { query, err := buildQueryForBindingsToDelete(bindingsToDelete) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed delete query crud setup") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed delete query crud setup", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed delete query crud setup", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -109,7 +109,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { if err := client.Delete(r.Context(), fmt.Sprintf("_q=%s", string(query)), &deleteCrudResponse); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for deleting unused bindings", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for deleting unused bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } logger.WithField("deletedBindings", deleteCrudResponse).Debug("binding deletion finished") @@ -124,7 +124,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { w, http.StatusInternalServerError, fmt.Sprintf("failed crud request to modify existing bindings. removed bindings: %d", deleteCrudResponse), - GENERIC_BUSINESS_ERROR_MESSAGE, + utils.GENERIC_BUSINESS_ERROR_MESSAGE, ) return } @@ -142,7 +142,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { w, http.StatusInternalServerError, fmt.Sprintf("failed response body creation. removed bindings: %d, modified bindings: %d", deleteCrudResponse, patchCrudResponse), - GENERIC_BUSINESS_ERROR_MESSAGE, + utils.GENERIC_BUSINESS_ERROR_MESSAGE, ) } if _, err := w.Write(responseBytes); err != nil { @@ -165,31 +165,31 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { logger := glogger.Get(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } reqBody := GrantRequestBody{} if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } resourceType := mux.Vars(r)["resourceType"] if resourceType != "" && reqBody.ResourceID == "" { - utils.FailResponseWithCode(w, http.StatusBadRequest, "missing resource id", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "missing resource id", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } if len(reqBody.Groups) == 0 && len(reqBody.Permissions) == 0 && len(reqBody.Subjects) == 0 && len(reqBody.Roles) == 0 { - utils.FailResponseWithCode(w, http.StatusBadRequest, "missing body fields, one of groups, permissions, subjects or roles is required", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusBadRequest, "missing body fields, one of groups, permissions, subjects or roles is required", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } client, err := crudclient.New(env.BindingsCrudServiceURL) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud setup") - utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -210,7 +210,7 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { var bindingIDCreated types.BindingCreateResponse if err := client.Post(r.Context(), &bindingToCreate, &bindingIDCreated); 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", GENERIC_BUSINESS_ERROR_MESSAGE) + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for creating bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } logger.WithFields(logrus.Fields{ @@ -229,7 +229,7 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { w, http.StatusInternalServerError, "failed response body creation", - GENERIC_BUSINESS_ERROR_MESSAGE, + utils.GENERIC_BUSINESS_ERROR_MESSAGE, ) } if _, err := w.Write(responseBytes); err != nil { From 21e1fad736a7117c5f5d70e0f43c3497185843b2 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:49:01 +0100 Subject: [PATCH 037/172] copy and null logger --- core/opamiddleware.go | 14 ++++++++++++++ handler_test.go | 6 ++---- internal/metrics/metrics.go | 14 ++++++++++++++ internal/metrics/metrics_test.go | 14 ++++++++++++++ internal/metrics/routes.go | 14 ++++++++++++++ internal/metrics/routes_test.go | 14 ++++++++++++++ internal/utils/http_test.go | 14 ++++++++++++++ 7 files changed, 86 insertions(+), 4 deletions(-) diff --git a/core/opamiddleware.go b/core/opamiddleware.go index 7de4b2e9..c1cb54c2 100644 --- a/core/opamiddleware.go +++ b/core/opamiddleware.go @@ -1,3 +1,17 @@ +// 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 core import ( diff --git a/handler_test.go b/handler_test.go index ae5ce7d0..a367ed8f 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1970,9 +1970,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { UserIdHeader: "miauserid", } - // nilLogger, _ := test.NewNullLogger() - nilLogger := logrus.New() - logger := logrus.NewEntry(nilLogger) + nilLogger, _ := test.NewNullLogger() b.ResetTimer() for n := 0; n < b.N; n++ { @@ -1997,7 +1995,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { ), metrics.SetupMetrics(""), ), - logger, + logrus.NewEntry(nilLogger), ), ) req.Header.Set("miausergroups", "area_rocket") diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 0b10cae6..a3d1e6f3 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -1,3 +1,17 @@ +// 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 metrics import ( diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index 346d7c56..d5ab4acf 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -1,3 +1,17 @@ +// 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 metrics import ( diff --git a/internal/metrics/routes.go b/internal/metrics/routes.go index 0e3918b4..60bb122c 100644 --- a/internal/metrics/routes.go +++ b/internal/metrics/routes.go @@ -1,3 +1,17 @@ +// 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 metrics import ( diff --git a/internal/metrics/routes_test.go b/internal/metrics/routes_test.go index e41563cf..9f1046f5 100644 --- a/internal/metrics/routes_test.go +++ b/internal/metrics/routes_test.go @@ -1,3 +1,17 @@ +// 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 metrics import ( diff --git a/internal/utils/http_test.go b/internal/utils/http_test.go index 08362ead..47e8d336 100644 --- a/internal/utils/http_test.go +++ b/internal/utils/http_test.go @@ -1,3 +1,17 @@ +// 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 utils import ( From ab6ee93f03f1ce5a588d4a7a4997e13c40c59e46 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:50:17 +0100 Subject: [PATCH 038/172] fix: test logger --- handler_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/handler_test.go b/handler_test.go index a367ed8f..d412a212 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1971,6 +1971,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { } nilLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(nilLogger) b.ResetTimer() for n := 0; n < b.N; n++ { @@ -1995,7 +1996,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { ), metrics.SetupMetrics(""), ), - logrus.NewEntry(nilLogger), + logger, ), ) req.Header.Set("miausergroups", "area_rocket") From 295f2ac1a220710653ef3d16a1eb7d4285bdcf34 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 17:59:47 +0100 Subject: [PATCH 039/172] service pkg --- main.go | 88 +----------------- main_test.go | 7 +- handler.go => service/handler.go | 2 +- handler_test.go => service/handler_test.go | 4 +- router.go => service/router.go | 90 ++++++++++++++++++- router_test.go => service/router_test.go | 8 +- .../standalone_apis.go | 2 +- .../standalone_apis_test.go | 2 +- statusroutes.go => service/statusroutes.go | 2 +- .../statusroutes_test.go | 6 +- 10 files changed, 108 insertions(+), 103 deletions(-) rename handler.go => service/handler.go (99%) rename handler_test.go => service/handler_test.go (99%) rename router.go => service/router.go (62%) rename router_test.go => service/router_test.go (98%) rename standalone_apis.go => service/standalone_apis.go (99%) rename standalone_apis_test.go => service/standalone_apis_test.go (99%) rename statusroutes.go => service/statusroutes.go (99%) rename statusroutes_test.go => service/statusroutes_test.go (97%) diff --git a/main.go b/main.go index f0b90740..06dfb24f 100644 --- a/main.go +++ b/main.go @@ -23,18 +23,13 @@ import ( "syscall" "time" - swagger "github.com/davidebianchi/gswagger" - "github.com/davidebianchi/gswagger/support/gorilla" - "github.com/getkin/kin-openapi/openapi3" - "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/service" - "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" "github.com/sirupsen/logrus" ) @@ -108,7 +103,7 @@ func entrypoint(shutdown chan os.Signal) { log.WithField("policiesLength", len(policiesEvaluators)).Debug("policies evaluators partial results computed") // Routing - router, err := setupRouter(log, env, opaModuleConfig, oas, policiesEvaluators, mongoClient) + router, err := service.SetupRouter(log, env, opaModuleConfig, oas, policiesEvaluators, mongoClient) if mongoClient != nil { defer mongoClient.Disconnect() } @@ -139,82 +134,3 @@ func entrypoint(shutdown chan os.Signal) { // SIGINT (Ctrl+C), SIGKILL or SIGQUIT will not be caught. helpers.GracefulShutdown(srv, shutdown, log, env.DelayShutdownSeconds) } - -func setupRouter( - log *logrus.Logger, - env config.EnvironmentVariables, - opaModuleConfig *core.OPAModuleConfig, - oas *openapi.OpenAPISpec, - policiesEvaluators core.PartialResultsEvaluators, - mongoClient *mongoclient.MongoClient, -) (*mux.Router, error) { - router := mux.NewRouter().UseEncodedPath() - router.Use(glogger.RequestMiddlewareLogger(log, []string{"/-/"})) - serviceName := "rönd" - StatusRoutes(router, serviceName, env.ServiceVersion) - - registry := prometheus.NewRegistry() - m := metrics.SetupMetrics("rond") - if env.ExposeMetrics { - m.MustRegister(registry) - metrics.MetricsRoute(router, registry) - } - router.Use(metrics.RequestMiddleware(m)) - - router.Use(config.RequestMiddlewareEnvironments(env)) - - evalRouter := router.NewRoute().Subrouter() - if env.Standalone { - router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) - - swaggerRouter, err := swagger.NewRouter(gorilla.NewRouter(router), swagger.Options{ - Context: context.Background(), - Openapi: &openapi3.T{ - Info: &openapi3.Info{ - Title: serviceName, - Version: env.ServiceVersion, - }, - }, - JSONDocumentationPath: "/openapi/json", - YAMLDocumentationPath: "/openapi/yaml", - }) - if err != nil { - return nil, err - } - - // standalone routes - if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings/resource/{resourceType}", revokeHandler, revokeDefinitions); err != nil { - return nil, err - } - if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings/resource/{resourceType}", grantHandler, grantDefinitions); err != nil { - return nil, err - } - if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings", revokeHandler, revokeDefinitions); err != nil { - return nil, err - } - if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings", grantHandler, grantDefinitions); err != nil { - return nil, err - } - - if err = swaggerRouter.GenerateAndExposeOpenapi(); err != nil { - return nil, err - } - } - - evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, &env, policiesEvaluators, routesToNotProxy)) - - if mongoClient != nil { - evalRouter.Use(mongoclient.MongoClientInjectorMiddleware(mongoClient)) - } - - setupRoutes(evalRouter, oas, env) - - //#nosec G104 -- Produces a false positive - router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { - path, _ := route.GetPathTemplate() - log.Tracef("Registered path: %s", path) - return nil - }) - - return router, nil -} diff --git a/main_test.go b/main_test.go index ffd41c91..a6c76877 100644 --- a/main_test.go +++ b/main_test.go @@ -35,6 +35,7 @@ import ( "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/service" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -1713,7 +1714,7 @@ filter_policy { evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) require.NoError(t, err, "unexpected error") - router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) require.NoError(t, err, "unexpected error") t.Run("some eval API", func(t *testing.T) { @@ -1765,7 +1766,7 @@ filter_policy { }) t.Run("grant API with headers to proxy", func(t *testing.T) { - reqBody := GrantRequestBody{ + reqBody := service.GrantRequestBody{ ResourceID: "my-company", Subjects: []string{"subj"}, Groups: []string{"group1"}, @@ -1868,7 +1869,7 @@ filter_policy { evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) require.NoError(t, err, "unexpected error") - router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) require.NoError(t, err, "unexpected error") t.Run("metrics API exposed correctly", func(t *testing.T) { diff --git a/handler.go b/service/handler.go similarity index 99% rename from handler.go rename to service/handler.go index 5efd15fe..d1739fd6 100644 --- a/handler.go +++ b/service/handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "encoding/json" diff --git a/handler_test.go b/service/handler_test.go similarity index 99% rename from handler_test.go rename to service/handler_test.go index d412a212..7bc791c5 100644 --- a/handler_test.go +++ b/service/handler_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "context" @@ -1941,7 +1941,7 @@ project.tenantId == "1234" } func BenchmarkEvaluateRequest(b *testing.B) { - moduleConfig, err := core.LoadRegoModule("./mocks/bench-policies") + moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") require.NoError(b, err, "Unexpected error") permission := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "allow_view_project"}} diff --git a/router.go b/service/router.go similarity index 62% rename from router.go rename to service/router.go index 7a51c33a..56343091 100644 --- a/router.go +++ b/service/router.go @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( + "context" "fmt" "net/http" "path" @@ -22,13 +23,21 @@ import ( "strings" swagger "github.com/davidebianchi/gswagger" + "github.com/davidebianchi/gswagger/support/gorilla" + "github.com/getkin/kin-openapi/openapi3" + "github.com/prometheus/client_golang/prometheus" + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" + "github.com/mia-platform/glogger/v2" + "github.com/sirupsen/logrus" ) var routesToNotProxy = utils.Union(statusRoutes, []string{metrics.MetricsRoutePath}) @@ -87,6 +96,85 @@ var grantDefinitions = swagger.Definitions{ }, } +func SetupRouter( + log *logrus.Logger, + env config.EnvironmentVariables, + opaModuleConfig *core.OPAModuleConfig, + oas *openapi.OpenAPISpec, + policiesEvaluators core.PartialResultsEvaluators, + mongoClient *mongoclient.MongoClient, +) (*mux.Router, error) { + router := mux.NewRouter().UseEncodedPath() + router.Use(glogger.RequestMiddlewareLogger(log, []string{"/-/"})) + serviceName := "rönd" + StatusRoutes(router, serviceName, env.ServiceVersion) + + registry := prometheus.NewRegistry() + m := metrics.SetupMetrics("rond") + if env.ExposeMetrics { + m.MustRegister(registry) + metrics.MetricsRoute(router, registry) + } + router.Use(metrics.RequestMiddleware(m)) + + router.Use(config.RequestMiddlewareEnvironments(env)) + + evalRouter := router.NewRoute().Subrouter() + if env.Standalone { + router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) + + swaggerRouter, err := swagger.NewRouter(gorilla.NewRouter(router), swagger.Options{ + Context: context.Background(), + Openapi: &openapi3.T{ + Info: &openapi3.Info{ + Title: serviceName, + Version: env.ServiceVersion, + }, + }, + JSONDocumentationPath: "/openapi/json", + YAMLDocumentationPath: "/openapi/yaml", + }) + if err != nil { + return nil, err + } + + // standalone routes + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings/resource/{resourceType}", revokeHandler, revokeDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings/resource/{resourceType}", grantHandler, grantDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/revoke/bindings", revokeHandler, revokeDefinitions); err != nil { + return nil, err + } + if _, err := swaggerRouter.AddRoute(http.MethodPost, "/grant/bindings", grantHandler, grantDefinitions); err != nil { + return nil, err + } + + if err = swaggerRouter.GenerateAndExposeOpenapi(); err != nil { + return nil, err + } + } + + evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, &env, policiesEvaluators, routesToNotProxy)) + + if mongoClient != nil { + evalRouter.Use(mongoclient.MongoClientInjectorMiddleware(mongoClient)) + } + + setupRoutes(evalRouter, oas, env) + + //#nosec G104 -- Produces a false positive + router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + path, _ := route.GetPathTemplate() + log.Tracef("Registered path: %s", path) + return nil + }) + + return router, nil +} + func setupRoutes(router *mux.Router, oas *openapi.OpenAPISpec, env config.EnvironmentVariables) { var documentationPermission string documentationPathInOAS := oas.Paths[env.TargetServiceOASPath] diff --git a/router_test.go b/service/router_test.go similarity index 98% rename from router_test.go rename to service/router_test.go index 94de7b8e..de52134d 100644 --- a/router_test.go +++ b/service/router_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "context" @@ -247,7 +247,7 @@ var mockRondConfigWithQueryGen = &openapi.RondConfig{ func TestSetupRoutesIntegration(t *testing.T) { envs := config.EnvironmentVariables{} - oas := prepareOASFromFile(t, "./mocks/simplifiedMock.json") + oas := prepareOASFromFile(t, "../mocks/simplifiedMock.json") log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) @@ -392,7 +392,7 @@ func TestSetupRoutesIntegration(t *testing.T) { }) t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { - oas := prepareOASFromFile(t, "./mocks/nestedPathsConfig.json") + oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -429,7 +429,7 @@ func TestSetupRoutesIntegration(t *testing.T) { }) t.Run("invokes a specific API within a nested path", func(t *testing.T) { - oas := prepareOASFromFile(t, "./mocks/nestedPathsConfig.json") + oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/standalone_apis.go b/service/standalone_apis.go similarity index 99% rename from standalone_apis.go rename to service/standalone_apis.go index 9b226acc..62a9993b 100644 --- a/standalone_apis.go +++ b/service/standalone_apis.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "encoding/json" diff --git a/standalone_apis_test.go b/service/standalone_apis_test.go similarity index 99% rename from standalone_apis_test.go rename to service/standalone_apis_test.go index 2f5277d5..38bcd5e4 100644 --- a/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "bytes" diff --git a/statusroutes.go b/service/statusroutes.go similarity index 99% rename from statusroutes.go rename to service/statusroutes.go index 05539c63..a76f8edc 100644 --- a/statusroutes.go +++ b/service/statusroutes.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "encoding/json" diff --git a/statusroutes_test.go b/service/statusroutes_test.go similarity index 97% rename from statusroutes_test.go rename to service/statusroutes_test.go index 1832f3d6..0b898116 100644 --- a/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package service import ( "context" @@ -122,7 +122,7 @@ test_policy { true } TargetServiceHost: "my-service:4444", PathPrefixStandalone: "/my-prefix", } - router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { @@ -155,7 +155,7 @@ test_policy { true } PathPrefixStandalone: "/my-prefix", ServiceVersion: "latest", } - router, err := setupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() From 7ee0e6f9f27a1c3038c058f30473915fb5087d5a Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 18:14:34 +0100 Subject: [PATCH 040/172] fix: benchmark --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e19d0c16..13300028 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: with: go-version: ${{ matrix.go_version }} - name: Run benchmark - run: go test -bench=. -run=Bench | tee output.txt + run: go test -benchmem -run=^$ -bench ^Bench ./... | tee output.txt - name: Download previous benchmark data uses: actions/cache@v3 with: From 5ad9962fc614147d3aab95fba3a2c8286bad1322 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 18:57:32 +0100 Subject: [PATCH 041/172] make bench --- .github/workflows/test.yml | 2 +- CONTRIBUTING.md | 2 +- Makefile | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13300028..c87696c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: with: go-version: ${{ matrix.go_version }} - name: Run benchmark - run: go test -benchmem -run=^$ -bench ^Bench ./... | tee output.txt + run: make bench | tee output.txt - name: Download previous benchmark data uses: actions/cache@v3 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dde7c345..635ecc79 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ sure you run benchmarks and verify that results are not affected by tour changes To run benchmark use: ```sh -go test ./... -bench=. -run=Bench -benchmem +make bench ``` ### Bench results diff --git a/Makefile b/Makefile index 1f9be497..57b17273 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,10 @@ test: clean mongo-start go test ./... -coverprofile coverage.out $(MAKE) clean +.PHONY: bench +bench: + go test -benchmem -bench=^Bench ./... + .PHONY: clean clean: docker rm mongo --force From cbc59088a54fc9c16871f1393d98ebcee9871183 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 19:01:01 +0100 Subject: [PATCH 042/172] verify bench errors --- core/opaevaluator_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 1d342883..7941d6d1 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -24,6 +24,7 @@ import ( "regexp" "strings" "testing" + "time" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" @@ -345,6 +346,7 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { b.StartTimer() + time.Sleep(200 * time.Millisecond) buildOptimizedResourcePermissionsMap(user) b.StopTimer() } From e3744d96d3de03d56bdab6069ef8b01d2f7d518b Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 19:03:35 +0100 Subject: [PATCH 043/172] fix: make bench script --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 57b17273..30249106 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ test: clean mongo-start $(MAKE) clean .PHONY: bench -bench: +bench: clean mongo-start go test -benchmem -bench=^Bench ./... .PHONY: clean From 802d41433844c1b6b63c4524a6ea740d72ae68c1 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 2 Jan 2023 19:06:09 +0100 Subject: [PATCH 044/172] bench alert verified --- core/opaevaluator_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 7941d6d1..1d342883 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -24,7 +24,6 @@ import ( "regexp" "strings" "testing" - "time" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" @@ -346,7 +345,6 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { b.StartTimer() - time.Sleep(200 * time.Millisecond) buildOptimizedResourcePermissionsMap(user) b.StopTimer() } From a65cad4ff76707f4ccfda74832e5ab43447f8c2d Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 10 Jan 2023 11:25:28 +0100 Subject: [PATCH 045/172] todo comment --- core/opaevaluator.go | 1 + openapi/routerinfo_middleware.go | 1 + 2 files changed, 2 insertions(+) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 4e5a90aa..dd04a169 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -476,6 +476,7 @@ func GetPartialResultsEvaluators(requestContext context.Context) (PartialResults return evaluators, nil } +// TODO: This should be made private in the future. type OPAModuleConfigKey struct{} type OPAModuleConfig struct { diff --git a/openapi/routerinfo_middleware.go b/openapi/routerinfo_middleware.go index 9ca8d82e..64900b9f 100644 --- a/openapi/routerinfo_middleware.go +++ b/openapi/routerinfo_middleware.go @@ -25,6 +25,7 @@ import ( "github.com/sirupsen/logrus" ) +// TODO: This should be made private in the future. type RouterInfoKey struct{} type RouterInfo struct { From 1f5ec2f5138631c4ad1c4a47738c4058029ce818 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:39:44 +0100 Subject: [PATCH 046/172] chore(deps): bump github.com/open-policy-agent/opa from 0.47.4 to 0.48.0 (#142) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.47.4 to 0.48.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.47.4...v0.48.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index f3f3b233..278a97e6 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.47.4 + github.com/open-policy-agent/opa v0.48.0 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 @@ -42,7 +42,7 @@ require ( github.com/knadh/koanf v1.4.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mia-platform/jsonschema v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -72,11 +72,11 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect + golang.org/x/crypto v0.4.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect - golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 80be3d48..9bab0811 100644 --- a/go.sum +++ b/go.sum @@ -99,7 +99,7 @@ github.com/davidebianchi/go-jsonclient v1.3.0 h1:6579uXOHEDcClHK8b3j/xWbQV4Khpn7 github.com/davidebianchi/go-jsonclient v1.3.0/go.mod h1:lIOd35jxGfGENA/M0s1klHJgagIDj2KM1O6A/V5vpPI= github.com/davidebianchi/gswagger v0.8.0 h1:szFH4hYEyVPfhcpggWQgKDNnIek5rXXIW04EvNlG/M0= github.com/davidebianchi/gswagger v0.8.0/go.mod h1:coXlWOGJDhZ1Zm/1ORvELzejxewFJIeB6DDD1vYo9zU= -github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -330,8 +330,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjKD5OWJ1s= github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= github.com/mia-platform/glogger/v2 v2.1.3 h1:Qt/qHETYaFa+Oso9XZauysnHF9CE5816AQJmMo2Uh0Q= @@ -374,8 +374,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.47.4 h1:CTPIoAv6/UJX+BkSkqytbofWrZHyfQ/A0ESE4FSKR9A= -github.com/open-policy-agent/opa v0.47.4/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= +github.com/open-policy-agent/opa v0.48.0 h1:s2K823yohAUu/HB4MOPWDhBh88JMKQv7uTr6S89fbM0= +github.com/open-policy-agent/opa v0.48.0/go.mod h1:CsQcksP+qGBxO9oEBj1NnZqKcjgjmTJbRNTzjZB/DXQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -544,8 +544,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= 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= @@ -625,7 +625,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= 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= @@ -648,8 +648,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -714,8 +714,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -728,8 +728,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From fc29b6df92bc652f76f4b25cb16eb23d6396ef28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 12:53:11 +0100 Subject: [PATCH 047/172] chore(deps): bump golang from 1.19.4 to 1.19.5 (#143) Bumps golang from 1.19.4 to 1.19.5. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4ddfa10a..1f04ffd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.19.4 AS builder +FROM golang:1.19.5 AS builder WORKDIR /app From 6ff6e38ce8a9b5e66125930e1639dfcffb881263 Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Thu, 12 Jan 2023 01:10:55 +0800 Subject: [PATCH 048/172] test: use `T.Setenv` to set env vars in tests (#144) This commit replaces `os.Setenv` with `t.Setenv` in tests. The environment variable is automatically restored to its original value when the test and all its subtests complete. Reference: https://pkg.go.dev/testing#T.Setenv Signed-off-by: Eng Zer Jun Signed-off-by: Eng Zer Jun --- internal/config/env_test.go | 26 +++------ main_test.go | 103 ++++++++++++------------------------ 2 files changed, 40 insertions(+), 89 deletions(-) diff --git a/internal/config/env_test.go b/internal/config/env_test.go index c01f0908..4035e2e4 100644 --- a/internal/config/env_test.go +++ b/internal/config/env_test.go @@ -19,7 +19,6 @@ import ( "fmt" "net/http" "net/http/httptest" - "os" "testing" "github.com/stretchr/testify/require" @@ -89,8 +88,7 @@ func TestGetEnvOrDie(t *testing.T) { {name: "TARGET_SERVICE_HOST", value: "http://localhost:3000"}, } envs := append(requiredEnvs, otherEnvs...) - unsetEnvs := setEnvs(envs) - defer unsetEnvs() + setEnvs(t, envs) actualEnvs := GetEnvOrDie() expectedEnvs := defaultAndRequiredEnvironmentVariables @@ -105,8 +103,7 @@ func TestGetEnvOrDie(t *testing.T) { {name: "BINDINGS_CRUD_SERVICE_URL", value: "http://crud-client"}, } envs := append(requiredEnvs, otherEnvs...) - unsetEnvs := setEnvs(envs) - defer unsetEnvs() + setEnvs(t, envs) actualEnvs := GetEnvOrDie() expectedEnvs := defaultAndRequiredEnvironmentVariables @@ -121,8 +118,7 @@ func TestGetEnvOrDie(t *testing.T) { {name: "STANDALONE", value: "true"}, } envs := append(requiredEnvs, otherEnvs...) - unsetEnvs := setEnvs(envs) - defer unsetEnvs() + setEnvs(t, envs) defer func() { r := recover() @@ -139,8 +135,7 @@ func TestGetEnvOrDie(t *testing.T) { {name: "STANDALONE", value: "false"}, } envs := append(requiredEnvs, otherEnvs...) - unsetEnvs := setEnvs(envs) - defer unsetEnvs() + setEnvs(t, envs) require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", TargetServiceHostEnvKey, StandaloneEnvKey), func() { GetEnvOrDie() @@ -150,8 +145,7 @@ func TestGetEnvOrDie(t *testing.T) { t.Run(`throws - no Standalone or TargetServiceHost`, func(t *testing.T) { otherEnvs := []env{} envs := append(requiredEnvs, otherEnvs...) - unsetEnvs := setEnvs(envs) - defer unsetEnvs() + setEnvs(t, envs) require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", TargetServiceHostEnvKey, StandaloneEnvKey), func() { GetEnvOrDie() @@ -164,15 +158,9 @@ type env struct { value string } -func setEnvs(envsToSet []env) func() { +func setEnvs(t *testing.T, envsToSet []env) { for _, env := range envsToSet { - os.Setenv(env.name, env.value) - } - - return func() { - for _, env := range envsToSet { - os.Unsetenv(env.name) - } + t.Setenv(env.name, env.value) } } diff --git a/main_test.go b/main_test.go index a6c76877..802dddd0 100644 --- a/main_test.go +++ b/main_test.go @@ -68,7 +68,7 @@ func TestProxyOASPath(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3000"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/custom/documentation/json"}, @@ -80,7 +80,6 @@ func TestProxyOASPath(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -112,7 +111,7 @@ func TestProxyOASPath(t *testing.T) { Reply(200). File("./mocks/documentationPathMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3007"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3006"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -123,7 +122,6 @@ func TestProxyOASPath(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -155,7 +153,7 @@ func TestProxyOASPath(t *testing.T) { Reply(200). File("./mocks/documentationPathMockWithPermissions.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3009"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3008"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -166,7 +164,6 @@ func TestProxyOASPath(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -180,7 +177,7 @@ func TestProxyOASPath(t *testing.T) { // FIXME: This function needs to be performed as last in order to make other tests working func TestEntrypoint(t *testing.T) { t.Run("fails for invalid module path, no module found", func(t *testing.T) { - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3000"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -191,7 +188,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) require.True(t, true, "If we get here the service has not started") - unsetEnvs() }) t.Run("opens server on port 3000", func(t *testing.T) { @@ -208,7 +204,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3000"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -220,7 +216,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() @@ -238,7 +233,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3000"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -262,7 +257,6 @@ func TestEntrypoint(t *testing.T) { flag := <-done require.Equal(t, true, flag) - unsetEnvs() }) t.Run("opa integration", func(t *testing.T) { @@ -287,7 +281,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3000"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -299,7 +293,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -350,7 +343,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMock.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3026"}, {name: "LOG_LEVEL", value: "fatal"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3001"}, @@ -364,7 +357,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -400,7 +392,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/mockWithXPermissionEmpty.json") - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3005"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3004"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -412,7 +404,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -445,7 +436,7 @@ func TestEntrypoint(t *testing.T) { return true }) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3333"}, {name: "TARGET_SERVICE_HOST", value: "localhost:4000"}, {name: "API_PERMISSIONS_FILE_PATH", value: "./mocks/nestedPathsConfig.json"}, @@ -457,7 +448,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -494,7 +484,7 @@ func TestEntrypoint(t *testing.T) { return true }) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "5555"}, {name: "TARGET_SERVICE_HOST", value: "localhost:6000"}, {name: "API_PERMISSIONS_FILE_PATH", value: "./mocks/mockForEncodedTest.json"}, @@ -506,7 +496,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -543,7 +532,7 @@ func TestEntrypoint(t *testing.T) { return true }) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "5556"}, {name: "TARGET_SERVICE_HOST", value: "localhost:6000"}, {name: "API_PERMISSIONS_FILE_PATH", value: "./mocks/mockForEncodedTest.json"}, @@ -555,7 +544,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -592,7 +580,7 @@ func TestEntrypoint(t *testing.T) { return true }) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "5557"}, {name: "TARGET_SERVICE_HOST", value: "localhost:6000"}, {name: "API_PERMISSIONS_FILE_PATH", value: "./mocks/mockForEncodedTest.json"}, @@ -604,7 +592,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -655,7 +642,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3003"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3002"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -690,7 +677,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -830,7 +816,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMockWithRowFiltering.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3034"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3033"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -845,7 +831,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -876,8 +862,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -926,7 +910,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "5034"}, {name: "LOG_LEVEL", value: "fatal"}, {name: "TARGET_SERVICE_HOST", value: "localhost:5033"}, @@ -962,7 +946,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1010,7 +993,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/simplifiedMockWithRowFiltering.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3036"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3035"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1025,7 +1008,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1056,8 +1039,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1096,7 +1077,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/pathsWithWildCardCollision.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3039"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3038"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1109,7 +1090,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1140,8 +1121,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1182,7 +1161,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/pathsWithWildCardCollision2.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3039"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3038"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1195,7 +1174,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1226,8 +1205,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1268,7 +1245,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/pathsWithWildCardCollision.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3039"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3038"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1281,7 +1258,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1312,8 +1289,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1354,7 +1329,7 @@ func TestEntrypoint(t *testing.T) { Reply(200). File("./mocks/oasExampleCrud.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3044"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3043"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1367,7 +1342,7 @@ func TestEntrypoint(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1398,8 +1373,6 @@ func TestEntrypoint(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1447,7 +1420,7 @@ func TestEntrypointWithResponseFiltering(t *testing.T) { Reply(200). File("./mocks/mockForResponseFilteringOnResponse.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3041"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3040"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1462,7 +1435,7 @@ func TestEntrypointWithResponseFiltering(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1493,8 +1466,6 @@ func TestEntrypointWithResponseFiltering(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) @@ -1555,15 +1526,9 @@ type env struct { value string } -func setEnvs(envsToSet []env) func() { +func setEnvs(t *testing.T, envsToSet []env) { for _, env := range envsToSet { - os.Setenv(env.name, env.value) - } - - return func() { - for _, env := range envsToSet { - os.Unsetenv(env.name) - } + t.Setenv(env.name, env.value) } } @@ -1589,7 +1554,7 @@ func TestIntegrationWithOASParamsInBrackets(t *testing.T) { Reply(200). File("./mocks/routesWithSamePath.json") - unsetBaseEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "HTTP_PORT", value: "3051"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3050"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, @@ -1604,7 +1569,7 @@ func TestIntegrationWithOASParamsInBrackets(t *testing.T) { randomizedDBNamePart := testutils.GetRandomName(10) mongoDBName := fmt.Sprintf("test-%s", randomizedDBNamePart) - unsetOtherEnvs := setEnvs([]env{ + setEnvs(t, []env{ {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, @@ -1636,8 +1601,6 @@ func TestIntegrationWithOASParamsInBrackets(t *testing.T) { entrypoint(shutdown) }() defer func() { - unsetBaseEnvs() - unsetOtherEnvs() shutdown <- syscall.SIGTERM }() time.Sleep(1 * time.Second) From 7ba9de3d45dbece96f248fc294558a1826c040b0 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 16 Jan 2023 11:40:32 +0100 Subject: [PATCH 049/172] Upgrade version to v1.6.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1f04ffd6..d37646e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.6.2" +ENV SERVICE_VERSION="1.6.3" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 35e949018256608d06fa4d9845b48476180ad7fd Mon Sep 17 00:00:00 2001 From: Paolo Martinoli <81316809+ugho16@users.noreply.github.com> Date: Fri, 20 Jan 2023 12:40:02 +0100 Subject: [PATCH 050/172] Added more users (#145) --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5882935..5d04fdb5 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,11 @@ Rönd natively allows you to build an RBAC solution based on Roles and Bindings Here is a list of awesome people using Rönd, if you're using it but do not appear in this list feel free to open a PR! - * [Mia-Platform](https://mia-platform.eu) + * [Cattolica Assicurazioni](https://www.cattolica.it/) + * [MDConcierge](https://www.mdconcierge.it/) * [Mia-Care](https://mia-care.io/) + * [Mia-Platform](https://mia-platform.eu) + * [PreviDigital](https://previdigital.com/) ## Local development From 92fb0ae3ee8e71ba06fd626929d3e229ea2224bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 11:49:10 +0100 Subject: [PATCH 051/172] chore(deps): bump github.com/getkin/kin-openapi from 0.112.0 to 0.113.0 (#146) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.112.0 to 0.113.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.112.0...v0.113.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 3 ++- go.sum | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 278a97e6..ae3cd50d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.8.0 - github.com/getkin/kin-openapi v0.112.0 + github.com/getkin/kin-openapi v0.113.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 @@ -51,6 +51,7 @@ require ( github.com/montanaflynn/stats v0.6.6 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/go.sum b/go.sum index 9bab0811..f413f8d4 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.112.0 h1:lnLXx3bAG53EJVI4E/w0N8i1Y/vUZUEsnrXkgnfn7/Y= -github.com/getkin/kin-openapi v0.112.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= +github.com/getkin/kin-openapi v0.113.0 h1:t9aNS/q5Agr7a55Jp1AuZ3sR2WzHESv3Dd2ys4UphsM= +github.com/getkin/kin-openapi v0.113.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -147,6 +147,8 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -385,6 +387,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= 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= @@ -487,6 +491,10 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/uptrace/bunrouter v1.0.19 h1:kdN1Nl/9RDq9eBPnjS6GvNKcgiAeMxdb9DbpslLndFg= github.com/uptrace/bunrouter v1.0.19/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= From eaece2231297a06be24711cc884999356881e0f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 11:24:19 +0100 Subject: [PATCH 052/172] chore(deps): bump docker/build-push-action from 3 to 4 (#147) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3...v4) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c87696c8..3ec23faa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -107,7 +107,7 @@ jobs: ${{ runner.os }}-buildx- - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . push: true From 970109257a255066cc4e250f4e434ac9783f80a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 16:11:18 +0100 Subject: [PATCH 053/172] chore(deps): bump github.com/getkin/kin-openapi from 0.113.0 to 0.114.0 (#149) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.113.0 to 0.114.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.113.0...v0.114.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ae3cd50d..4767933f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.8.0 - github.com/getkin/kin-openapi v0.113.0 + github.com/getkin/kin-openapi v0.114.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index f413f8d4..f6fbd345 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.113.0 h1:t9aNS/q5Agr7a55Jp1AuZ3sR2WzHESv3Dd2ys4UphsM= -github.com/getkin/kin-openapi v0.113.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8= +github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 9e7b6f6d989a84965969c3fe6b9506a94bb225f3 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Fri, 10 Feb 2023 13:46:50 +0100 Subject: [PATCH 054/172] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d04fdb5..437bc033 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ For local development you need to have Go installed locally, checkout the [go.mo make test ``` -Please note that in order to run tests you need Docker to be installed, since tests need a local instance of MongoDB to be up and running `make tests` takes care of it by creating a new `mongo` container. +Please note that in order to run tests you need Docker to be installed; tests need a local instance of MongoDB to be up and running, the `make test` command will take care of it by creating a new `mongodb` container. The container is auomatically removed at the end of tests; if it remains leaked simply run `make clean`. ### Contributing From 7c04c1cf82ee1a7b0fabb516611421641b1dce69 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Fri, 10 Feb 2023 14:46:08 +0100 Subject: [PATCH 055/172] fix: sec warning for defer response body close (#151) * defer resp body close * defer resp body close * fix: handle error * fix: typo --- openapi/openapi_utils.go | 11 ++++++++--- openapi/openapi_utils_test.go | 12 +++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 3e681d2a..927091a9 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -267,12 +267,17 @@ func deserializeSpec(spec []byte, errorWrapper error) (*OpenAPISpec, error) { return &oas, nil } -func fetchOpenAPI(url string) (*OpenAPISpec, error) { +func fetchOpenAPI(log *logrus.Logger, url string) (*OpenAPISpec, error) { resp, err := http.DefaultClient.Get(url) if err != nil { return nil, fmt.Errorf("%w: %s", ErrRequestFailed, err) } - defer resp.Body.Close() + defer func() { + err := resp.Body.Close() + if err != nil { + log.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed response body close") + } + }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%w: invalid status code %d", ErrRequestFailed, resp.StatusCode) @@ -309,7 +314,7 @@ func LoadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariable var oas *OpenAPISpec documentationURL := fmt.Sprintf("%s://%s%s", HTTPScheme, env.TargetServiceHost, env.TargetServiceOASPath) for { - fetchedOAS, err := fetchOpenAPI(documentationURL) + fetchedOAS, err := fetchOpenAPI(log, documentationURL) if err != nil { log.WithFields(logrus.Fields{ "targetServiceHost": env.TargetServiceHost, diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index ff6ebb71..dca04e47 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -27,6 +27,8 @@ import ( ) func TestFetchOpenAPI(t *testing.T) { + log, _ := test.NewNullLogger() + t.Run("fetches json OAS", func(t *testing.T) { defer gock.Off() @@ -37,7 +39,7 @@ func TestFetchOpenAPI(t *testing.T) { url := "http://localhost:3000/documentation/json" - openApiSpec, err := fetchOpenAPI(url) + openApiSpec, err := fetchOpenAPI(log, url) require.True(t, gock.IsDone(), "Mock has not been invoked") require.NoError(t, err, "unexpected error") @@ -84,7 +86,7 @@ func TestFetchOpenAPI(t *testing.T) { t.Run("request execution fails for invalid URL", func(t *testing.T) { url := "http://invalidUrl.com" - _, err := fetchOpenAPI(url) + _, err := fetchOpenAPI(log, url) t.Logf("Expected error occurred: %s", err.Error()) require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") @@ -93,7 +95,7 @@ func TestFetchOpenAPI(t *testing.T) { t.Run("request execution fails for invalid URL syntax", func(t *testing.T) { url := " http://url with a tab.com" - _, err := fetchOpenAPI(url) + _, err := fetchOpenAPI(log, url) t.Logf("Expected error occurred: %s", err.Error()) require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") @@ -109,7 +111,7 @@ func TestFetchOpenAPI(t *testing.T) { url := "http://localhost:3000/documentation/json" - _, err := fetchOpenAPI(url) + _, err := fetchOpenAPI(log, url) t.Logf("Expected error occurred: %s", err.Error()) require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") @@ -124,7 +126,7 @@ func TestFetchOpenAPI(t *testing.T) { url := "http://localhost:3000/documentation/json" - _, err := fetchOpenAPI(url) + _, err := fetchOpenAPI(log, url) t.Logf("Expected error occurred: %s", err.Error()) require.True(t, errors.Is(err, ErrRequestFailed), "unexpected error") From 59ab505b470e895d14e65758697cbd1e3d6147b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 23:44:27 +0100 Subject: [PATCH 056/172] chore(deps): bump github.com/open-policy-agent/opa from 0.48.0 to 0.49.0 (#150) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.48.0 to 0.49.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.48.0...v0.49.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 3 ++- go.sum | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 4767933f..2f29eb1b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.48.0 + github.com/open-policy-agent/opa v0.49.0 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 @@ -33,6 +33,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect diff --git a/go.sum b/go.sum index f6fbd345..adc56cf0 100644 --- a/go.sum +++ b/go.sum @@ -206,7 +206,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -376,8 +377,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.48.0 h1:s2K823yohAUu/HB4MOPWDhBh88JMKQv7uTr6S89fbM0= -github.com/open-policy-agent/opa v0.48.0/go.mod h1:CsQcksP+qGBxO9oEBj1NnZqKcjgjmTJbRNTzjZB/DXQ= +github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k= +github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 1672f525e2d6ca9ce6b4999a8d0c198d4ed4a5f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:11:08 +0100 Subject: [PATCH 057/172] chore(deps): bump github.com/uptrace/bunrouter from 1.0.19 to 1.0.20 (#152) Bumps [github.com/uptrace/bunrouter](https://github.com/uptrace/bunrouter) from 1.0.19 to 1.0.20. - [Release notes](https://github.com/uptrace/bunrouter/releases) - [Changelog](https://github.com/uptrace/bunrouter/blob/master/CHANGELOG.md) - [Commits](https://github.com/uptrace/bunrouter/compare/v1.0.19...v1.0.20) --- updated-dependencies: - dependency-name: github.com/uptrace/bunrouter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2f29eb1b..7787fa86 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 - github.com/uptrace/bunrouter v1.0.19 + github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.1 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index adc56cf0..3b1a425f 100644 --- a/go.sum +++ b/go.sum @@ -496,8 +496,8 @@ github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/uptrace/bunrouter v1.0.19 h1:kdN1Nl/9RDq9eBPnjS6GvNKcgiAeMxdb9DbpslLndFg= -github.com/uptrace/bunrouter v1.0.19/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= +github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/Tp4c= +github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= From 06e0cd112f500fba45e967ecc1096c56750710b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:42:39 +0100 Subject: [PATCH 058/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.1 to 1.11.2 (#153) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.1 to 1.11.2. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.1...v1.11.2) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7787fa86..6308442f 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.1 + go.mongodb.org/mongo-driver v1.11.2 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 3b1a425f..b469434a 100644 --- a/go.sum +++ b/go.sum @@ -527,8 +527,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= -go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw= +go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= 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= From a82e6812b69e35f1e7fdfe2a9201d4ff2660b0c1 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:46:31 +0100 Subject: [PATCH 059/172] feat: add role name in types (#154) * feat: add role name in types * fix: tests --- internal/mongoclient/mongoclient_test.go | 16 ++++++++++++---- internal/testutils/mongodb.go | 4 ++++ types/rbactypes.go | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/internal/mongoclient/mongoclient_test.go b/internal/mongoclient/mongoclient_test.go index 2dce0996..af4b073e 100644 --- a/internal/mongoclient/mongoclient_test.go +++ b/internal/mongoclient/mongoclient_test.go @@ -251,22 +251,24 @@ func TestMongoCollections(t *testing.T) { expected := []types.Role{ { RoleID: "role1", + RoleName: "Role1", Permissions: []string{"permission1", "permission2", "foobar"}, CRUDDocumentState: "PUBLIC", }, { RoleID: "role3", + RoleName: "Role3", Permissions: []string{"permission3", "permission5", "console.project.view"}, CRUDDocumentState: "PUBLIC", }, { RoleID: "notUsedByAnyone", + RoleName: "Not Used By Anyone", Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, CRUDDocumentState: "PUBLIC", }, } - require.True(t, reflect.DeepEqual(result, expected), - "Error while getting permissions") + require.True(t, reflect.DeepEqual(result, expected), "Error while getting permissions") }) t.Run("retrieve all roles by id from mongo", func(t *testing.T) { @@ -299,11 +301,13 @@ func TestMongoCollections(t *testing.T) { expected := []types.Role{ { RoleID: "role1", + RoleName: "Role1", Permissions: []string{"permission1", "permission2", "foobar"}, CRUDDocumentState: "PUBLIC", }, { RoleID: "role3", + RoleName: "Role3", Permissions: []string{"permission3", "permission5", "console.project.view"}, CRUDDocumentState: "PUBLIC", }, @@ -343,6 +347,7 @@ func TestMongoFindOne(t *testing.T) { t.Run("finds a document", func(t *testing.T) { result, err := mongoClient.FindOne(context.Background(), "roles", map[string]interface{}{ "roleId": "role3", + "name": "Role3", }) require.NoError(t, err) resultMap := result.(map[string]interface{}) @@ -351,6 +356,7 @@ func TestMongoFindOne(t *testing.T) { delete(resultMap, "_id") require.Equal(t, map[string]interface{}{ "roleId": "role3", + "name": "Role3", "__STATE__": "PUBLIC", "permissions": []interface{}{ string("permission3"), @@ -399,9 +405,9 @@ func TestMongoFindMany(t *testing.T) { t.Run("finds multiple documents", func(t *testing.T) { result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ "$or": []map[string]interface{}{ - {"roleId": "role3"}, + {"roleId": "role3", "name": "Role3"}, {"roleId": "role9999"}, - {"roleId": "role6"}, + {"roleId": "role6", "name": "Role6"}, }, }) require.NoError(t, err) @@ -413,6 +419,7 @@ func TestMongoFindMany(t *testing.T) { delete(resultMap, "_id") require.Equal(t, map[string]interface{}{ "roleId": "role3", + "name": "Role3", "__STATE__": "PUBLIC", "permissions": []interface{}{ string("permission3"), @@ -427,6 +434,7 @@ func TestMongoFindMany(t *testing.T) { delete(result1Map, "_id") require.Equal(t, map[string]interface{}{ "roleId": "role6", + "name": "Role6", "__STATE__": "PRIVATE", "permissions": []interface{}{ string("permission3"), diff --git a/internal/testutils/mongodb.go b/internal/testutils/mongodb.go index 7e552cc8..15f3f0a3 100644 --- a/internal/testutils/mongodb.go +++ b/internal/testutils/mongodb.go @@ -127,21 +127,25 @@ func PopulateDBForTesting( roles := []interface{}{ types.Role{ RoleID: "role1", + RoleName: "Role1", Permissions: []string{"permission1", "permission2", "foobar"}, CRUDDocumentState: "PUBLIC", }, types.Role{ RoleID: "role3", + RoleName: "Role3", Permissions: []string{"permission3", "permission5", "console.project.view"}, CRUDDocumentState: "PUBLIC", }, types.Role{ RoleID: "role6", + RoleName: "Role6", Permissions: []string{"permission3", "permission5"}, CRUDDocumentState: "PRIVATE", }, types.Role{ RoleID: "notUsedByAnyone", + RoleName: "Not Used By Anyone", Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, CRUDDocumentState: "PUBLIC", }, diff --git a/types/rbactypes.go b/types/rbactypes.go index fa4121b8..f8343c68 100644 --- a/types/rbactypes.go +++ b/types/rbactypes.go @@ -57,6 +57,7 @@ type BindingCreateResponse struct { type Role struct { RoleID string `bson:"roleId" json:"roleId"` + RoleName string `bson:"name" json:"name"` CRUDDocumentState string `bson:"__STATE__" json:"-"` Permissions []string `bson:"permissions" json:"permissions"` } From 2d990faa135d9228cad718f54314f3bbd635157f Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Wed, 15 Feb 2023 11:47:20 +0100 Subject: [PATCH 060/172] Upgrade version to v1.6.4 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d37646e1..25513451 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.6.3" +ENV SERVICE_VERSION="1.6.4" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From a5431f94d3506ff46564c33762656496985185af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 15:27:44 +0100 Subject: [PATCH 061/172] chore(deps): bump github.com/open-policy-agent/opa from 0.49.0 to 0.49.1 (#156) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.49.0 to 0.49.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.49.0...v0.49.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6308442f..16ec5bd9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.49.0 + github.com/open-policy-agent/opa v0.49.1 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index b469434a..3ecf26fd 100644 --- a/go.sum +++ b/go.sum @@ -377,8 +377,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k= -github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w= +github.com/open-policy-agent/opa v0.49.1 h1:hXnKOmzVk8TyCTHEoHTZvYjAqwOBS9n+3mf4zmdDtP0= +github.com/open-policy-agent/opa v0.49.1/go.mod h1:Wvc+vpbXuYxhKk0uvhTxmDmZ3rjSXtqYJBtGAPNoJ6E= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 10becc42ee4c4c29c9c52da25bdc03b8d17c9d94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:40:42 +0100 Subject: [PATCH 062/172] chore(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#157) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 16ec5bd9..fe2e2b38 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.2 gopkg.in/h2non/gock.v1 v1.1.2 diff --git a/go.sum b/go.sum index 3ecf26fd..47e16203 100644 --- a/go.sum +++ b/go.sum @@ -481,8 +481,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From e3ff073e973eed977bee508164e3b8d5f5b52453 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:40:47 +0100 Subject: [PATCH 063/172] chore(deps): bump github.com/open-policy-agent/opa from 0.49.1 to 0.49.2 (#158) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.49.1 to 0.49.2. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.49.1...v0.49.2) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fe2e2b38..4beee59b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.49.1 + github.com/open-policy-agent/opa v0.49.2 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 47e16203..6e84c42e 100644 --- a/go.sum +++ b/go.sum @@ -377,8 +377,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.49.1 h1:hXnKOmzVk8TyCTHEoHTZvYjAqwOBS9n+3mf4zmdDtP0= -github.com/open-policy-agent/opa v0.49.1/go.mod h1:Wvc+vpbXuYxhKk0uvhTxmDmZ3rjSXtqYJBtGAPNoJ6E= +github.com/open-policy-agent/opa v0.49.2 h1:n8ntRq/yDWy+cmYaqSLrHXmrT3tX8WlK28vjFQdC6W8= +github.com/open-policy-agent/opa v0.49.2/go.mod h1:7L3lN5qe8xboRmEHxC5lGjo5KsRMdK+CCLiFoOCP7rU= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 2a3822814172c3974344d4548a37abf842194724 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 14:38:42 +0100 Subject: [PATCH 064/172] chore(deps): bump github.com/open-policy-agent/opa from 0.49.2 to 0.50.0 (#160) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.49.2 to 0.50.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.49.2...v0.50.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 4beee59b..7cc9a7b6 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.49.2 + github.com/open-policy-agent/opa v0.50.0 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 @@ -24,14 +24,14 @@ require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect @@ -77,8 +77,8 @@ require ( golang.org/x/crypto v0.4.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 6e84c42e..4cc2110a 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA 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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -118,7 +119,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -186,8 +187,9 @@ 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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -377,8 +379,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.49.2 h1:n8ntRq/yDWy+cmYaqSLrHXmrT3tX8WlK28vjFQdC6W8= -github.com/open-policy-agent/opa v0.49.2/go.mod h1:7L3lN5qe8xboRmEHxC5lGjo5KsRMdK+CCLiFoOCP7rU= +github.com/open-policy-agent/opa v0.50.0 h1:CBRj7lJ9DFDHvlx2SRP6uFOCD9ooxDdNW9fYK2IIW+0= +github.com/open-policy-agent/opa v0.50.0/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -635,7 +637,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 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= @@ -724,8 +726,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -738,8 +740,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 540a664dc11bdd20f436c1b8315e776167734701 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Mar 2023 09:32:19 +0100 Subject: [PATCH 065/172] chore(deps): bump github.com/getkin/kin-openapi from 0.114.0 to 0.115.0 (#162) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.114.0 to 0.115.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.114.0...v0.115.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7cc9a7b6..c0c028bc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.8.0 - github.com/getkin/kin-openapi v0.114.0 + github.com/getkin/kin-openapi v0.115.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index 4cc2110a..d49b65dd 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8= -github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU= +github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 2c01591970f493ed2f34c4ac4877babecd089896 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:53:37 +0100 Subject: [PATCH 066/172] chore(deps): bump actions/setup-go from 3 to 4 (#164) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ec23faa..ef90f7eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Use golang ${{ matrix.go_version }} - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go_version }} - name: Go version @@ -37,7 +37,7 @@ jobs: os: [ubuntu-latest] steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go_version }} - name: Run benchmark From cc9f0d286425e4a44920479c15b3280c3537b767 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 13:02:44 +0100 Subject: [PATCH 067/172] chore(deps): bump github.com/open-policy-agent/opa from 0.50.0 to 0.50.1 (#165) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.50.0 to 0.50.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.50.0...v0.50.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c0c028bc..9d42e9e0 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.50.0 + github.com/open-policy-agent/opa v0.50.1 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index d49b65dd..41c6b492 100644 --- a/go.sum +++ b/go.sum @@ -379,8 +379,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.50.0 h1:CBRj7lJ9DFDHvlx2SRP6uFOCD9ooxDdNW9fYK2IIW+0= -github.com/open-policy-agent/opa v0.50.0/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= +github.com/open-policy-agent/opa v0.50.1 h1:ZQOqmzTUjcdX7Bu6gnmWZ6ghFTAQI0rI1fR7AqaOW70= +github.com/open-policy-agent/opa v0.50.1/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From d39082f2920498798fdde676667022d011faff2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:21:48 +0100 Subject: [PATCH 068/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.2 to 1.11.3 (#166) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.2 to 1.11.3. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.2...v1.11.3) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9d42e9e0..1a8fe957 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.2 + go.mongodb.org/mongo-driver v1.11.3 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 41c6b492..d7dc98c3 100644 --- a/go.sum +++ b/go.sum @@ -530,8 +530,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw= -go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= 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= From 5a8d08b151323121a4229890b0df1b043ae9f7f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:04:49 +0100 Subject: [PATCH 069/172] chore(deps): bump github.com/open-policy-agent/opa from 0.50.1 to 0.50.2 (#168) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.50.1 to 0.50.2. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.50.1...v0.50.2) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a8fe957..8fa01e4c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.50.1 + github.com/open-policy-agent/opa v0.50.2 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index d7dc98c3..358c9052 100644 --- a/go.sum +++ b/go.sum @@ -379,8 +379,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.50.1 h1:ZQOqmzTUjcdX7Bu6gnmWZ6ghFTAQI0rI1fR7AqaOW70= -github.com/open-policy-agent/opa v0.50.1/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= +github.com/open-policy-agent/opa v0.50.2 h1:iD2kKLFkflgSCTMtrC/3jLmOQ7IWyDXMg6+VQA0tSC0= +github.com/open-policy-agent/opa v0.50.2/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 915415919d1ba3e72765649a46c86e441388f6ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:13:09 +0100 Subject: [PATCH 070/172] chore(deps): bump github.com/samber/lo from 1.37.0 to 1.38.1 (#167) Bumps [github.com/samber/lo](https://github.com/samber/lo) from 1.37.0 to 1.38.1. - [Release notes](https://github.com/samber/lo/releases) - [Changelog](https://github.com/samber/lo/blob/master/CHANGELOG.md) - [Commits](https://github.com/samber/lo/compare/v1.37.0...v1.38.1) --- updated-dependencies: - dependency-name: github.com/samber/lo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8fa01e4c..249b7044 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.50.2 github.com/prometheus/client_golang v1.14.0 - github.com/samber/lo v1.37.0 + github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 diff --git a/go.sum b/go.sum index 358c9052..cd5df00d 100644 --- a/go.sum +++ b/go.sum @@ -444,8 +444,8 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= -github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= From dac20dcb8e3fe48b33ce4d0085aefe9b8b971239 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:29:23 +0200 Subject: [PATCH 071/172] chore(deps): bump github.com/open-policy-agent/opa from 0.50.2 to 0.51.0 (#169) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.50.2 to 0.51.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.50.2...v0.51.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 249b7044..26299901 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.50.2 + github.com/open-policy-agent/opa v0.51.0 github.com/prometheus/client_golang v1.14.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index cd5df00d..7dec6418 100644 --- a/go.sum +++ b/go.sum @@ -379,8 +379,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.50.2 h1:iD2kKLFkflgSCTMtrC/3jLmOQ7IWyDXMg6+VQA0tSC0= -github.com/open-policy-agent/opa v0.50.2/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= +github.com/open-policy-agent/opa v0.51.0 h1:2hS5xhos8HtkN+mgpqMhNJSFtn/1n/h3wh+AeTPJg6Q= +github.com/open-policy-agent/opa v0.51.0/go.mod h1:OjmwLfXdeR7skSxrt8Yd3ScXTqPxyJn7GeTRJrcEerU= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 0eaa30f0baa67d57d6b94f6827f14673a79558be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:41:00 +0200 Subject: [PATCH 072/172] chore(deps): bump github.com/prometheus/client_golang (#172) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 9 ++++----- go.sum | 36 +++++++++--------------------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index 26299901..814c7ba4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.51.0 - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.15.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 @@ -33,7 +33,6 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect @@ -56,8 +55,8 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect @@ -79,7 +78,7 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 7dec6418..52e528da 100644 --- a/go.sum +++ b/go.sum @@ -76,7 +76,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA 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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -135,12 +134,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -209,7 +206,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -295,7 +291,6 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -315,7 +310,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -366,7 +361,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -406,11 +400,9 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -422,17 +414,15 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -633,10 +623,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 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= @@ -647,8 +634,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -722,14 +707,11 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -899,8 +881,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 5f45706cde437c78f37ef69f354cb41df59436e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:41:13 +0200 Subject: [PATCH 073/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.3 to 1.11.4 (#170) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.3 to 1.11.4. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.3...v1.11.4) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 814c7ba4..91eabee2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.3 + go.mongodb.org/mongo-driver v1.11.4 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 52e528da..74f0c126 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= -go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= 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= From c73bd0db883bff5a0efd13fa94744289a6c48531 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:45:51 +0200 Subject: [PATCH 074/172] chore(deps): bump github.com/davidebianchi/gswagger from 0.8.0 to 0.9.0 (#173) Bumps [github.com/davidebianchi/gswagger](https://github.com/davidebianchi/gswagger) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/davidebianchi/gswagger/releases) - [Changelog](https://github.com/davidebianchi/gswagger/blob/main/CHANGELOG.md) - [Commits](https://github.com/davidebianchi/gswagger/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: github.com/davidebianchi/gswagger dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 91eabee2..9d9ba5b2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 - github.com/davidebianchi/gswagger v0.8.0 + github.com/davidebianchi/gswagger v0.9.0 github.com/getkin/kin-openapi v0.115.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 @@ -38,7 +38,7 @@ require ( github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.15.11 // indirect + github.com/klauspost/compress v1.16.3 // indirect github.com/knadh/koanf v1.4.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -73,10 +73,10 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/crypto v0.4.0 // indirect + golang.org/x/crypto v0.7.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/text v0.8.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 74f0c126..1df17915 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidebianchi/go-jsonclient v1.3.0 h1:6579uXOHEDcClHK8b3j/xWbQV4Khpn7aMcH7tOmRjwY= github.com/davidebianchi/go-jsonclient v1.3.0/go.mod h1:lIOd35jxGfGENA/M0s1klHJgagIDj2KM1O6A/V5vpPI= -github.com/davidebianchi/gswagger v0.8.0 h1:szFH4hYEyVPfhcpggWQgKDNnIek5rXXIW04EvNlG/M0= -github.com/davidebianchi/gswagger v0.8.0/go.mod h1:coXlWOGJDhZ1Zm/1ORvELzejxewFJIeB6DDD1vYo9zU= +github.com/davidebianchi/gswagger v0.9.0 h1:wztdl5oSQ0PGgrbhivPr71VNIf4QUKQCeOffbd6lnTE= +github.com/davidebianchi/gswagger v0.9.0/go.mod h1:Ge69aGQIAWZs63UzaStPfqGT5u/gEXLsQ6vTM3gzDCE= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -299,8 +299,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/knadh/koanf v0.6.0/go.mod h1:HGb//qrjEExYwYqqoxit9S1rvFU+YCx1jz3GtcdZEy8= github.com/knadh/koanf v1.4.3 h1:rSJcSH5LSFhvzBRsAYfT3k7eLP0I4UxeZqjtAatk+wc= github.com/knadh/koanf v1.4.3/go.mod h1:5FAkuykKXZvLqhAbP4peWgM5CTcZmn7L1d27k/a+kfg= @@ -546,8 +546,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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= @@ -709,8 +709,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From fa044ae435946f79256910b8d6ca4c01c500fbd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 16:34:11 +0200 Subject: [PATCH 075/172] chore(deps): bump github.com/getkin/kin-openapi from 0.115.0 to 0.116.0 (#174) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.115.0 to 0.116.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.115.0...v0.116.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9d9ba5b2..8f1f0956 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.9.0 - github.com/getkin/kin-openapi v0.115.0 + github.com/getkin/kin-openapi v0.116.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index 1df17915..14151b49 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU= -github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E= +github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 9665ceed1eb482c6f1511d8ff00259fb90bd1db7 Mon Sep 17 00:00:00 2001 From: Giovanna Monti <60603265+GiovannaMonti@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:59:23 +0200 Subject: [PATCH 076/172] fix: add permissions field to binding (#175) Co-authored-by: Giovanna Monti --- CHANGELOG.md | 4 ++++ service/standalone_apis.go | 9 +++++---- service/standalone_apis_test.go | 21 ++++++++++----------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa9fb4ec..2529573c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +### Fixed + +- correctly manage binding permissions in grant api + ## 1.5.0 - 19-10-2022 ## 1.4.3 - 12-10-2022 diff --git a/service/standalone_apis.go b/service/standalone_apis.go index 62a9993b..3e096e2e 100644 --- a/service/standalone_apis.go +++ b/service/standalone_apis.go @@ -194,10 +194,11 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { } bindingToCreate := types.Binding{ - BindingID: uuid.New().String(), - Groups: reqBody.Groups, - Roles: reqBody.Roles, - Subjects: reqBody.Subjects, + BindingID: uuid.New().String(), + Groups: reqBody.Groups, + Permissions: reqBody.Permissions, + Roles: reqBody.Roles, + Subjects: reqBody.Subjects, } if resourceType != "" { diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 38bcd5e4..8c520bf5 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -555,10 +555,11 @@ func TestGrantHandler(t *testing.T) { defer gock.Flush() reqBody := setupGrantRequestBody(t, GrantRequestBody{ - Subjects: []string{"piero"}, - ResourceID: "projectID", - Roles: []string{"editor"}, - Groups: []string{"test-group"}, + Subjects: []string{"piero"}, + ResourceID: "projectID", + Roles: []string{"editor"}, + Permissions: []string{"test-permission"}, + Groups: []string{"test-group"}, }) gock.DisableNetworking() @@ -569,9 +570,6 @@ func TestGrantHandler(t *testing.T) { bodyBytes, err := io.ReadAll(req.Body) require.Nil(t, err, "unxpected error reading body in matcher") - containsNull := strings.Contains(string(bodyBytes), `"permissions":null`) - require.False(t, containsNull, "unexpected null found") - err = json.Unmarshal(bodyBytes, &body) require.Nil(t, err, "unxpected error parsing body in matcher") @@ -580,10 +578,11 @@ func TestGrantHandler(t *testing.T) { body.BindingID = "REDACTED" require.Equal(t, types.Binding{ - BindingID: "REDACTED", - Groups: []string{"test-group"}, - Roles: []string{"editor"}, - Subjects: []string{"piero"}, + BindingID: "REDACTED", + Groups: []string{"test-group"}, + Permissions: []string{"test-permission"}, + Roles: []string{"editor"}, + Subjects: []string{"piero"}, Resource: &types.Resource{ ResourceType: "my-resource", ResourceID: "projectID", From ddf23d7487af38071d516989712ffe5c4c831ac3 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:05:31 +0200 Subject: [PATCH 077/172] fix: deprecation CHANGELOG notice --- CHANGELOG.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2529573c..207e1080 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,7 @@ -> ⚠ This file is deprecated. Releases after 1.4.3 use GitHub built-in releases to track changes ⚠ +> ⚠ This file is deprecated. Releases after 1.4.3 use GitHub built-in releases to track changes ⚠ # CHANGELOG -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -### Fixed - -- correctly manage binding permissions in grant api - ## 1.5.0 - 19-10-2022 ## 1.4.3 - 12-10-2022 From 42442fef31b2070f5cf192d66dba2afb8b49c94e Mon Sep 17 00:00:00 2001 From: Giovanna Monti <60603265+GiovannaMonti@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:08:35 +0200 Subject: [PATCH 078/172] feat: support in operator in query (#176) * feat: support in operator in query * print * add in operator conversion * fix: support both slices and values in * fix: empty newline --------- Co-authored-by: Federico Maggi Co-authored-by: Giovanna Monti --- internal/opatranslator/mongo.go | 18 ++++++ service/handler_test.go | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/internal/opatranslator/mongo.go b/internal/opatranslator/mongo.go index 5f160001..7abb4c02 100644 --- a/internal/opatranslator/mongo.go +++ b/internal/opatranslator/mongo.go @@ -15,6 +15,8 @@ package opatranslator import ( + "reflect" + "go.mongodb.org/mongo-driver/bson" ) @@ -30,6 +32,8 @@ const ( EqOp = "eq" EqualOp = "equal" NeqOp = "neq" + // https://github.com/open-policy-agent/opa/blob/main/ast/builtins.go#L345 + InOp = "internal.member_2" ) var rangeOperatorStrategies = map[string]func(pipeline *[]bson.M, fieldName string, fieldValue interface{}){ @@ -40,6 +44,7 @@ var rangeOperatorStrategies = map[string]func(pipeline *[]bson.M, fieldName stri EqOp: HandleEquals, EqualOp: HandleEquals, NeqOp: HandleNotEquals, + InOp: HandleIn, } func HandleOperations(operation string, pipeline *[]bson.M, fieldName string, fieldValue interface{}) bool { @@ -56,6 +61,19 @@ func HandleEquals(pipeline *[]bson.M, fieldName string, fieldValue interface{}) *pipeline = append(*pipeline, filter) } +// Parse the in operator into equivalent mongo query. +func HandleIn(pipeline *[]bson.M, fieldName string, fieldValue interface{}) { + reflectValue := reflect.ValueOf(fieldValue) + + mongoInOperatorValue := fieldValue + if reflectValue.Kind() != reflect.Slice { + mongoInOperatorValue = []interface{}{fieldValue} + } + + filter := bson.M{fieldName: bson.M{"$in": mongoInOperatorValue}} + *pipeline = append(*pipeline, filter) +} + // Parse the != into equivalent mongo query. func HandleNotEquals(pipeline *[]bson.M, fieldName string, fieldValue interface{}) { filter := bson.M{fieldName: bson.M{"$ne": fieldValue}} diff --git a/service/handler_test.go b/service/handler_test.go index 7bc791c5..2eaed118 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -816,6 +816,111 @@ allow { require.Equal(t, expectedQuery, filterQuery) }) + t.Run("sends filter query with $in", func(t *testing.T) { + t.Run("as array", func(t *testing.T) { + + policy := `package policies +import future.keywords.in + +allow { + input.request.method == "GET" + + employee := data.resources[_] + ["member_test"] in employee.membership +} + +allow { + input.request.method == "GET" + input.request.path == "/api" + employee := data.resources[_] + employee.salary > 0 +} +` + + mockBodySting := "I am a body" + + body := strings.NewReader(mockBodySting) + + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + require.NoError(t, err, "Unexpected error") + + ctx := createContext(t, + context.Background(), + env, + nil, + mockRondConfigWithQueryGen, + &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + partialEvaluators, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set("Content-Type", "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + filterQuery := r.Header.Get("rowfilterquery") + expectedQuery := `{"$or":[{"$and":[{"membership":{"$in":["member_test"]}}]},{"$and":[{"salary":{"$gt":0}}]}]}` + require.Equal(t, expectedQuery, filterQuery) + }) + + t.Run("as single item", func(t *testing.T) { + + policy := `package policies +import future.keywords.in + +allow { + input.request.method == "GET" + groups := {"groupid":123} + + query := data.resources[_] + query.membership in groups.groupid +} + +allow { + input.request.method == "GET" + input.request.path == "/api" + employee := data.resources[_] + employee.salary > 0 +} +` + + mockBodySting := "I am a body" + + body := strings.NewReader(mockBodySting) + + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + require.NoError(t, err, "Unexpected error") + + ctx := createContext(t, + context.Background(), + env, + nil, + mockRondConfigWithQueryGen, + &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, + partialEvaluators, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set("Content-Type", "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + filterQuery := r.Header.Get("rowfilterquery") + expectedQuery := `{"$or":[{"$and":[{"membership":{"$in":[123]}}]},{"$and":[{"salary":{"$gt":0}}]}]}` + require.Equal(t, expectedQuery, filterQuery) + }) + }) + t.Run("sends empty filter query", func(t *testing.T) { policy := `package policies allow { From 84cda8a6cce7e266f3d7305203b810892510549e Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:47:25 +0200 Subject: [PATCH 079/172] refactor: handler_test in operator order (#177) --- service/handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/handler_test.go b/service/handler_test.go index 2eaed118..6aa08b5d 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -878,7 +878,7 @@ allow { groups := {"groupid":123} query := data.resources[_] - query.membership in groups.groupid + groups.groupid in query.membership } allow { From 6115bd5cec6c48ac35d442548501008247e829ff Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Thu, 27 Apr 2023 17:49:43 +0200 Subject: [PATCH 080/172] Upgrade version to v1.7.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 25513451..55e1749a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.6.4" +ENV SERVICE_VERSION="1.7.0" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 7dfcac34202d568b26f0a67fe3323c2112d0146c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 13:40:08 +0200 Subject: [PATCH 081/172] chore(deps): bump github.com/open-policy-agent/opa from 0.51.0 to 0.52.0 (#178) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.51.0 to 0.52.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.51.0...v0.52.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 8f1f0956..66164506 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.51.0 + github.com/open-policy-agent/opa v0.52.0 github.com/prometheus/client_golang v1.15.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.0 @@ -77,7 +77,7 @@ require ( golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 14151b49..4b399ae6 100644 --- a/go.sum +++ b/go.sum @@ -373,8 +373,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.51.0 h1:2hS5xhos8HtkN+mgpqMhNJSFtn/1n/h3wh+AeTPJg6Q= -github.com/open-policy-agent/opa v0.51.0/go.mod h1:OjmwLfXdeR7skSxrt8Yd3ScXTqPxyJn7GeTRJrcEerU= +github.com/open-policy-agent/opa v0.52.0 h1:Rv3F+VCDqsufaiYy/3S9/Iuk0yfcREK4iZmWbNsKZjA= +github.com/open-policy-agent/opa v0.52.0/go.mod h1:2n99s7WY/BXZUWUOq10JdTgK+G6XM4FYGoe7kQ5Vg0s= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -430,7 +430,7 @@ github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8d github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -624,7 +624,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 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= @@ -722,8 +722,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 47b54e7c209d19b62114dade48d538896fae8b6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 19:01:58 +0200 Subject: [PATCH 082/172] chore(deps): bump github.com/prometheus/client_golang (#179) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.0 to 1.15.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.15.0...v1.15.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 66164506..b2ac9c17 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.52.0 - github.com/prometheus/client_golang v1.15.0 + github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 4b399ae6..3a9ee208 100644 --- a/go.sum +++ b/go.sum @@ -401,8 +401,8 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From 008df09821171b20167f80150ebcd7b35a6320b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 19:02:18 +0200 Subject: [PATCH 083/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.4 to 1.11.5 (#181) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.4 to 1.11.5. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.4...v1.11.5) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b2ac9c17..11663390 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.4 + go.mongodb.org/mongo-driver v1.11.5 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 3a9ee208..8759135b 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= -go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.mongodb.org/mongo-driver v1.11.5 h1:CLrb1a22ddViSnnPCzGT4+PGrOJsUXI/9SWj8N1uHCM= +go.mongodb.org/mongo-driver v1.11.5/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= 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= From dbab72382d3734690f52f0e618ccb048c939f47c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 19:09:48 +0200 Subject: [PATCH 084/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.5 to 1.11.6 (#182) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.5 to 1.11.6. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.5...v1.11.6) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 11663390..d2c11221 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.5 + go.mongodb.org/mongo-driver v1.11.6 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 8759135b..1ec8d66d 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.5 h1:CLrb1a22ddViSnnPCzGT4+PGrOJsUXI/9SWj8N1uHCM= -go.mongodb.org/mongo-driver v1.11.5/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o= +go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= 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= From 8f1485e558ebc7f5c4ffe1098c565f15140670fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 19:20:01 +0200 Subject: [PATCH 085/172] chore(deps): bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 (#183) Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: github.com/sirupsen/logrus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d2c11221..ccc3cac1 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/open-policy-agent/opa v0.52.0 github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 - github.com/sirupsen/logrus v1.9.0 + github.com/sirupsen/logrus v1.9.1 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.6 diff --git a/go.sum b/go.sum index 1ec8d66d..f66de9fa 100644 --- a/go.sum +++ b/go.sum @@ -441,8 +441,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ= +github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= From eb6bf100f363582a82ed7536fe4bd6939aaea638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 16:04:06 +0200 Subject: [PATCH 086/172] chore(deps): bump github.com/getkin/kin-openapi from 0.116.0 to 0.117.0 (#185) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.116.0 to 0.117.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.116.0...v0.117.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ccc3cac1..61e2bac9 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/go-jsonclient v1.3.0 github.com/davidebianchi/gswagger v0.9.0 - github.com/getkin/kin-openapi v0.116.0 + github.com/getkin/kin-openapi v0.117.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index f66de9fa..464ba37f 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E= -github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.117.0 h1:QT2DyGujAL09F4NrKDHJGsUoIprlIcFVHWDVDcUFE8A= +github.com/getkin/kin-openapi v0.117.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 8936c96e64dd946d9dd8d92c86f4dfea2701ed92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 16:04:25 +0200 Subject: [PATCH 087/172] chore(deps): bump github.com/sirupsen/logrus from 1.9.1 to 1.9.2 (#184) Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.1 to 1.9.2. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.9.1...v1.9.2) --- updated-dependencies: - dependency-name: github.com/sirupsen/logrus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 61e2bac9..07b1d0b5 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/open-policy-agent/opa v0.52.0 github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 - github.com/sirupsen/logrus v1.9.1 + github.com/sirupsen/logrus v1.9.2 github.com/stretchr/testify v1.8.2 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.6 diff --git a/go.sum b/go.sum index 464ba37f..71c59463 100644 --- a/go.sum +++ b/go.sum @@ -441,8 +441,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ= -github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= From 7bcb6225475f2dbccc76e4de72e2ed3001808355 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 17:41:51 +0200 Subject: [PATCH 088/172] chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#186) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 07b1d0b5..e094e8f9 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.2 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.6 gopkg.in/h2non/gock.v1 v1.1.2 diff --git a/go.sum b/go.sum index 71c59463..a674701b 100644 --- a/go.sum +++ b/go.sum @@ -474,8 +474,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From fa2e2b20fdbf90bddc45be590aacd2f6e7cbdcdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 11:14:18 +0200 Subject: [PATCH 089/172] chore(deps): bump github.com/open-policy-agent/opa from 0.52.0 to 0.53.0 (#187) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.52.0 to 0.53.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.52.0...v0.53.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 +++++++++--- go.sum | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index e094e8f9..f8fd5188 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.52.0 + github.com/open-policy-agent/opa v0.53.0 github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.2 @@ -28,6 +28,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // 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 + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect @@ -73,10 +76,13 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/sdk v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index a674701b..afedc129 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,7 @@ 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/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= 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= @@ -117,6 +118,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= @@ -131,6 +133,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -138,6 +142,11 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -156,7 +165,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -233,7 +242,9 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= @@ -373,8 +384,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.52.0 h1:Rv3F+VCDqsufaiYy/3S9/Iuk0yfcREK4iZmWbNsKZjA= -github.com/open-policy-agent/opa v0.52.0/go.mod h1:2n99s7WY/BXZUWUOq10JdTgK+G6XM4FYGoe7kQ5Vg0s= +github.com/open-policy-agent/opa v0.53.0 h1:zC/0sI+Gof5/oiFNS3DmoJa11D0m0InZeDhZyzi+l6E= +github.com/open-policy-agent/opa v0.53.0/go.mod h1:j3wl8FqSz/+u33Scl72Ms2wxkZx4yZPdqSCrOqBqdsA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -529,6 +540,18 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0 h1:yt2NKzK7Vyo6h0+X8BA4FpreZQTlVEIarnsBP/H5mzs= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -624,7 +647,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 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= @@ -645,8 +668,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -709,8 +732,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -848,6 +871,7 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -869,6 +893,7 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= 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= From 774a4f51440dcf05d09495484c71d070ab59d5e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 17:22:51 +0200 Subject: [PATCH 090/172] chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#188) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f8fd5188..36c0e0d4 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.2 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.8.4 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.6 gopkg.in/h2non/gock.v1 v1.1.2 diff --git a/go.sum b/go.sum index afedc129..52031668 100644 --- a/go.sum +++ b/go.sum @@ -485,8 +485,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From ff3033b883b0de618ccb475249e436f113808e97 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Wed, 31 May 2023 10:58:57 +0200 Subject: [PATCH 091/172] test: added test case with validation of nested field usage in filter query generation policy (#189) * test: added test case with validation of nested field usage in filter query generation policy * test --- internal/utils/general_test.go | 51 ++++++++++++++++++++++++++++++ internal/utils/testfile.txt | 1 + service/handler_test.go | 58 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 internal/utils/general_test.go create mode 100644 internal/utils/testfile.txt diff --git a/internal/utils/general_test.go b/internal/utils/general_test.go new file mode 100644 index 00000000..7bbf43d2 --- /dev/null +++ b/internal/utils/general_test.go @@ -0,0 +1,51 @@ +package utils + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFilterList(t *testing.T) { + input := []string{"a", "b", "a"} + filter := []string{"a", "c"} + out := FilterList(input, filter) + require.Equal(t, []string{"b"}, out) +} + +func TestSanitizeString(t *testing.T) { + testCases := []struct { + input string + expected string + }{ + {input: "test", expected: "test"}, + {input: "new\nline", expected: "newline"}, + {input: "carriage\rreturn", expected: "carriagereturn"}, + {input: "new\nline\ncarriage\rreturn\n\r", expected: "newlinecarriagereturn"}, + } + + for i, testCase := range testCases { + t.Run( + fmt.Sprintf("testcase #%d %s -> %s", i, testCase.input, testCase.expected), + func(t *testing.T) { + out := SanitizeString(testCase.input) + require.Equal(t, testCase.expected, out) + }, + ) + } +} + +func TestReadFile(t *testing.T) { + t.Run("ok", func(t *testing.T) { + content, err := ReadFile("./testfile.txt") + require.NoError(t, err) + require.Equal(t, "the-content", string(content)) + }) + + t.Run("ko", func(t *testing.T) { + content, err := ReadFile("./missingfile.txt") + require.Error(t, err) + require.Nil(t, content) + }) +} diff --git a/internal/utils/testfile.txt b/internal/utils/testfile.txt new file mode 100644 index 00000000..764d9fc1 --- /dev/null +++ b/internal/utils/testfile.txt @@ -0,0 +1 @@ +the-content \ No newline at end of file diff --git a/service/handler_test.go b/service/handler_test.go index 6aa08b5d..31432464 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -323,6 +323,64 @@ allow { require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) + t.Run("sends filter query with nested data", func(t *testing.T) { + policy := `package policies +allow { + employee := data.resources[_] + employee.data.manager == "manager_test" +} +` + + invoked := false + mockBodySting := "I am a body" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + defer r.Body.Close() + buf, err := io.ReadAll(r.Body) + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") + filterQuery := r.Header.Get("rowfilterquery") + expectedQuery := `{"$or":[{"$and":[{"data.manager":{"$eq":"manager_test"}}]}]}` + require.Equal(t, expectedQuery, filterQuery) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Mocked Backend Body Example")) + })) + defer server.Close() + + body := strings.NewReader(mockBodySting) + + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + require.NoError(t, err, "Unexpected error") + + serverURL, _ := url.Parse(server.URL) + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + nil, + mockRondConfigWithQueryGen, + OPAModuleConfig, + partialEvaluators, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + buf, err := io.ReadAll(w.Body) + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") + }) + t.Run("sends empty filter query", func(t *testing.T) { policy := `package policies allow { From 1b6274fad9342cd9ba1a81317b316ee2e60dd618 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 11:26:36 +0200 Subject: [PATCH 092/172] chore(deps): bump github.com/davidebianchi/go-jsonclient (#191) Bumps [github.com/davidebianchi/go-jsonclient](https://github.com/davidebianchi/go-jsonclient) from 1.3.0 to 1.5.0. - [Changelog](https://github.com/davidebianchi/go-jsonclient/blob/master/CHANGELOG.md) - [Commits](https://github.com/davidebianchi/go-jsonclient/compare/v1.3.0...v1.5.0) --- updated-dependencies: - dependency-name: github.com/davidebianchi/go-jsonclient dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 36c0e0d4..ca3f86b8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/rond-authz/rond go 1.18 require ( - github.com/davidebianchi/go-jsonclient v1.3.0 + github.com/davidebianchi/go-jsonclient v1.5.0 github.com/davidebianchi/gswagger v0.9.0 github.com/getkin/kin-openapi v0.117.0 github.com/google/uuid v1.3.0 diff --git a/go.sum b/go.sum index 52031668..8537df1d 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidebianchi/go-jsonclient v1.3.0 h1:6579uXOHEDcClHK8b3j/xWbQV4Khpn7aMcH7tOmRjwY= -github.com/davidebianchi/go-jsonclient v1.3.0/go.mod h1:lIOd35jxGfGENA/M0s1klHJgagIDj2KM1O6A/V5vpPI= +github.com/davidebianchi/go-jsonclient v1.5.0 h1:PVDunAF/6c30D2SSx711efsrUP3hhml+uGT6oD3lzVY= +github.com/davidebianchi/go-jsonclient v1.5.0/go.mod h1:lXd/hkx23H590Dod74j5GsmpgxF8RIAGZDt1P5+2mqo= github.com/davidebianchi/gswagger v0.9.0 h1:wztdl5oSQ0PGgrbhivPr71VNIf4QUKQCeOffbd6lnTE= github.com/davidebianchi/gswagger v0.9.0/go.mod h1:Ge69aGQIAWZs63UzaStPfqGT5u/gEXLsQ6vTM3gzDCE= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= From f8d829e45ab5c502346afce580f5a847c3bdc6a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:33:10 +0200 Subject: [PATCH 093/172] chore(deps): bump github.com/sirupsen/logrus from 1.9.2 to 1.9.3 (#193) Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.2 to 1.9.3. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.9.2...v1.9.3) --- updated-dependencies: - dependency-name: github.com/sirupsen/logrus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ca3f86b8..590d9bbc 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/open-policy-agent/opa v0.53.0 github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 - github.com/sirupsen/logrus v1.9.2 + github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 github.com/uptrace/bunrouter v1.0.20 go.mongodb.org/mongo-driver v1.11.6 diff --git a/go.sum b/go.sum index 8537df1d..31bf1f47 100644 --- a/go.sum +++ b/go.sum @@ -452,8 +452,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= From 832974ebd169ca8530ed0cfd950f71f2d610c3c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:01:53 +0000 Subject: [PATCH 094/172] chore(deps): bump github.com/open-policy-agent/opa from 0.53.0 to 0.53.1 Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.53.0 to 0.53.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.53.0...v0.53.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 590d9bbc..0409b19c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.53.0 + github.com/open-policy-agent/opa v0.53.1 github.com/prometheus/client_golang v1.15.1 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index 31bf1f47..724c4c94 100644 --- a/go.sum +++ b/go.sum @@ -384,8 +384,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.53.0 h1:zC/0sI+Gof5/oiFNS3DmoJa11D0m0InZeDhZyzi+l6E= -github.com/open-policy-agent/opa v0.53.0/go.mod h1:j3wl8FqSz/+u33Scl72Ms2wxkZx4yZPdqSCrOqBqdsA= +github.com/open-policy-agent/opa v0.53.1 h1:APN8iA7Txgel13kSkc6S8dbUulydiPojXt6iyubmB7Q= +github.com/open-policy-agent/opa v0.53.1/go.mod h1:j3wl8FqSz/+u33Scl72Ms2wxkZx4yZPdqSCrOqBqdsA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 9b1a4dbf57dd24e38948ddfd3e8bc3fd0249501a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 15:00:13 +0200 Subject: [PATCH 095/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.6 to 1.11.7 (#196) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.6 to 1.11.7. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.6...v1.11.7) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0409b19c..7d0a768b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.6 + go.mongodb.org/mongo-driver v1.11.7 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 724c4c94..b0298e7a 100644 --- a/go.sum +++ b/go.sum @@ -531,8 +531,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o= -go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= +go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= 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= From 3847c9691ed48978c9730f63598032527c9ac6ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jun 2023 17:56:56 +0200 Subject: [PATCH 096/172] chore(deps): bump github.com/getkin/kin-openapi from 0.117.0 to 0.118.0 (#198) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.117.0 to 0.118.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.117.0...v0.118.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7d0a768b..99937199 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ 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.117.0 + github.com/getkin/kin-openapi v0.118.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 diff --git a/go.sum b/go.sum index b0298e7a..d0ebfca9 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.117.0 h1:QT2DyGujAL09F4NrKDHJGsUoIprlIcFVHWDVDcUFE8A= -github.com/getkin/kin-openapi v0.117.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= +github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= From 829a3dd25588aaf91038e7cea1ab9de4e41d8830 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Tue, 13 Jun 2023 17:33:47 +0200 Subject: [PATCH 097/172] test(161): added test for repro (#190) --- service/handler_test.go | 122 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/service/handler_test.go b/service/handler_test.go index 31432464..93554e2b 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -451,6 +451,128 @@ allow { require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) + // https://github.com/rond-authz/rond/issues/161 + t.Run("issue #161", func(t *testing.T) { + + t.Run("issue repro", func(t *testing.T) { + policy := `package policies +allow { + resource := data.resources[_] + print(resource) +} + ` + + invoked := false + mockBodySting := "I am a body" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + defer r.Body.Close() + buf, err := io.ReadAll(r.Body) + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") + filterQuery := r.Header.Get("rowfilterquery") + expectedQuery := `` + require.Equal(t, expectedQuery, filterQuery) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Mocked Backend Body Example")) + })) + defer server.Close() + + body := strings.NewReader(mockBodySting) + + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + require.NoError(t, err, "Unexpected error") + + serverURL, _ := url.Parse(server.URL) + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + nil, + mockRondConfigWithQueryGen, + OPAModuleConfig, + partialEvaluators, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + buf, err := io.ReadAll(w.Body) + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") + }) + + t.Run("print statement support", func(t *testing.T) { + policy := `package policies +allow { + employee := data.resources[_] + print("hi") + employee.manager == "manager_test" + print("hi") +} + ` + + invoked := false + mockBodySting := "I am a body" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + defer r.Body.Close() + buf, err := io.ReadAll(r.Body) + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") + filterQuery := r.Header.Get("rowfilterquery") + expectedQuery := `{"$or":[{"$and":[{"manager":{"$eq":"manager_test"}}]}]}` + require.Equal(t, expectedQuery, filterQuery) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Mocked Backend Body Example")) + })) + defer server.Close() + + body := strings.NewReader(mockBodySting) + + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + require.NoError(t, err, "Unexpected error") + + serverURL, _ := url.Parse(server.URL) + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + nil, + mockRondConfigWithQueryGen, + OPAModuleConfig, + partialEvaluators, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + buf, err := io.ReadAll(w.Body) + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") + }) + }) + t.Run("sends empty filter query with application-json as content-type", func(t *testing.T) { policy := `package policies allow { From 41b305ee6f3a75e84f20abd6fb0bce48de992abc Mon Sep 17 00:00:00 2001 From: Giovanna Monti <60603265+GiovannaMonti@users.noreply.github.com> Date: Wed, 14 Jun 2023 17:26:02 +0200 Subject: [PATCH 098/172] feat: ignore trailing slash support (#200) * (wip): first attempt * different approach * use first approach with regexp * fix: openapi utils ignore trailing slash feature * fix: service router trailing slash management * remove duplicate test * update nestedPathsConfig and related tests * add integration tests * add configuration validation * refactor: create oas maps utility function * Update service/router_test.go * remove duplicate type * fix: configuration validation and utility maps generation bugs * fix: invalidate config also if just one of the two paths has the IgnoreTrailingSlash flag for the same method * reorder utils; update changelog * undo changes on CHANGELOG file * apply suggestions * fix: unify integration tests into one * add continue command; fix error copy typo * rename CreateOASUtilityMaps to UnwrapConfiguration and set it as a method of oas * fix: remove old tests --------- Co-authored-by: Federico Maggi --- core/opamiddleware.go | 6 +- main_test.go | 88 +++++++++++++++++++++ mocks/invalidOASConfiguration.json | 31 ++++++++ mocks/nestedPathsConfig.json | 27 +++++++ mocks/rego-policies/example.rego | 4 + mocks/validOASConfiguration.json | 75 ++++++++++++++++++ openapi/openapi_utils.go | 123 +++++++++++++++++++++++++++-- openapi/openapi_utils_test.go | 73 +++++++++++++---- service/router.go | 30 +++---- service/router_test.go | 81 +++++++++++++------ 10 files changed, 473 insertions(+), 65 deletions(-) create mode 100644 mocks/invalidOASConfiguration.json create mode 100644 mocks/validOASConfiguration.json diff --git a/core/opamiddleware.go b/core/opamiddleware.go index c1cb54c2..3cd42149 100644 --- a/core/opamiddleware.go +++ b/core/opamiddleware.go @@ -16,6 +16,7 @@ package core import ( "errors" + "fmt" "net/http" "strings" @@ -35,7 +36,10 @@ func OPAMiddleware( policyEvaluators PartialResultsEvaluators, routesToNotProxy []string, ) mux.MiddlewareFunc { - OASrouter := openAPISpec.PrepareOASRouter() + OASrouter, err := openAPISpec.PrepareOASRouter() + if err != nil { + panic(fmt.Sprintf("Fatal: invalid OAS configuration: %s", err.Error())) + } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/main_test.go b/main_test.go index 802dddd0..4b76d071 100644 --- a/main_test.go +++ b/main_test.go @@ -606,6 +606,94 @@ func TestEntrypoint(t *testing.T) { require.True(t, gock.IsDone(), "the proxy forwards the request when the permissions aren't granted.") }) + t.Run("api permissions file path registered with and without trailing slash when ignoreTrailingSlash is true", func(t *testing.T) { + gock.Flush() + shutdown := make(chan os.Signal, 1) + + defer gock.Off() + defer gock.DisableNetworkingFilters() + defer gock.DisableNetworking() + + gock.EnableNetworking() + gock.NetworkingFilter(func(r *http.Request) bool { + if r.URL.Path == "/documentation/json" { + return false + } + if r.URL.Path == "/with/trailing/slash/" && r.URL.Host == "localhost:3339" { + return false + } + if r.URL.Path == "/with/trailing/slash" && r.URL.Host == "localhost:3339" { + return false + } + if r.URL.Path == "/without/trailing/slash" && r.URL.Host == "localhost:3339" { + return false + } + if r.URL.Path == "/without/trailing/slash/" && r.URL.Host == "localhost:3339" { + return false + } + + return true + }) + + setEnvs(t, []env{ + {name: "HTTP_PORT", value: "5559"}, + {name: "TARGET_SERVICE_HOST", value: "localhost:3339"}, + {name: "API_PERMISSIONS_FILE_PATH", value: "./mocks/nestedPathsConfig.json"}, + {name: "OPA_MODULES_DIRECTORY", value: "./mocks/rego-policies"}, + {name: "LOG_LEVEL", value: "fatal"}, + }) + + go func() { + entrypoint(shutdown) + }() + defer func() { + shutdown <- syscall.SIGTERM + }() + time.Sleep(1 * time.Second) + + gock.New("http://localhost:3339"). + Get("/with/trailing/slash/"). + Reply(200). + JSON(map[string]interface{}{"originalMsg": "this is the original"}) + + gock.New("http://localhost:3339"). + Get("/with/trailing/slash"). + Reply(200). + JSON(map[string]interface{}{"originalMsg": "this is the original"}) + + gock.New("http://localhost:3339"). + Post("/without/trailing/slash"). + Reply(200) + + gock.New("http://localhost:3339"). + Post("/without/trailing/slash/"). + Reply(200) + + resp1, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:5559%s", "/with/trailing/slash/")) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp1.StatusCode, "unexpected status code.") + + respBody1, _ := io.ReadAll(resp1.Body) + require.Equal(t, "\"/with/trailing/slash/\"", string(respBody1)) + + resp2, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:5559%s", "/with/trailing/slash")) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp2.StatusCode, "unexpected status code.") + + respBody2, _ := io.ReadAll(resp2.Body) + require.Equal(t, "\"/with/trailing/slash\"", string(respBody2)) + + resp3, err := http.DefaultClient.Post(fmt.Sprintf("http://localhost:5559%s", "/without/trailing/slash"), "application/json", nil) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp3.StatusCode, "unexpected status code.") + + resp4, err := http.DefaultClient.Post(fmt.Sprintf("http://localhost:5559%s", "/without/trailing/slash/"), "application/json", nil) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp4.StatusCode, "unexpected status code.") + + require.True(t, gock.IsDone(), "the proxy forwards the request when the permissions aren't granted.") + }) + t.Run("mongo integration", func(t *testing.T) { shutdown := make(chan os.Signal, 1) diff --git a/mocks/invalidOASConfiguration.json b/mocks/invalidOASConfiguration.json new file mode 100644 index 00000000..67bbdd23 --- /dev/null +++ b/mocks/invalidOASConfiguration.json @@ -0,0 +1,31 @@ +{ + "paths": { + "/ignore/trailing/slash/": { + "all": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "responseFlow": { + "policyName": "original_path" + }, + "options": { + "ignoreTrailingSlash": false + } + } + } + }, + "/ignore/trailing/slash": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + } + } +} diff --git a/mocks/nestedPathsConfig.json b/mocks/nestedPathsConfig.json index 938ef713..c4b17a39 100644 --- a/mocks/nestedPathsConfig.json +++ b/mocks/nestedPathsConfig.json @@ -147,6 +147,33 @@ "allow": "project_get" } } + }, + "/with/trailing/slash/": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "responseFlow": { + "policyName": "original_path" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + }, + "/without/trailing/slash": { + "post": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } } } } \ No newline at end of file diff --git a/mocks/rego-policies/example.rego b/mocks/rego-policies/example.rego index 85af5afb..5031aff4 100644 --- a/mocks/rego-policies/example.rego +++ b/mocks/rego-policies/example.rego @@ -19,6 +19,10 @@ responsepolicy [response] { response := {"msg": "hey there"} } +original_path [response] { + response := input.request.path +} + allow_commit { input.request.pathParams.projectId == "5df2260277baff0011fde823" } diff --git a/mocks/validOASConfiguration.json b/mocks/validOASConfiguration.json new file mode 100644 index 00000000..5b5001be --- /dev/null +++ b/mocks/validOASConfiguration.json @@ -0,0 +1,75 @@ +{ + "paths": { + "/ignore/trailing/slash": { + "post": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "responseFlow": { + "policyName": "original_path" + }, + "options": { + "ignoreTrailingSlash": true + } + } + }, + "patch": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "responseFlow": { + "policyName": "original_path" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + }, + "/ignore/trailing/slash/": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + }, + "/different/api/based/on/final/slash/": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + } + } + } + }, + "/different/api/based/on/final/slash": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "options": { + "ignoreTrailingSlash": false + } + } + }, + "post": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + } + } +} diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 927091a9..02bf4117 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -56,6 +56,7 @@ type XPermissionKey struct{} type PermissionOptions struct { EnableResourcePermissionsMapOptimization bool `json:"enableResourcePermissionsMapOptimization"` + IgnoreTrailingSlash bool `json:"ignoreTrailingSlash,omitempty"` } // Config v1 // @@ -153,22 +154,36 @@ func createOasHandler(scopedMethodContent VerbConfig) func(http.ResponseWriter, header.Set("resourceFilter.rowFilter.headerKey", permission.RequestFlow.QueryOptions.HeaderName) header.Set("responseFilter.policy", permission.ResponseFlow.PolicyName) header.Set("options.enableResourcePermissionsMapOptimization", strconv.FormatBool(permission.Options.EnableResourcePermissionsMapOptimization)) + header.Set("options.ignoreTrailingSlash", strconv.FormatBool(permission.Options.IgnoreTrailingSlash)) } } -func (oas *OpenAPISpec) PrepareOASRouter() *bunrouter.CompatRouter { +func (oas *OpenAPISpec) PrepareOASRouter() (*bunrouter.CompatRouter, error) { OASRouter := bunrouter.New().Compat() routeMap := oas.createRoutesMap() - for OASPath, OASContent := range oas.Paths { + configurationError := validateConfiguration(oas) + if configurationError != nil { + return nil, configurationError + } + + for OASPath, OASContent := range oas.Paths { OASPathCleaned := ConvertPathVariablesToColons(cleanWildcard(OASPath)) for method, methodContent := range OASContent { scopedMethod := strings.ToUpper(method) - handler := createOasHandler(methodContent) if scopedMethod != strings.ToUpper(AllHTTPMethod) { OASRouter.Handle(scopedMethod, OASPathCleaned, handler) + if methodContent.PermissionV2 != nil && methodContent.PermissionV2.Options.IgnoreTrailingSlash { + slashLaxPathToRegister := OASPathCleaned + if strings.HasSuffix(OASPathCleaned, "/") { + slashLaxPathToRegister = strings.TrimSuffix(slashLaxPathToRegister, "/") + } else { + slashLaxPathToRegister += "/" + } + OASRouter.Handle(scopedMethod, slashLaxPathToRegister, handler) + } continue } @@ -180,7 +195,7 @@ func (oas *OpenAPISpec) PrepareOASRouter() *bunrouter.CompatRouter { } } - return OASRouter + return OASRouter, nil } // FIXME: This is not a logic method of OAS, but could be a method of OASRouter @@ -201,7 +216,11 @@ func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path s } enableResourcePermissionsMapOptimization, err := strconv.ParseBool(recorderResult.Header.Get("options.enableResourcePermissionsMapOptimization")) if err != nil { - return RondConfig{}, fmt.Errorf("error while parsing rowFilter.enabled: %s", err) + return RondConfig{}, fmt.Errorf("error while parsing options.enableResourcePermissionsMapOptimization: %s", err) + } + ignoreTrailingSlash, err := strconv.ParseBool(recorderResult.Header.Get("options.ignoreTrailingSlash")) + if err != nil { + return RondConfig{}, fmt.Errorf("error while parsing options.ignoreTrailingSlash: %s", err) } return RondConfig{ RequestFlow: RequestFlow{ @@ -216,6 +235,7 @@ func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path s }, Options: PermissionOptions{ EnableResourcePermissionsMapOptimization: enableResourcePermissionsMapOptimization, + IgnoreTrailingSlash: ignoreTrailingSlash, }, }, nil } @@ -232,6 +252,10 @@ func newRondConfigFromPermissionV1(v1Permission *XPermission) *RondConfig { ResponseFlow: ResponseFlow{ PolicyName: v1Permission.ResponseFilter.Policy, }, + Options: PermissionOptions{ + EnableResourcePermissionsMapOptimization: v1Permission.Options.EnableResourcePermissionsMapOptimization, + IgnoreTrailingSlash: v1Permission.Options.IgnoreTrailingSlash, + }, } } @@ -358,3 +382,92 @@ var matchBrackets = regexp.MustCompile(`\/{(\w+)}`) func ConvertPathVariablesToColons(path string) string { return matchBrackets.ReplaceAllString(path, "/:$1") } + +type IgnoreTrailingSlashMap map[string]map[string]bool + +func (i IgnoreTrailingSlashMap) Add(path, verb string, ignore bool) { + if verbMap, ok := i[path]; !ok || verbMap == nil { + i[path] = make(map[string]bool) + } + i[path][verb] = ignore +} + +func findDuplicatesInList(list []string) map[string]bool { + duplicates := make(map[string]bool) + for i := 0; i < len(list)-1; i++ { + for j := i + 1; j < len(list); j++ { + if list[i] == list[j] { + duplicates[list[i]] = true + } + } + } + return duplicates +} + +func listContainsString(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} + +func validateConfiguration(oas *OpenAPISpec) error { + paths, methodsMap, ignoreTrailingSlashMap := oas.UnwrapConfiguration() + var pathsWithoutSuffix []string + + for _, path := range paths { + pathsWithoutSuffix = append(pathsWithoutSuffix, strings.TrimSuffix(path, "/")) + } + + duplicates := findDuplicatesInList(pathsWithoutSuffix) + + for path := range duplicates { + pathWithSuffix := path + "/" + for _, method := range methodsMap[path] { + shouldIgnoreTrailingSlash := listContainsString(methodsMap[path], method) && listContainsString(methodsMap[pathWithSuffix], method) && (ignoreTrailingSlashMap[path][method] || ignoreTrailingSlashMap[pathWithSuffix][method]) + if shouldIgnoreTrailingSlash { + return fmt.Errorf("duplicate paths: \"%s\" and \"%s\" with ignoreTrailingSlash flag active", path, pathWithSuffix) + } + } + } + return nil +} + +func (oas *OpenAPISpec) UnwrapConfiguration() ([]string, map[string][]string, IgnoreTrailingSlashMap) { + paths := make([]string, 0) + methods := make(map[string][]string) + ignoreTrailingSlashMap := make(IgnoreTrailingSlashMap) + + for path, pathMethods := range oas.Paths { + paths = append(paths, path) + + for method, methodContent := range pathMethods { + upperCaseMethod := strings.ToUpper(method) + + if methodContent.PermissionV2 != nil { + if method == AllHTTPMethod { + for _, verb := range OasSupportedHTTPMethods { + ignoreTrailingSlashMap.Add(path, strings.ToUpper(verb), methodContent.PermissionV2.Options.IgnoreTrailingSlash) + continue + } + } + ignoreTrailingSlashMap.Add(path, upperCaseMethod, methodContent.PermissionV2.Options.IgnoreTrailingSlash) + } + + if method == AllHTTPMethod { + methods[path] = OasSupportedHTTPMethods + continue + } + + if methods[path] == nil { + methods[path] = []string{} + } + + methods[path] = append(methods[path], upperCaseMethod) + } + } + + return paths, methods, ignoreTrailingSlashMap +} diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index dca04e47..182ad2a7 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -274,10 +274,23 @@ func TestLoadOAS(t *testing.T) { }) } +func TestConfigurationValidation(t *testing.T) { + t.Run("invalid configuration", func(t *testing.T) { + oas := prepareOASFromFile(t, "../mocks/invalidOASConfiguration.json") + _, err := oas.PrepareOASRouter() + require.EqualError(t, err, "duplicate paths: \"/ignore/trailing/slash\" and \"/ignore/trailing/slash/\" with ignoreTrailingSlash flag active") + }) + t.Run("valid configuration", func(t *testing.T) { + oas := prepareOASFromFile(t, "../mocks/validOASConfiguration.json") + _, err := oas.PrepareOASRouter() + require.NoError(t, err) + }) +} + func TestFindPermission(t *testing.T) { t.Run("nested cases", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") - OASRouter := oas.PrepareOASRouter() + OASRouter, _ := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/not/existing/route", "GET") require.Empty(t, RondConfig{}, found) @@ -292,6 +305,7 @@ func TestFindPermission(t *testing.T) { require.EqualError(t, err, fmt.Sprintf("%s: PUT /use/method/that/not/existing/put", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/foo/bar/barId", "GET") + require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar_params", @@ -301,9 +315,9 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/bar/barId/another-params-not-configured", "GET") + require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar", @@ -313,13 +327,13 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/bar/nested/case/really/nested", "GET") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) found, err = oas.FindPermission(OASRouter, "/foo/bar/nested", "GET") + require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo_bar_nested", @@ -329,9 +343,9 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/foo/simble", "PATCH") + require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ PolicyName: "foo", @@ -341,52 +355,81 @@ func TestFindPermission(t *testing.T) { }, }, }, found) - require.NoError(t, err) found, err = oas.FindPermission(OASRouter, "/test/all", "GET") require.Equal(t, RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: GET /test/all", ErrNotFoundOASDefinition)) found, err = oas.FindPermission(OASRouter, "/test/all/", "GET") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "GET") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "POST") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PUT") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PATCH") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "DELETE") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) found, err = oas.FindPermission(OASRouter, "/test/all/verb", "HEAD") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) found, err = oas.FindPermission(OASRouter, "/projects/", "POST") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) require.NoError(t, err) + require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) found, err = oas.FindPermission(OASRouter, "/projects/", "GET") + require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_get"}}, found) + + found, err = oas.FindPermission(OASRouter, "/with/trailing/slash/", "GET") require.NoError(t, err) + require.Equal(t, RondConfig{ + RequestFlow: RequestFlow{PolicyName: "foo_bar"}, + ResponseFlow: ResponseFlow{PolicyName: "original_path"}, + Options: PermissionOptions{IgnoreTrailingSlash: true}, + }, found) + + found, err = oas.FindPermission(OASRouter, "/with/trailing/slash", "GET") + require.NoError(t, err) + require.Equal(t, RondConfig{ + RequestFlow: RequestFlow{PolicyName: "foo_bar"}, + ResponseFlow: ResponseFlow{PolicyName: "original_path"}, + Options: PermissionOptions{IgnoreTrailingSlash: true}, + }, found) + + found, err = oas.FindPermission(OASRouter, "/without/trailing/slash", "POST") + require.NoError(t, err) + require.Equal(t, RondConfig{ + RequestFlow: RequestFlow{PolicyName: "foo_bar"}, + Options: PermissionOptions{IgnoreTrailingSlash: true}, + }, found) + + found, err = oas.FindPermission(OASRouter, "/without/trailing/slash/", "POST") + require.NoError(t, err) + require.Equal(t, RondConfig{ + RequestFlow: RequestFlow{PolicyName: "foo_bar"}, + Options: PermissionOptions{IgnoreTrailingSlash: true}, + }, found) }) t.Run("encoded cases", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/mockForEncodedTest.json") - OASRouter := oas.PrepareOASRouter() + OASRouter, _ := oas.PrepareOASRouter() found, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) diff --git a/service/router.go b/service/router.go index 56343091..c2c17422 100644 --- a/service/router.go +++ b/service/router.go @@ -184,25 +184,10 @@ func setupRoutes(router *mux.Router, oas *openapi.OpenAPISpec, env config.Enviro } } + paths, methodsMap, ignoreTrailingSlashMap := oas.UnwrapConfiguration() + // NOTE: The following sort is required by mux router because it expects // routes to be registered in the proper order - paths := make([]string, 0) - methods := make(map[string][]string, 0) - - for path, pathMethods := range oas.Paths { - paths = append(paths, path) - for method := range pathMethods { - if method == openapi.AllHTTPMethod { - methods[path] = openapi.OasSupportedHTTPMethods - continue - } - if methods[path] == nil { - methods[path] = []string{} - } - - methods[path] = append(methods[path], strings.ToUpper(method)) - } - } sort.Sort(sort.Reverse(sort.StringSlice(paths))) for _, path := range paths { @@ -215,14 +200,21 @@ func setupRoutes(router *mux.Router, oas *openapi.OpenAPISpec, env config.Enviro } if strings.Contains(pathToRegister, "*") { pathWithoutAsterisk := strings.ReplaceAll(pathToRegister, "*", "") - router.PathPrefix(openapi.ConvertPathVariablesToBrackets(pathWithoutAsterisk)).HandlerFunc(rbacHandler).Methods(methods[path]...) + router.PathPrefix(openapi.ConvertPathVariablesToBrackets(pathWithoutAsterisk)).HandlerFunc(rbacHandler).Methods(methodsMap[path]...) continue } if path == env.TargetServiceOASPath && documentationPermission == "" { router.HandleFunc(openapi.ConvertPathVariablesToBrackets(pathToRegister), alwaysProxyHandler).Methods(http.MethodGet) continue } - router.HandleFunc(openapi.ConvertPathVariablesToBrackets(pathToRegister), rbacHandler).Methods(methods[path]...) + for _, method := range methodsMap[path] { + actualPathToRegister := openapi.ConvertPathVariablesToBrackets(pathToRegister) + shouldIgnoreTrailingSlash := ignoreTrailingSlashMap[path][method] + if shouldIgnoreTrailingSlash { + actualPathToRegister = fmt.Sprintf("/{%s:%s\\/?}", openapi.ConvertPathVariablesToBrackets(pathToRegister), openapi.ConvertPathVariablesToBrackets(pathToRegister)) + } + router.HandleFunc(actualPathToRegister, rbacHandler).Methods(method) + } } if documentationPathInOAS == nil { router.HandleFunc(openapi.ConvertPathVariablesToBrackets(env.TargetServiceOASPath), alwaysProxyHandler) diff --git a/service/router_test.go b/service/router_test.go index de52134d..4a5517d1 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -44,20 +44,41 @@ func TestSetupRoutes(t *testing.T) { router := mux.NewRouter() oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ - "/foo": openapi.PathVerbs{}, - "/bar": openapi.PathVerbs{}, - "/foo/bar": openapi.PathVerbs{}, - "/-/ready": openapi.PathVerbs{}, - "/-/healthz": openapi.PathVerbs{}, - "/-/check-up": openapi.PathVerbs{}, - "/-/metrics": openapi.PathVerbs{}, - "/-/rond/metrics": openapi.PathVerbs{}, - "/-/rbac-healthz": openapi.PathVerbs{}, - "/-/rbac-ready": openapi.PathVerbs{}, - "/-/rbac-check-up": openapi.PathVerbs{}, + "/foo": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/bar": openapi.PathVerbs{"post": openapi.VerbConfig{}}, + "/foo/bar": openapi.PathVerbs{"patch": openapi.VerbConfig{}}, + "/-/ready": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/healthz": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/check-up": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/metrics": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/rond/metrics": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/rbac-healthz": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/rbac-ready": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/rbac-check-up": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/with/trailing/slash": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "filter_policy", + }, + Options: openapi.PermissionOptions{IgnoreTrailingSlash: true}, + }, + }, + }, }, } - expectedPaths := []string{"/", "/-/check-up", "/-/healthz", "/-/metrics", "/-/ready", "/bar", "/documentation/json", "/foo", "/foo/bar"} + expectedPaths := []string{ + "/", + "/-/check-up", + "/-/healthz", + "/-/metrics", + "/-/ready", + "/bar", + "/documentation/json", + "/foo", + "/foo/bar", + "/{/with/trailing/slash:/with/trailing/slash\\/?}", + } setupRoutes(router, oas, envs) @@ -80,17 +101,27 @@ func TestSetupRoutes(t *testing.T) { router := mux.NewRouter() oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ - "/-/ready": openapi.PathVerbs{}, - "/-/healthz": openapi.PathVerbs{}, - "/-/check-up": openapi.PathVerbs{}, + "/-/ready": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/healthz": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/-/check-up": openapi.PathVerbs{"get": openapi.VerbConfig{}}, // General route - "/foo/*": openapi.PathVerbs{}, - "/foo/bar/*": openapi.PathVerbs{}, - "/foo/bar/nested": openapi.PathVerbs{}, - "/foo/bar/:barId": openapi.PathVerbs{}, + "/foo/*": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/bar/*": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/bar/nested": openapi.PathVerbs{"post": openapi.VerbConfig{}}, + "/foo/bar/:barId": openapi.PathVerbs{"get": openapi.VerbConfig{}}, }, } - expectedPaths := []string{"/", "/-/ready", "/-/healthz", "/-/check-up", "/foo/", "/foo/bar/", "/foo/bar/nested", "/foo/bar/{barId}", "/documentation/json"} + expectedPaths := []string{ + "/", + "/-/ready", + "/-/healthz", + "/-/check-up", + "/foo/bar/nested", + "/foo/bar/{barId}", + "/foo/bar/", + "/foo/", + "/documentation/json", + } sort.Strings(expectedPaths) setupRoutes(router, oas, envs) @@ -119,11 +150,11 @@ func TestSetupRoutes(t *testing.T) { router := mux.NewRouter() oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ - "/documentation/json": openapi.PathVerbs{}, - "/foo/*": openapi.PathVerbs{}, - "/foo/bar/*": openapi.PathVerbs{}, - "/foo/bar/nested": openapi.PathVerbs{}, - "/foo/bar/:barId": openapi.PathVerbs{}, + "/documentation/json": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/*": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/bar/*": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/bar/nested": openapi.PathVerbs{"get": openapi.VerbConfig{}}, + "/foo/bar/:barId": openapi.PathVerbs{"get": openapi.VerbConfig{}}, }, } expectedPaths := []string{"/validate/", "/validate/documentation/json", "/validate/foo/", "/validate/foo/bar/", "/validate/foo/bar/nested", "/validate/foo/bar/{barId}"} From 34f39d3b6d7c88e514aa466618936b6134c9aa58 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Wed, 14 Jun 2023 18:13:30 +0200 Subject: [PATCH 099/172] fix: add panic guard --- core/opaevaluator.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index dd04a169..dd06ab83 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -436,11 +436,16 @@ func buildOptimizedResourcePermissionsMap(user types.User) PermissionsOnResource permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) rolesMap := buildRolesMap(user.UserRoles) for _, binding := range user.UserBindings { + if binding.Resource == nil { + continue + } + for _, role := range binding.Roles { rolePermissions, ok := rolesMap[role] if !ok { continue } + for _, permission := range rolePermissions { key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) permissionsOnResourceMap[key] = true From 3df017fd95b67a3a1fd088b8d2f16edab00a229b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:15:46 +0200 Subject: [PATCH 100/172] chore(deps): bump github.com/prometheus/client_golang (#201) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 99937199..1ba3eafc 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.53.1 - github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/client_golang v1.16.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 @@ -59,7 +59,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect diff --git a/go.sum b/go.sum index d0ebfca9..27bd3c72 100644 --- a/go.sum +++ b/go.sum @@ -412,8 +412,8 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -432,8 +432,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= From 28ff2138cf728710c28e4ac363808f3635929770 Mon Sep 17 00:00:00 2001 From: Giovanna Monti <60603265+GiovannaMonti@users.noreply.github.com> Date: Thu, 15 Jun 2023 15:40:58 +0200 Subject: [PATCH 101/172] fix: ignore trailing slash bug with ALL method (#202) * fix: ignore trailing slash bug with ALL method * add integration test --- main_test.go | 30 ++++++++++++++++++++++++++++++ mocks/nestedPathsConfig.json | 15 +++++++++++++++ openapi/openapi_utils.go | 26 +++++++++++++++----------- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/main_test.go b/main_test.go index 4b76d071..1ce3bcac 100644 --- a/main_test.go +++ b/main_test.go @@ -631,6 +631,12 @@ func TestEntrypoint(t *testing.T) { if r.URL.Path == "/without/trailing/slash/" && r.URL.Host == "localhost:3339" { return false } + if r.URL.Path == "/ignore/trailing/slash" && r.URL.Host == "localhost:3339" { + return false + } + if r.URL.Path == "/ignore/trailing/slash/" && r.URL.Host == "localhost:3339" { + return false + } return true }) @@ -669,6 +675,16 @@ func TestEntrypoint(t *testing.T) { Post("/without/trailing/slash/"). Reply(200) + gock.New("http://localhost:3339"). + Get("/ignore/trailing/slash/"). + Reply(200). + JSON(map[string]interface{}{"originalMsg": "this is the original"}) + + gock.New("http://localhost:3339"). + Get("/ignore/trailing/slash"). + Reply(200). + JSON(map[string]interface{}{"originalMsg": "this is the original"}) + resp1, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:5559%s", "/with/trailing/slash/")) require.Equal(t, nil, err) require.Equal(t, http.StatusOK, resp1.StatusCode, "unexpected status code.") @@ -691,6 +707,20 @@ func TestEntrypoint(t *testing.T) { require.Equal(t, nil, err) require.Equal(t, http.StatusOK, resp4.StatusCode, "unexpected status code.") + resp5, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:5559%s", "/ignore/trailing/slash/")) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp5.StatusCode, "unexpected status code.") + + respBody5, _ := io.ReadAll(resp5.Body) + require.Equal(t, "\"/ignore/trailing/slash/\"", string(respBody5)) + + resp6, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:5559%s", "/ignore/trailing/slash")) + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp6.StatusCode, "unexpected status code.") + + respBody6, _ := io.ReadAll(resp6.Body) + require.Equal(t, "\"/ignore/trailing/slash\"", string(respBody6)) + require.True(t, gock.IsDone(), "the proxy forwards the request when the permissions aren't granted.") }) diff --git a/mocks/nestedPathsConfig.json b/mocks/nestedPathsConfig.json index c4b17a39..4c26a3c8 100644 --- a/mocks/nestedPathsConfig.json +++ b/mocks/nestedPathsConfig.json @@ -149,6 +149,21 @@ } }, "/with/trailing/slash/": { + "all": { + "x-rond": { + "requestFlow": { + "policyName": "foo_bar" + }, + "responseFlow": { + "policyName": "original_path" + }, + "options": { + "ignoreTrailingSlash": true + } + } + } + }, + "/ignore/trailing/slash/": { "get": { "x-rond": { "requestFlow": { diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 02bf4117..4a6c4721 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -174,22 +174,13 @@ func (oas *OpenAPISpec) PrepareOASRouter() (*bunrouter.CompatRouter, error) { handler := createOasHandler(methodContent) if scopedMethod != strings.ToUpper(AllHTTPMethod) { - OASRouter.Handle(scopedMethod, OASPathCleaned, handler) - if methodContent.PermissionV2 != nil && methodContent.PermissionV2.Options.IgnoreTrailingSlash { - slashLaxPathToRegister := OASPathCleaned - if strings.HasSuffix(OASPathCleaned, "/") { - slashLaxPathToRegister = strings.TrimSuffix(slashLaxPathToRegister, "/") - } else { - slashLaxPathToRegister += "/" - } - OASRouter.Handle(scopedMethod, slashLaxPathToRegister, handler) - } + registerLaxPath(OASRouter, scopedMethod, methodContent, OASPathCleaned, handler) continue } for _, method := range OasSupportedHTTPMethods { if !routeMap.contains(OASPath, method) { - OASRouter.Handle(method, OASPathCleaned, handler) + registerLaxPath(OASRouter, method, methodContent, OASPathCleaned, handler) } } } @@ -198,6 +189,19 @@ func (oas *OpenAPISpec) PrepareOASRouter() (*bunrouter.CompatRouter, error) { return OASRouter, nil } +func registerLaxPath(OASRouter *bunrouter.CompatRouter, method string, methodContent VerbConfig, OASPathCleaned string, handler http.HandlerFunc) { + OASRouter.Handle(method, OASPathCleaned, handler) + if methodContent.PermissionV2 != nil && methodContent.PermissionV2.Options.IgnoreTrailingSlash { + slashLaxPathToRegister := OASPathCleaned + if strings.HasSuffix(OASPathCleaned, "/") { + slashLaxPathToRegister = strings.TrimSuffix(slashLaxPathToRegister, "/") + } else { + slashLaxPathToRegister += "/" + } + OASRouter.Handle(method, slashLaxPathToRegister, handler) + } +} + // FIXME: This is not a logic method of OAS, but could be a method of OASRouter func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path string, method string) (RondConfig, error) { recorder := httptest.NewRecorder() From 3d7e00e76e8ea5be4b2bd277b53031d0f2a5b7e2 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Thu, 15 Jun 2023 17:04:09 +0200 Subject: [PATCH 102/172] Upgrade version to v1.8.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 55e1749a..f675f21c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.7.0" +ENV SERVICE_VERSION="1.8.0" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From 32c26ba5e208155e3ef467603dc7bf141c335587 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Sun, 18 Jun 2023 08:29:48 +0200 Subject: [PATCH 103/172] Remove env from core package (#199) * feat: remove env from opa middleware * fix: remove internal types since it is a duplication * feat: remove config from transport * feat: remove config from evaluator * feat: remove config from openapi * test: add test * Update internal/config/env_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * refactor: transport use user header keys struct * feat: add env check in config package * fix: resolve merge issues --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/opa_transport.go | 45 +++- core/opa_transport_test.go | 216 +++++++++------ core/opaevaluator.go | 212 ++++++++------- core/opaevaluator_test.go | 320 ++++++++++++----------- core/opamiddleware.go | 15 +- core/opamiddleware_test.go | 47 ++-- internal/config/env.go | 34 ++- internal/config/env_test.go | 61 ++++- internal/mongoclient/mongoclient.go | 20 +- internal/mongoclient/mongoclient_test.go | 44 +++- internal/types/rbactypes.go | 81 ------ internal/utils/http.go | 2 +- internal/utils/http_test.go | 2 +- main.go | 10 +- main_test.go | 4 +- openapi/openapi_utils.go | 29 +- openapi/openapi_utils_test.go | 15 +- openapi/routerinfo_middleware_test.go | 3 - service/handler.go | 39 ++- service/handler_test.go | 75 +++--- service/router.go | 5 +- service/router_test.go | 6 +- service/statusroutes_test.go | 3 +- types/rbactypes.go | 7 + 24 files changed, 724 insertions(+), 571 deletions(-) delete mode 100644 internal/types/rbactypes.go diff --git a/core/opa_transport.go b/core/opa_transport.go index 320bb6a1..37cc47ac 100644 --- a/core/opa_transport.go +++ b/core/opa_transport.go @@ -23,12 +23,12 @@ import ( "net/http" "strconv" - "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" + "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) @@ -40,26 +40,34 @@ type OPATransport struct { request *http.Request permission *openapi.RondConfig partialResultsEvaluators PartialResultsEvaluators - env config.EnvironmentVariables + + clientHeaderKey string + userHeaders types.UserHeadersKeys + evaluatorOptions *EvaluatorOptions } func NewOPATransport( - defaultTransport http.RoundTripper, + transport http.RoundTripper, context context.Context, logger *logrus.Entry, req *http.Request, permission *openapi.RondConfig, partialResultsEvaluators PartialResultsEvaluators, - env config.EnvironmentVariables, + clientHeaderKey string, + userHeadersKeys types.UserHeadersKeys, + evaluatorOptions *EvaluatorOptions, ) *OPATransport { return &OPATransport{ - http.DefaultTransport, - req.Context(), - logger, - req, - permission, - partialResultsEvaluators, - env, + RoundTripper: transport, + context: req.Context(), + logger: logger, + request: req, + permission: permission, + partialResultsEvaluators: partialResultsEvaluators, + + clientHeaderKey: clientHeaderKey, + userHeaders: userHeadersKeys, + evaluatorOptions: evaluatorOptions, } } @@ -100,19 +108,28 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return nil, fmt.Errorf("response body is not valid: %s", err.Error()) } - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(t.logger, t.request, t.env) + userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(t.logger, t.request, t.userHeaders) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil } - input, err := CreateRegoQueryInput(t.request, t.env, t.permission.Options.EnableResourcePermissionsMapOptimization, userInfo, decodedBody) + pathParams := mux.Vars(t.request) + input, err := InputFromRequest(t.request, userInfo, t.clientHeaderKey, pathParams, decodedBody) + if err != nil { + t.responseWithError(resp, err, http.StatusInternalServerError) + return resp, nil + } + + regoInput, err := CreateRegoQueryInput(t.logger, input, RegoInputOptions{ + EnableResourcePermissionsMapOptimization: t.permission.Options.EnableResourcePermissionsMapOptimization, + }) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil } - evaluator, err := t.partialResultsEvaluators.GetEvaluatorFromPolicy(t.context, t.permission.ResponseFlow.PolicyName, input, t.env) + evaluator, err := t.partialResultsEvaluators.GetEvaluatorFromPolicy(t.context, t.permission.ResponseFlow.PolicyName, regoInput, t.evaluatorOptions) if err != nil { t.logger.WithField("error", logrus.Fields{ "policyName": t.permission.ResponseFlow.PolicyName, diff --git a/core/opa_transport_test.go b/core/opa_transport_test.go index 18310db1..2f80d11d 100644 --- a/core/opa_transport_test.go +++ b/core/opa_transport_test.go @@ -16,16 +16,16 @@ package core import ( "bytes" + "context" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "strconv" - "strings" "testing" - "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" @@ -39,7 +39,6 @@ import ( func TestRoundTripErrors(t *testing.T) { logger, _ := test.NewNullLogger() - envs := config.EnvironmentVariables{} defer gock.Off() @@ -61,7 +60,9 @@ func TestRoundTripErrors(t *testing.T) { req, nil, nil, - envs, + "", + types.UserHeadersKeys{}, + nil, } resp, err := transport.RoundTrip(req) @@ -87,7 +88,6 @@ func TestIs2xx(t *testing.T) { } func TestOPATransportResponseWithError(t *testing.T) { - envs := config.EnvironmentVariables{} logger, _ := test.NewNullLogger() req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) @@ -99,7 +99,9 @@ func TestOPATransportResponseWithError(t *testing.T) { req, nil, nil, - envs, + "", + types.UserHeadersKeys{}, + nil, } t.Run("generic business error message", func(t *testing.T) { @@ -148,25 +150,24 @@ func TestOPATransportResponseWithError(t *testing.T) { } func TestOPATransportRoundTrip(t *testing.T) { - envs := config.EnvironmentVariables{ - UserIdHeader: "useridheader", - UserGroupsHeader: "usergroupsheader", - UserPropertiesHeader: "userpropertiesheader", - } - logger, _ := test.NewNullLogger() req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) t.Run("returns error on RoundTrip error", func(t *testing.T) { - transport := &OPATransport{ + transport := NewOPATransport( &MockRoundTrip{Error: fmt.Errorf("some error")}, req.Context(), logrus.NewEntry(logger), req, + nil, nil, + "", + types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, nil, - nil, - envs, - } + ) _, err := transport.RoundTrip(req) require.Error(t, err, "some error") @@ -180,13 +181,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } updatedResp, err := transport.RoundTrip(req) @@ -206,13 +209,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) @@ -231,13 +236,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) @@ -255,13 +262,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{http.CanonicalHeaderKey("some"): []string{"content"}}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) @@ -269,6 +278,53 @@ func TestOPATransportRoundTrip(t *testing.T) { require.Equal(t, []string{"content"}, resp.Header[http.CanonicalHeaderKey("some")]) }) + t.Run("ok with filter response", func(t *testing.T) { + resp := http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader([]byte(`{"some":"field"}`))), + ContentLength: 16, + Header: http.Header{"Content-Type": []string{"application/json"}}, + } + + req = req.Clone(metrics.WithValue(openapi.WithRouterInfo(logrus.NewEntry(logger), req.Context(), req), metrics.SetupMetrics(""))) + + partialResult, err := NewPartialResultEvaluator(context.Background(), "my_policy", &OPAModuleConfig{ + Content: "package policies my_policy [resources] { resources := input.response.body }", + }, nil, nil) + require.NoError(t, err) + + transport := &OPATransport{ + RoundTripper: &MockRoundTrip{Response: &resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + permission: &openapi.RondConfig{ + ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, + }, + partialResultsEvaluators: PartialResultsEvaluators{"my_policy": PartialEvaluator{partialResult}}, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, + } + + actualResp, err := transport.RoundTrip(req) + require.NoError(t, err, "response body is not valid") + require.Equal(t, http.StatusOK, actualResp.StatusCode) + require.Equal(t, int64(16), actualResp.ContentLength) + + body, err := io.ReadAll(actualResp.Body) + require.NoError(t, err) + require.Equal(t, []byte(`{"some":"field"}`), body) + + expectedHeaders := http.Header{} + expectedHeaders.Set("Content-Type", "application/json") + expectedHeaders.Set("Content-Length", "16") + + require.Equal(t, expectedHeaders, actualResp.Header) + }) + t.Run("failure on non-json response content-type", func(t *testing.T) { resp := &http.Response{ StatusCode: http.StatusOK, @@ -277,13 +333,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"text/plain"}}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) @@ -291,7 +349,7 @@ func TestOPATransportRoundTrip(t *testing.T) { require.Equal(t, http.StatusInternalServerError, resp.StatusCode) bodyBytes, err := io.ReadAll(resp.Body) require.Nil(t, err) - require.True(t, strings.Contains(string(bodyBytes), "content-type is not application/json")) + require.Contains(t, string(bodyBytes), "content-type is not application/json") }) t.Run("failure on non-json response even with json content-type", func(t *testing.T) { @@ -302,13 +360,15 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"application/json"}}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) @@ -330,20 +390,22 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"application/json"}}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - ctx, - logrus.NewEntry(logger), - req, - nil, - nil, - envs, + RoundTripper: &MockRoundTrip{Response: resp}, + context: ctx, + logger: logrus.NewEntry(logger), + request: req, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) require.Nil(t, err) require.Equal(t, http.StatusInternalServerError, resp.StatusCode) bodyBytes, err := io.ReadAll(resp.Body) require.Nil(t, err) - require.True(t, strings.Contains(string(bodyBytes), "Error while retrieving user bindings")) + require.Contains(t, string(bodyBytes), "error while retrieving user bindings") }) t.Run("failure on create rego input", func(t *testing.T) { @@ -358,22 +420,26 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"application/json"}}, } transport := &OPATransport{ - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - &openapi.RondConfig{ + RoundTripper: &MockRoundTrip{Response: resp}, + context: req.Context(), + logger: logrus.NewEntry(logger), + request: req, + permission: &openapi.RondConfig{ ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, }, - PartialResultsEvaluators{"my_policy": {}}, - envs, + partialResultsEvaluators: PartialResultsEvaluators{"my_policy": {}}, + userHeaders: types.UserHeadersKeys{ + IDHeaderKey: "useridheader", + GroupsHeaderKey: "usergroupsheader", + PropertiesHeaderKey: "userpropertiesheader", + }, } resp, err := transport.RoundTrip(req) require.Nil(t, err) require.Equal(t, http.StatusInternalServerError, resp.StatusCode) bodyBytes, err := io.ReadAll(resp.Body) require.Nil(t, err) - require.True(t, strings.Contains(string(bodyBytes), "user properties header is not valid")) + require.Contains(t, string(bodyBytes), "user properties header is not valid") }) } diff --git a/core/opaevaluator.go b/core/opaevaluator.go index dd06ab83..776a0f9a 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -28,7 +28,6 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" - "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" @@ -37,7 +36,6 @@ import ( "github.com/rond-authz/rond/custom_builtins" - "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" @@ -66,11 +64,11 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(policy string, ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (*PartialEvaluator, error) { +func createPartialEvaluator(policy string, ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { glogger.Get(ctx).Infof("precomputing rego query for allow policy: %s", policy) policyEvaluatorTime := time.Now() - partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, mongoClient, env) + partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, mongoClient, options) if err == nil { glogger.Get(ctx).Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime)) return &PartialEvaluator{ @@ -80,7 +78,7 @@ func createPartialEvaluator(policy string, ctx context.Context, mongoClient type return nil, err } -func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, env config.EnvironmentVariables) (PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { policyEvaluators := PartialResultsEvaluators{} for path, OASContent := range oas.Paths { for verb, verbConfig := range OASContent { @@ -98,7 +96,7 @@ func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *o } if _, ok := policyEvaluators[allowPolicy]; !ok { - evaluator, err := createPartialEvaluator(allowPolicy, ctx, mongoClient, oas, opaModuleConfig, env) + evaluator, err := createPartialEvaluator(allowPolicy, ctx, mongoClient, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -109,7 +107,7 @@ func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *o if responsePolicy != "" { if _, ok := policyEvaluators[responsePolicy]; !ok { - evaluator, err := createPartialEvaluator(responsePolicy, ctx, mongoClient, oas, opaModuleConfig, env) + evaluator, err := createPartialEvaluator(responsePolicy, ctx, mongoClient, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -157,7 +155,14 @@ func (h printHook) Print(_ print.Context, message string) error { return err } -func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, env config.EnvironmentVariables) (*OPAEvaluator, error) { +type EvaluatorOptions struct { + EnablePrintStatements bool +} + +func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { + if options == nil { + options = &EvaluatorOptions{} + } inputTerm, err := ast.ParseTerm(string(input)) if err != nil { return nil, fmt.Errorf("failed input parse: %v", err) @@ -171,7 +176,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod rego.ParsedInput(inputTerm.Value), rego.Unknowns(Unknowns), rego.Capabilities(ast.CapabilitiesForThisVersion()), - rego.EnablePrintStatements(env.LogLevel == config.TraceLogLevel), + rego.EnablePrintStatements(options.EnablePrintStatements), rego.PrintHook(NewPrintHook(os.Stdout, policy)), custom_builtins.GetHeaderFunction, custom_builtins.MongoFindOne, @@ -185,7 +190,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod }, nil } -func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.Request, env config.EnvironmentVariables, policy string, input []byte, responseBody interface{}) (*OPAEvaluator, error) { +func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.Request, policy string, input []byte, responseBody interface{}, options *EvaluatorOptions) (*OPAEvaluator, error) { opaModuleConfig, err := GetOPAModuleConfig(req.Context()) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no OPA module configuration found in context") @@ -197,7 +202,7 @@ func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.R }).Info("Policy to be evaluated") opaEvaluatorInstanceTime := time.Now() - evaluator, err := NewOPAEvaluator(ctx, policy, opaModuleConfig, input, env) + evaluator, err := NewOPAEvaluator(ctx, policy, opaModuleConfig, input, options) if err != nil { logger.WithError(err).Error("failed RBAC policy creation") return nil, err @@ -206,7 +211,11 @@ func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.R return evaluator, nil } -func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, mongoClient types.IMongoClient, env config.EnvironmentVariables) (*rego.PartialResult, error) { +func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, mongoClient types.IMongoClient, evaluatorOptions *EvaluatorOptions) (*rego.PartialResult, error) { + if evaluatorOptions == nil { + evaluatorOptions = &EvaluatorOptions{} + } + sanitizedPolicy := strings.Replace(policy, ".", "_", -1) queryString := fmt.Sprintf("data.policies.%s", sanitizedPolicy) @@ -214,7 +223,7 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf rego.Query(queryString), rego.Module(opaModuleConfig.Name, opaModuleConfig.Content), rego.Unknowns(Unknowns), - rego.EnablePrintStatements(env.LogLevel == config.TraceLogLevel), + rego.EnablePrintStatements(evaluatorOptions.EnablePrintStatements), rego.PrintHook(NewPrintHook(os.Stdout, policy)), rego.Capabilities(ast.CapabilitiesForThisVersion()), custom_builtins.GetHeaderFunction, @@ -228,7 +237,11 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf return &results, err } -func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, env config.EnvironmentVariables) (*OPAEvaluator, error) { +func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { + if options == nil { + options = &EvaluatorOptions{} + } + if eval, ok := partialEvaluators[policy]; ok { inputTerm, err := ast.ParseTerm(string(input)) if err != nil { @@ -237,7 +250,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con evaluator := eval.PartialEvaluator.Rego( rego.ParsedInput(inputTerm.Value), - rego.EnablePrintStatements(env.LogLevel == config.TraceLogLevel), + rego.EnablePrintStatements(options.EnablePrintStatements), rego.PrintHook(NewPrintHook(os.Stdout, policy)), ) @@ -365,65 +378,19 @@ func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission return dataFromEvaluation, nil, nil } -func CreateRegoQueryInput(req *http.Request, env config.EnvironmentVariables, enableResourcePermissionsMapOptimization bool, user types.User, responseBody interface{}) ([]byte, error) { - requestContext := req.Context() - logger := glogger.Get(requestContext) - opaInputCreationTime := time.Now() - userProperties := make(map[string]interface{}) - _, err := utils.UnmarshalHeader(req.Header, env.UserPropertiesHeader, &userProperties) - if err != nil { - return nil, fmt.Errorf("user properties header is not valid: %s", err.Error()) - } - - userGroup := make([]string, 0) - userGroupsNotSplitted := req.Header.Get(env.UserGroupsHeader) - if userGroupsNotSplitted != "" { - userGroup = strings.Split(userGroupsNotSplitted, ",") - } - - var permissionsMap PermissionsOnResourceMap - if enableResourcePermissionsMapOptimization { - logger.Info("preparing optimized resourcePermissionMap for OPA evaluator") - opaPermissionsMapTime := time.Now() - permissionsMap = buildOptimizedResourcePermissionsMap(user) - logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Tracef("resource permission map creation") - } +type RegoInputOptions struct { + EnableResourcePermissionsMapOptimization bool +} - input := Input{ - ClientType: req.Header.Get(env.ClientTypeHeader), - Request: InputRequest{ - Method: req.Method, - Path: req.URL.Path, - Headers: req.Header, - Query: req.URL.Query(), - PathParams: mux.Vars(req), - }, - Response: InputResponse{ - Body: responseBody, - }, - User: InputUser{ - Bindings: user.UserBindings, - Roles: user.UserRoles, - Properties: userProperties, - Groups: userGroup, - ResourcePermissionsMap: permissionsMap, - }, - } +func CreateRegoQueryInput( + logger *logrus.Entry, + input Input, + options RegoInputOptions, +) ([]byte, error) { + opaInputCreationTime := time.Now() - shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && - req.ContentLength > 0 && - (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) + input.buildOptimizedResourcePermissionsMap(logger, options.EnableResourcePermissionsMapOptimization) - if shouldParseJSONBody { - bodyBytes, err := io.ReadAll(req.Body) - if err != nil { - return nil, fmt.Errorf("failed request body parse: %s", err.Error()) - } - if err := json.Unmarshal(bodyBytes, &input.Request.Body); err != nil { - return nil, fmt.Errorf("failed request body deserialization: %s", err.Error()) - } - req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - } inputBytes, err := json.Marshal(input) if err != nil { return nil, fmt.Errorf("failed input JSON encode: %v", err) @@ -432,33 +399,6 @@ func CreateRegoQueryInput(req *http.Request, env config.EnvironmentVariables, en return inputBytes, nil } -func buildOptimizedResourcePermissionsMap(user types.User) PermissionsOnResourceMap { - permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) - rolesMap := buildRolesMap(user.UserRoles) - for _, binding := range user.UserBindings { - if binding.Resource == nil { - continue - } - - for _, role := range binding.Roles { - rolePermissions, ok := rolesMap[role] - if !ok { - continue - } - - for _, permission := range rolePermissions { - key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) - permissionsOnResourceMap[key] = true - } - } - for _, permission := range binding.Permissions { - key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) - permissionsOnResourceMap[key] = true - } - } - return permissionsOnResourceMap -} - func buildRolesMap(roles []types.Role) map[string][]string { var rolesMap = make(map[string][]string, 0) for _, role := range roles { @@ -509,6 +449,37 @@ type Input struct { ClientType string `json:"clientType,omitempty"` User InputUser `json:"user"` } + +func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, enableResourcePermissionsMapOptimization bool) { + if !enableResourcePermissionsMapOptimization { + return + } + logger.Info("preparing optimized resourcePermissionMap for OPA evaluator") + opaPermissionsMapTime := time.Now() + + user := input.User + permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) + rolesMap := buildRolesMap(user.Roles) + for _, binding := range user.Bindings { + for _, role := range binding.Roles { + rolePermissions, ok := rolesMap[role] + if !ok { + continue + } + for _, permission := range rolePermissions { + key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) + permissionsOnResourceMap[key] = true + } + } + for _, permission := range binding.Permissions { + key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) + permissionsOnResourceMap[key] = true + } + } + input.User.ResourcePermissionsMap = permissionsOnResourceMap + logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Tracef("resource permission map creation") +} + type InputRequest struct { Body interface{} `json:"body,omitempty"` Headers http.Header `json:"headers,omitempty"` @@ -568,3 +539,48 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { Content: string(fileContent), }, nil } + +func InputFromRequest( + req *http.Request, + user types.User, + clientTypeHeaderKey string, + pathParams map[string]string, + responseBody any, +) (Input, error) { + shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && + req.ContentLength > 0 && + (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) + + var requestBody any + if shouldParseJSONBody { + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + return Input{}, fmt.Errorf("failed request body parse: %s", err.Error()) + } + if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { + return Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error()) + } + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + } + + return Input{ + ClientType: req.Header.Get(clientTypeHeaderKey), + Request: InputRequest{ + Method: req.Method, + Path: req.URL.Path, + Headers: req.Header, + Query: req.URL.Query(), + PathParams: pathParams, + Body: requestBody, + }, + Response: InputResponse{ + Body: responseBody, + }, + User: InputUser{ + Properties: user.Properties, + Groups: user.UserGroups, + Bindings: user.UserBindings, + Roles: user.UserRoles, + }, + }, nil +} diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 1d342883..7d345abf 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -22,7 +22,6 @@ import ( "net/http" "net/http/httptest" "regexp" - "strings" "testing" "github.com/rond-authz/rond/internal/config" @@ -40,11 +39,10 @@ import ( ) func TestNewOPAEvaluator(t *testing.T) { - envs := config.EnvironmentVariables{} input := map[string]interface{}{} inputBytes, _ := json.Marshal(input) t.Run("policy sanitization", func(t *testing.T) { - evaluator, _ := NewOPAEvaluator(context.Background(), "very.composed.policy", &OPAModuleConfig{Content: "package policies very_composed_policy {true}"}, inputBytes, envs) + evaluator, _ := NewOPAEvaluator(context.Background(), "very.composed.policy", &OPAModuleConfig{Content: "package policies very_composed_policy {true}"}, inputBytes, nil) result, err := evaluator.PolicyEvaluator.Eval(context.TODO()) require.Nil(t, err, "unexpected error") @@ -57,97 +55,86 @@ func TestNewOPAEvaluator(t *testing.T) { } func TestCreateRegoInput(t *testing.T) { - env := config.EnvironmentVariables{} - user := types.User{} - enableResourcePermissionsMapOptimization := false - - t.Run("headers", func(t *testing.T) { - t.Run("allow empty userproperties header", func(t *testing.T) { - env := config.EnvironmentVariables{ - UserPropertiesHeader: "userproperties", - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Set("userproperties", "") - - _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - }) - - t.Run("fail on invalid userproperties header value", func(t *testing.T) { - env := config.EnvironmentVariables{ - UserPropertiesHeader: "userproperties", - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Set("userproperties", "1") + logrusLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(logrusLogger) - _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Error(t, err) - }) + t.Run("returns correctly", func(t *testing.T) { + actual, err := CreateRegoQueryInput(logger, Input{}, RegoInputOptions{}) + require.NoError(t, err) + require.Equal(t, "{\"request\":{\"method\":\"\",\"path\":\"\"},\"response\":{},\"user\":{}}", string(actual)) }) - t.Run("body integration", func(t *testing.T) { - expectedRequestBody := []byte(`{"Key":42}`) - reqBody := struct{ Key int }{ - Key: 42, + t.Run("buildOptimizedResourcePermissionsMap", func(t *testing.T) { + user := InputUser{ + Roles: []types.Role{ + { + RoleID: "role1", + Permissions: []string{"permission1", "permission2"}, + }, + { + RoleID: "role2", + Permissions: []string{"permission3", "permission4"}, + }, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type2", + ResourceID: "resource2", + }, + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, } - reqBodyBytes, err := json.Marshal(reqBody) - require.Nil(t, err, "Unexpected error") - - t.Run("ignored on method GET", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - - inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) - }) - - t.Run("ignore nil body on method POST", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", nil) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - - inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) - }) - t.Run("added on accepted methods", func(t *testing.T) { - acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} - - for _, method := range acceptedMethods { - req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - - require.True(t, strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody)), "Unexpected body for method %s", method) + t.Run("insert map", func(t *testing.T) { + input := Input{ + User: user, } - }) - t.Run("added with content-type specifying charset", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") - inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - - require.True(t, strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody)), "Unexpected body for method %s", http.MethodPost) - }) - - t.Run("reject on method POST but with invalid body", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - _, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.True(t, err != nil) + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission1:type1:resource1": true, + "permission2:type1:resource1": true, + "permissionNotInRole1:type1:resource1": true, + "permission3:type2:resource2": true, + "permission4:type2:resource2": true, + "permission1:type3:resource3": true, + "permission2:type3:resource3": true, + "permission3:type3:resource3": true, + "permission4:type3:resource3": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) }) - t.Run("ignore body on method POST but with another content type", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") + t.Run("do nothing if enableResourcePermissionsMapOptimization is false", func(t *testing.T) { + input := Input{ + User: user, + } - inputBytes, err := CreateRegoQueryInput(req, env, enableResourcePermissionsMapOptimization, user, nil) - require.Nil(t, err, "Unexpected error") - require.True(t, !strings.Contains(string(inputBytes), fmt.Sprintf(`"body":%s`, expectedRequestBody))) + input.buildOptimizedResourcePermissionsMap(logger, false) + require.Nil(t, input.User.ResourcePermissionsMap) }) }) + } func TestCreatePolicyEvaluators(t *testing.T) { @@ -155,17 +142,17 @@ func TestCreatePolicyEvaluators(t *testing.T) { log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) - envs := config.EnvironmentVariables{ + opaModuleDirectory := "../mocks/rego-policies" + loadOptions := openapi.LoadOptions{ APIPermissionsFilePath: "../mocks/simplifiedMock.json", - OPAModulesDirectory: "../mocks/rego-policies", } - openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) + openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, loadOptions) require.NoError(t, err, "unexpected error") - opaModuleConfig, err := LoadRegoModule(envs.OPAModulesDirectory) + opaModuleConfig, err := LoadRegoModule(opaModuleDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) + policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -174,17 +161,18 @@ func TestCreatePolicyEvaluators(t *testing.T) { log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) - envs := config.EnvironmentVariables{ + opaModulesDirectory := "../mocks/rego-policies" + + loadOptions := openapi.LoadOptions{ APIPermissionsFilePath: "../mocks/pathsConfigAllInclusive.json", - OPAModulesDirectory: "../mocks/rego-policies", } - openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, envs) + openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, loadOptions) require.NoError(t, err, "unexpected error") - opaModuleConfig, err := LoadRegoModule(envs.OPAModulesDirectory) + opaModuleConfig, err := LoadRegoModule(opaModulesDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, envs) + policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -209,62 +197,7 @@ func TestBuildRolesMap(t *testing.T) { require.Equal(t, expected, result) } -func TestBuildOptimizedResourcePermissionsMap(t *testing.T) { - user := types.User{ - UserRoles: []types.Role{ - { - RoleID: "role1", - Permissions: []string{"permission1", "permission2"}, - }, - { - RoleID: "role2", - Permissions: []string{"permission3", "permission4"}, - }, - }, - UserBindings: []types.Binding{ - { - Resource: &types.Resource{ - ResourceType: "type1", - ResourceID: "resource1", - }, - Roles: []string{"role1"}, - Permissions: []string{"permissionNotInRole1"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type2", - ResourceID: "resource2", - }, - Roles: []string{"role2"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type3", - ResourceID: "resource3", - }, - Roles: []string{"role1", "role2"}, - Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, - }, - }, - } - result := buildOptimizedResourcePermissionsMap(user) - expected := PermissionsOnResourceMap{ - "permission1:type1:resource1": true, - "permission2:type1:resource1": true, - "permissionNotInRole1:type1:resource1": true, - "permission3:type2:resource2": true, - "permission4:type2:resource2": true, - "permission1:type3:resource3": true, - "permission2:type3:resource3": true, - "permission3:type3:resource3": true, - "permission4:type3:resource3": true, - "permissionNotInRole2:type3:resource3": true, - "permissionNotInRole3:type3:resource3": true, - } - require.Equal(t, expected, result) -} func TestCreateQueryEvaluator(t *testing.T) { - envs := config.EnvironmentVariables{} policy := `package policies allow { true @@ -302,13 +235,13 @@ column_policy{ inputBytes, _ := json.Marshal(input) t.Run("create evaluator with allowPolicy", func(t *testing.T) { - evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, envs, permission.AllowPermission, inputBytes, nil) + evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, permission.AllowPermission, inputBytes, nil, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) t.Run("create evaluator with policy for column filtering", func(t *testing.T) { - evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, envs, permission.ResponseFilter.Policy, inputBytes, nil) + evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, permission.ResponseFilter.Policy, inputBytes, nil, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) @@ -337,15 +270,21 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { bindings = append(bindings, binding) } - user := types.User{ - UserRoles: roles, - UserBindings: bindings, + user := InputUser{ + Roles: roles, + Bindings: bindings, + } + + logrusLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(logrusLogger) + input := Input{ + User: user, } b.ResetTimer() for n := 0; n < b.N; n++ { b.StartTimer() - buildOptimizedResourcePermissionsMap(user) + input.buildOptimizedResourcePermissionsMap(logger, true) b.StopTimer() } } @@ -397,7 +336,6 @@ func createContext( func TestGetHeaderFunction(t *testing.T) { headerKeyMocked := "exampleKey" headerValueMocked := "value" - env := config.EnvironmentVariables{} opaModule := &OPAModuleConfig{ Name: "example.rego", @@ -414,7 +352,7 @@ func TestGetHeaderFunction(t *testing.T) { } inputBytes, _ := json.Marshal(input) - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) + opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) @@ -433,7 +371,7 @@ func TestGetHeaderFunction(t *testing.T) { } inputBytes, _ := json.Marshal(input) - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, env) + opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) @@ -478,3 +416,73 @@ func TestGetPolicyEvaluators(t *testing.T) { require.True(t, opaEval != nil, "OPA Module config not found.") }) } + +func TestInputFromRequest(t *testing.T) { + user := types.User{} + clientTypeHeaderKey := "clienttypeheader" + pathParams := map[string]string{} + + t.Run("request body integration", func(t *testing.T) { + expectedRequestBody := map[string]interface{}{ + "Key": float64(42), + } + reqBody := struct{ Key int }{ + Key: 42, + } + reqBodyBytes, err := json.Marshal(reqBody) + require.Nil(t, err, "Unexpected error") + + t.Run("ignored on method GET", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("ignore nil body on method POST", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("added on accepted methods", func(t *testing.T) { + acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} + + for _, method := range acceptedMethods { + req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + } + }) + + t.Run("added with content-type specifying charset", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + }) + + t.Run("reject on method POST but with invalid body", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + _, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.ErrorContains(t, err, "failed request body deserialization:") + }) + + t.Run("ignore body on method POST but with another content type", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + }) +} diff --git a/core/opamiddleware.go b/core/opamiddleware.go index 3cd42149..72e2b7b5 100644 --- a/core/opamiddleware.go +++ b/core/opamiddleware.go @@ -20,7 +20,6 @@ import ( "net/http" "strings" - "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" @@ -29,12 +28,18 @@ import ( "github.com/sirupsen/logrus" ) +type OPAMiddlewareOptions struct { + IsStandalone bool + PathPrefixStandalone string +} + func OPAMiddleware( opaModuleConfig *OPAModuleConfig, openAPISpec *openapi.OpenAPISpec, - envs *config.EnvironmentVariables, policyEvaluators PartialResultsEvaluators, routesToNotProxy []string, + targetServiceOASPath string, + options *OPAMiddlewareOptions, ) mux.MiddlewareFunc { OASrouter, err := openAPISpec.PrepareOASRouter() if err != nil { @@ -49,14 +54,14 @@ func OPAMiddleware( } path := r.URL.EscapedPath() - if envs.Standalone { - path = strings.Replace(r.URL.EscapedPath(), envs.PathPrefixStandalone, "", 1) + if options != nil && options.IsStandalone { + path = strings.Replace(r.URL.EscapedPath(), options.PathPrefixStandalone, "", 1) } logger := glogger.Get(r.Context()) permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) - if r.Method == http.MethodGet && r.URL.Path == envs.TargetServiceOASPath && permission.RequestFlow.PolicyName == "" { + if r.Method == http.MethodGet && r.URL.Path == targetServiceOASPath && permission.RequestFlow.PolicyName == "" { fields := logrus.Fields{} if err != nil { fields["error"] = logrus.Fields{"message": err.Error()} diff --git a/core/opamiddleware_test.go b/core/opamiddleware_test.go index 93dee484..a5f48253 100644 --- a/core/opamiddleware_test.go +++ b/core/opamiddleware_test.go @@ -22,7 +22,6 @@ import ( "os" "testing" - "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" @@ -31,7 +30,6 @@ import ( ) func TestOPAMiddleware(t *testing.T) { - var envs = config.EnvironmentVariables{} var partialEvaluators = PartialResultsEvaluators{} routesNotToProxy := make([]string, 0) @@ -46,7 +44,7 @@ todo { true }`, require.NoError(t, err) err = json.Unmarshal(openAPISpecContent, &openAPISpec) require.NoError(t, err) - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) t.Run(`missing oas paths`, func(t *testing.T) { builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -107,10 +105,8 @@ foobar { true }`, t.Run(`ok - path is known on oas with no permission declared`, func(t *testing.T) { openAPISpec, err := openapi.LoadOASFile("../mocks/documentationPathMock.json") require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + targetServiceOASPath := "/documentation/json" + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -125,10 +121,8 @@ foobar { true }`, t.Run(`ok - path is missing on oas and request is equal to serviceTargetOASPath`, func(t *testing.T) { openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + targetServiceOASPath := "/documentation/json" + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -143,10 +137,8 @@ foobar { true }`, t.Run(`ok - path is NOT known on oas but is proxied anyway`, func(t *testing.T) { openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) - var envs = config.EnvironmentVariables{ - TargetServiceOASPath: "/documentation/custom/json", - } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + targetServiceOASPath := "/documentation/custom/json" + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -170,7 +162,7 @@ foobar { true }`, todo { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -192,7 +184,7 @@ todo { true }`, foobar { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -214,7 +206,7 @@ foobar { true }`, very_very_composed_permission { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -236,12 +228,12 @@ very_very_composed_permission { true }`, very_very_composed_permission_with_eval { true }`, } - envs := config.EnvironmentVariables{ - Standalone: false, - PathPrefixStandalone: "/eval", // default value + options := &OPAMiddlewareOptions{ + IsStandalone: false, + PathPrefixStandalone: "/eval", } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -264,10 +256,9 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.Nil(t, err) - - envs := config.EnvironmentVariables{ - Standalone: true, - PathPrefixStandalone: "/eval", // default value + options := &OPAMiddlewareOptions{ + IsStandalone: true, + PathPrefixStandalone: "/eval", } t.Run("injects correct path removing prefix", func(t *testing.T) { @@ -277,7 +268,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { very_very_composed_permission { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -299,7 +290,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { very_very_composed_permission_with_eval { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, &envs, partialEvaluators, routesNotToProxy) + middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") diff --git a/internal/config/env.go b/internal/config/env.go index 751ba76a..5d630a10 100644 --- a/internal/config/env.go +++ b/internal/config/env.go @@ -25,13 +25,13 @@ import ( ) const ( - APIPermissionsFilePathEnvKey = "API_PERMISSIONS_FILE_PATH" - TargetServiceOASPathEnvKey = "TARGET_SERVICE_OAS_PATH" - StandaloneEnvKey = "STANDALONE" - TargetServiceHostEnvKey = "TARGET_SERVICE_HOST" - BindingsCrudServiceURL = "BINDINGS_CRUD_SERVICE_URL" + apiPermissionsFilePathEnvKey = "API_PERMISSIONS_FILE_PATH" + targetServiceOASPathEnvKey = "TARGET_SERVICE_OAS_PATH" + standaloneEnvKey = "STANDALONE" + targetServiceHostEnvKey = "TARGET_SERVICE_HOST" + bindingsCrudServiceURL = "BINDINGS_CRUD_SERVICE_URL" - TraceLogLevel = "trace" + traceLogLevel = "trace" ) // EnvironmentVariables struct with the mapping of desired @@ -76,11 +76,11 @@ var EnvVariablesConfig = []configlib.EnvConfig{ DefaultValue: "latest", }, { - Key: TargetServiceHostEnvKey, + Key: targetServiceHostEnvKey, Variable: "TargetServiceHost", }, { - Key: TargetServiceOASPathEnvKey, + Key: targetServiceOASPathEnvKey, Variable: "TargetServiceOASPath", }, { @@ -89,7 +89,7 @@ var EnvVariablesConfig = []configlib.EnvConfig{ Required: true, }, { - Key: APIPermissionsFilePathEnvKey, + Key: apiPermissionsFilePathEnvKey, Variable: "APIPermissionsFilePath", }, { @@ -130,7 +130,7 @@ var EnvVariablesConfig = []configlib.EnvConfig{ Variable: "RolesCollectionName", }, { - Key: StandaloneEnvKey, + Key: standaloneEnvKey, Variable: "Standalone", }, { @@ -139,7 +139,7 @@ var EnvVariablesConfig = []configlib.EnvConfig{ DefaultValue: "/eval", }, { - Key: BindingsCrudServiceURL, + Key: bindingsCrudServiceURL, Variable: "BindingsCrudServiceURL", }, { @@ -184,11 +184,15 @@ func GetEnvOrDie() EnvironmentVariables { } if env.TargetServiceHost == "" && !env.Standalone { - panic(fmt.Errorf("missing environment variables, one of %s or %s set to true is required", TargetServiceHostEnvKey, StandaloneEnvKey)) + panic(fmt.Errorf("missing environment variables, one of %s or %s set to true is required", targetServiceHostEnvKey, standaloneEnvKey)) } if env.Standalone && env.BindingsCrudServiceURL == "" { - panic(fmt.Errorf("missing environment variables, %s must be set if mode is standalone", BindingsCrudServiceURL)) + panic(fmt.Errorf("missing environment variables, %s must be set if mode is standalone", bindingsCrudServiceURL)) + } + + if env.APIPermissionsFilePath == "" && env.TargetServiceOASPath == "" { + panic(fmt.Errorf("missing environment variables, one of %s or %s is required", apiPermissionsFilePathEnvKey, targetServiceOASPathEnvKey)) } return env @@ -215,3 +219,7 @@ func (env EnvironmentVariables) GetAdditionalHeadersToProxy() []string { } return customHeaders } + +func (env EnvironmentVariables) IsTraceLogLevel() bool { + return env.LogLevel == traceLogLevel +} diff --git a/internal/config/env_test.go b/internal/config/env_test.go index 4035e2e4..7c9a477e 100644 --- a/internal/config/env_test.go +++ b/internal/config/env_test.go @@ -79,6 +79,7 @@ func TestGetEnvOrDie(t *testing.T) { ServiceVersion: "latest", OPAModulesDirectory: "/modules", + APIPermissionsFilePath: "/oas", AdditionalHeadersToProxy: "miauserid", ExposeMetrics: true, } @@ -86,6 +87,7 @@ func TestGetEnvOrDie(t *testing.T) { t.Run(`returns correctly - with TargetServiceHost`, func(t *testing.T) { otherEnvs := []env{ {name: "TARGET_SERVICE_HOST", value: "http://localhost:3000"}, + {name: apiPermissionsFilePathEnvKey, value: "/oas"}, } envs := append(requiredEnvs, otherEnvs...) setEnvs(t, envs) @@ -101,6 +103,7 @@ func TestGetEnvOrDie(t *testing.T) { otherEnvs := []env{ {name: "STANDALONE", value: "true"}, {name: "BINDINGS_CRUD_SERVICE_URL", value: "http://crud-client"}, + {name: apiPermissionsFilePathEnvKey, value: "/oas"}, } envs := append(requiredEnvs, otherEnvs...) setEnvs(t, envs) @@ -113,9 +116,10 @@ func TestGetEnvOrDie(t *testing.T) { require.Equal(t, expectedEnvs, actualEnvs, "Unexpected envs variables.") }) - t.Run(`returns error - with Standalone and not BindingsCrudServiceURL`, func(t *testing.T) { + t.Run(`throws - with Standalone and not BindingsCrudServiceURL`, func(t *testing.T) { otherEnvs := []env{ {name: "STANDALONE", value: "true"}, + {name: apiPermissionsFilePathEnvKey, value: "/oas"}, } envs := append(requiredEnvs, otherEnvs...) setEnvs(t, envs) @@ -123,7 +127,6 @@ func TestGetEnvOrDie(t *testing.T) { defer func() { r := recover() t.Logf("expected panic %+v", r) - }() GetEnvOrDie() @@ -133,24 +136,56 @@ func TestGetEnvOrDie(t *testing.T) { t.Run(`throws - with Standalone to false`, func(t *testing.T) { otherEnvs := []env{ {name: "STANDALONE", value: "false"}, + {name: apiPermissionsFilePathEnvKey, value: "/oas"}, } envs := append(requiredEnvs, otherEnvs...) setEnvs(t, envs) - require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", TargetServiceHostEnvKey, StandaloneEnvKey), func() { + require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", targetServiceHostEnvKey, standaloneEnvKey), func() { GetEnvOrDie() }, "Unexpected envs variables.") }) t.Run(`throws - no Standalone or TargetServiceHost`, func(t *testing.T) { - otherEnvs := []env{} + otherEnvs := []env{ + {name: apiPermissionsFilePathEnvKey, value: "/oas"}, + } + envs := append(requiredEnvs, otherEnvs...) + setEnvs(t, envs) + + require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", targetServiceHostEnvKey, standaloneEnvKey), func() { + GetEnvOrDie() + }, "Unexpected envs variables.") + }) + + t.Run(`throws - no APIPermissionsFilePath or TargetServiceOASPath`, func(t *testing.T) { + otherEnvs := []env{ + {name: "TARGET_SERVICE_HOST", value: "http://localhost:3000"}, + } envs := append(requiredEnvs, otherEnvs...) setEnvs(t, envs) - require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s set to true is required", TargetServiceHostEnvKey, StandaloneEnvKey), func() { + require.PanicsWithError(t, fmt.Sprintf("missing environment variables, one of %s or %s is required", apiPermissionsFilePathEnvKey, targetServiceOASPathEnvKey), func() { GetEnvOrDie() }, "Unexpected envs variables.") }) + + t.Run(`returns correctly - TargetServiceOASPath set`, func(t *testing.T) { + otherEnvs := []env{ + {name: "TARGET_SERVICE_HOST", value: "http://localhost:3000"}, + {name: "TARGET_SERVICE_OAS_PATH", value: "/path"}, + } + envs := append(requiredEnvs, otherEnvs...) + setEnvs(t, envs) + + actualEnvs := GetEnvOrDie() + expectedEnvs := defaultAndRequiredEnvironmentVariables + expectedEnvs.TargetServiceHost = "http://localhost:3000" + expectedEnvs.APIPermissionsFilePath = "" + expectedEnvs.TargetServiceOASPath = "/path" + + require.Equal(t, expectedEnvs, actualEnvs, "Unexpected envs variables.") + }) } type env struct { @@ -208,3 +243,19 @@ func TestGetAdditionalHeadersToProxy(t *testing.T) { require.Equal(t, []string{"head1", "head2", "x-forwarded-for", "x-request-id", "x-forwarded-proto", "x-forwarded-host"}, headersToProxy) }) } + +func TestIsTraceLogLevel(t *testing.T) { + t.Run("true", func(t *testing.T) { + env := EnvironmentVariables{ + LogLevel: traceLogLevel, + } + + require.True(t, env.IsTraceLogLevel()) + }) + + t.Run("false", func(t *testing.T) { + env := EnvironmentVariables{} + + require.False(t, env.IsTraceLogLevel()) + }) +} diff --git a/internal/mongoclient/mongoclient.go b/internal/mongoclient/mongoclient.go index bb79cc8d..7f6cbc4a 100644 --- a/internal/mongoclient/mongoclient.go +++ b/internal/mongoclient/mongoclient.go @@ -28,6 +28,7 @@ import ( "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" + "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -282,23 +283,30 @@ func RolesIDsFromBindings(bindings []types.Binding) []string { return rolesIds } -func RetrieveUserBindingsAndRoles(logger *logrus.Entry, req *http.Request, env config.EnvironmentVariables) (types.User, error) { +func RetrieveUserBindingsAndRoles(logger *logrus.Entry, req *http.Request, userHeaders types.UserHeadersKeys) (types.User, error) { requestContext := req.Context() mongoClient, err := GetMongoClientFromContext(requestContext) if err != nil { - return types.User{}, fmt.Errorf("Unexpected error retrieving MongoDB Client from request context") + return types.User{}, fmt.Errorf("unexpected error retrieving MongoDB Client from request context") } var user types.User - user.UserGroups = strings.Split(req.Header.Get(env.UserGroupsHeader), ",") - user.UserID = req.Header.Get(env.UserIdHeader) + user.UserGroups = strings.Split(req.Header.Get(userHeaders.GroupsHeaderKey), ",") + user.UserID = req.Header.Get(userHeaders.IDHeaderKey) + + userProperties := make(map[string]interface{}) + _, err = utils.UnmarshalHeader(req.Header, userHeaders.PropertiesHeaderKey, &userProperties) + if err != nil { + return types.User{}, fmt.Errorf("user properties header is not valid: %s", err.Error()) + } + user.Properties = userProperties if mongoClient != nil && user.UserID != "" { user.UserBindings, err = mongoClient.RetrieveUserBindings(requestContext, &user) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("something went wrong while retrieving user bindings") - return types.User{}, fmt.Errorf("Error while retrieving user bindings: %s", err.Error()) + return types.User{}, fmt.Errorf("error while retrieving user bindings: %s", err.Error()) } userRolesIds := RolesIDsFromBindings(user.UserBindings) @@ -306,7 +314,7 @@ func RetrieveUserBindingsAndRoles(logger *logrus.Entry, req *http.Request, env c if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("something went wrong while retrieving user roles") - return types.User{}, fmt.Errorf("Error while retrieving user Roles: %s", err.Error()) + return types.User{}, fmt.Errorf("error while retrieving user Roles: %s", err.Error()) } logger.WithFields(logrus.Fields{ "foundBindingsLength": len(user.UserBindings), diff --git a/internal/mongoclient/mongoclient_test.go b/internal/mongoclient/mongoclient_test.go index af4b073e..cb9c956f 100644 --- a/internal/mongoclient/mongoclient_test.go +++ b/internal/mongoclient/mongoclient_test.go @@ -473,16 +473,17 @@ func TestRolesIDSFromBindings(t *testing.T) { func TestRetrieveUserBindingsAndRoles(t *testing.T) { logger, _ := test.NewNullLogger() - env := config.EnvironmentVariables{ - UserGroupsHeader: "thegroupsheader", - UserIdHeader: "theuserheader", + userHeaders := types.UserHeadersKeys{ + GroupsHeaderKey: "thegroupsheader", + IDHeaderKey: "theuserheader", + PropertiesHeaderKey: "userproperties", } t.Run("fails if MongoClient is in context but of the wrong type", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(context.WithValue(req.Context(), types.MongoClientContextKey{}, "test")) - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, env) + _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, userHeaders) require.Error(t, err, "Unexpected error retrieving MongoDB Client from request context") }) @@ -491,11 +492,12 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, env) + user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, userHeaders) require.NoError(t, err) require.Equal(t, types.User{ UserID: "userId", UserGroups: []string{"group1", "group2"}, + Properties: map[string]interface{}{}, }, user) }) @@ -506,7 +508,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(WithMongoClient(req.Context(), mock)) - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) + _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) require.NoError(t, err) }) @@ -519,7 +521,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) + _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) require.Error(t, err, "Error while retrieving user bindings: some error") }) @@ -535,7 +537,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) + _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) require.Error(t, err, "Error while retrieving user Roles: some error 2") }) @@ -556,7 +558,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, env) + user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) require.NoError(t, err) require.Equal(t, types.User{ UserID: "userId", @@ -570,6 +572,30 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { {RoleID: "r2", Permissions: []string{"p3", "p4"}}, {RoleID: "r3", Permissions: []string{"p5"}}, }, + Properties: map[string]interface{}{}, + }, user) + }) + + t.Run("allow empty userproperties header", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("userproperties", "") + req.Header.Set("thegroupsheader", "group1,group2") + req.Header.Set("theuserheader", "userId") + + user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + require.NoError(t, err) + require.Equal(t, types.User{ + UserID: "userId", + UserGroups: []string{"group1", "group2"}, + Properties: map[string]interface{}{}, }, user) }) + + t.Run("fail on invalid userproperties header value", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("userproperties", "1") + + _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + require.ErrorContains(t, err, "user properties header is not valid:") + }) } diff --git a/internal/types/rbactypes.go b/internal/types/rbactypes.go deleted file mode 100644 index 46705b32..00000000 --- a/internal/types/rbactypes.go +++ /dev/null @@ -1,81 +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 types - -import ( - "context" -) - -type User struct { - UserID string - UserGroups []string - UserRoles []Role - UserBindings []Binding -} - -type MongoClientContextKey struct{} - -type Resource struct { - ResourceType string `bson:"resourceType" json:"resourceType,omitempty"` - ResourceID string `bson:"resourceId" json:"resourceId,omitempty"` -} - -type Binding struct { - Resource Resource `bson:"resource" json:"resource"` - BindingID string `bson:"bindingId" json:"bindingId"` - CRUDDocumentState string `bson:"__STATE__" json:"-"` - Groups []string `bson:"groups" json:"groups,omitempty"` - Subjects []string `bson:"subjects" json:"subjects,omitempty"` - Permissions []string `bson:"permissions" json:"permissions,omitempty"` - Roles []string `bson:"roles" json:"roles,omitempty"` -} - -type BindingFilter struct { - BindingID string `bson:"bindingId" json:"bindingId"` -} - -type BindingUpdate struct { - Groups []string `bson:"groups" json:"groups"` - Subjects []string `bson:"subjects" json:"subjects"` -} - -type BindingCreateResponse struct { - ObjectID string `json:"_id"` -} - -type Role struct { - RoleID string `bson:"roleId" json:"roleId"` - CRUDDocumentState string `bson:"__STATE__" json:"-"` - Permissions []string `bson:"permissions" json:"permissions"` -} - -// MongoClientContextKey is the context key that shall be used to save -// mongo Collection reference in request contexts. -type IMongoClient interface { - Disconnect() - - RetrieveUserBindings(ctx context.Context, user *User) ([]Binding, error) - RetrieveRoles(ctx context.Context) ([]Role, error) - RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]Role, error) - - FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) - FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) -} - -type RequestError struct { - Error string `json:"error"` - Message string `json:"message"` - StatusCode int `json:"statusCode"` -} diff --git a/internal/utils/http.go b/internal/utils/http.go index 2e904e0e..0cd61bbc 100644 --- a/internal/utils/http.go +++ b/internal/utils/http.go @@ -19,7 +19,7 @@ import ( "net/http" "strings" - "github.com/rond-authz/rond/internal/types" + "github.com/rond-authz/rond/types" ) const ContentTypeHeaderKey = "content-type" diff --git a/internal/utils/http_test.go b/internal/utils/http_test.go index 47e8d336..bb873aa3 100644 --- a/internal/utils/http_test.go +++ b/internal/utils/http_test.go @@ -21,7 +21,7 @@ import ( "net/http/httptest" "testing" - "github.com/rond-authz/rond/internal/types" + "github.com/rond-authz/rond/types" "github.com/stretchr/testify/require" ) diff --git a/main.go b/main.go index 06dfb24f..5fbfa0c4 100644 --- a/main.go +++ b/main.go @@ -66,7 +66,11 @@ func entrypoint(shutdown chan os.Signal) { } log.WithField("opaModuleFileName", opaModuleConfig.Name).Trace("rego module successfully loaded") - oas, err := openapi.LoadOASFromFileOrNetwork(log, env) + oas, err := openapi.LoadOASFromFileOrNetwork(log, openapi.LoadOptions{ + APIPermissionsFilePath: env.APIPermissionsFilePath, + TargetServiceOASPath: env.TargetServiceOASPath, + TargetServiceHost: env.TargetServiceHost, + }) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, @@ -93,7 +97,9 @@ func entrypoint(shutdown chan os.Signal) { logrus.NewEntry(log), ) - policiesEvaluators, err := core.SetupEvaluators(ctx, mongoClient, oas, opaModuleConfig, env) + policiesEvaluators, err := core.SetupEvaluators(ctx, mongoClient, oas, opaModuleConfig, &core.EvaluatorOptions{ + EnablePrintStatements: env.IsTraceLogLevel(), + }) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, diff --git a/main_test.go b/main_test.go index 1ce3bcac..f98d21a1 100644 --- a/main_test.go +++ b/main_test.go @@ -1792,7 +1792,7 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) @@ -1947,7 +1947,7 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, env) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 4a6c4721..03e71f34 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -27,7 +27,6 @@ import ( "strings" "time" - "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" "github.com/sirupsen/logrus" @@ -323,13 +322,19 @@ func LoadOASFile(APIPermissionsFilePath string) (*OpenAPISpec, error) { return deserializeSpec(fileContentByte, utils.ErrFileLoadFailed) } -func LoadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariables) (*OpenAPISpec, error) { - if env.APIPermissionsFilePath != "" { - log.WithField("oasFilePath", env.APIPermissionsFilePath).Debug("Attempt to load OAS from file") - oas, err := LoadOASFile(env.APIPermissionsFilePath) +type LoadOptions struct { + APIPermissionsFilePath string + TargetServiceOASPath string + TargetServiceHost string +} + +func LoadOASFromFileOrNetwork(log *logrus.Logger, config LoadOptions) (*OpenAPISpec, error) { + if config.APIPermissionsFilePath != "" { + log.WithField("oasFilePath", config.APIPermissionsFilePath).Debug("Attempt to load OAS from file") + oas, err := LoadOASFile(config.APIPermissionsFilePath) if err != nil { log.WithFields(logrus.Fields{ - "APIPermissionsFilePath": env.APIPermissionsFilePath, + "APIPermissionsFilePath": config.APIPermissionsFilePath, }).Warn("failed api permissions file read") return nil, err } @@ -337,16 +342,16 @@ func LoadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariable return oas, nil } - if env.TargetServiceOASPath != "" { - log.WithField("oasApiPath", env.TargetServiceOASPath).Debug("Attempt to load OAS from target service") + if config.TargetServiceOASPath != "" { + log.WithField("oasApiPath", config.TargetServiceOASPath).Debug("Attempt to load OAS from target service") var oas *OpenAPISpec - documentationURL := fmt.Sprintf("%s://%s%s", HTTPScheme, env.TargetServiceHost, env.TargetServiceOASPath) + documentationURL := fmt.Sprintf("%s://%s%s", HTTPScheme, config.TargetServiceHost, config.TargetServiceOASPath) for { fetchedOAS, err := fetchOpenAPI(log, documentationURL) if err != nil { log.WithFields(logrus.Fields{ - "targetServiceHost": env.TargetServiceHost, - "targetOASPath": env.TargetServiceOASPath, + "targetServiceHost": config.TargetServiceHost, + "targetOASPath": config.TargetServiceOASPath, "error": logrus.Fields{"message": err.Error()}, }).Warn("failed OAS fetch, retry in 1s") time.Sleep(1 * time.Second) @@ -358,7 +363,7 @@ func LoadOASFromFileOrNetwork(log *logrus.Logger, env config.EnvironmentVariable return oas, nil } - return nil, fmt.Errorf("missing environment variables one of %s or %s is required", config.TargetServiceOASPathEnvKey, config.APIPermissionsFilePathEnvKey) + return nil, fmt.Errorf("missing openapi config: one of TargetServiceOASPath or APIPermissionsFilePath is required") } func WithXPermission(requestContext context.Context, permission *RondConfig) context.Context { diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index 182ad2a7..b4936c8e 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -20,7 +20,6 @@ import ( "fmt" "testing" - "github.com/rond-authz/rond/internal/config" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" @@ -175,12 +174,12 @@ func TestLoadOAS(t *testing.T) { log, _ := test.NewNullLogger() t.Run("if TargetServiceOASPath & APIPermissionsFilePath are set together, expect to read oas from static file", func(t *testing.T) { - envs := config.EnvironmentVariables{ + options := LoadOptions{ TargetServiceHost: "localhost:3000", TargetServiceOASPath: "/documentation/json", APIPermissionsFilePath: "../mocks/pathsConfig.json", } - openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) + openApiSpec, err := LoadOASFromFileOrNetwork(log, options) require.True(t, err == nil, "unexpected error") require.True(t, openApiSpec != nil, "unexpected nil result") require.Equal(t, OpenAPIPaths{ @@ -209,7 +208,7 @@ func TestLoadOAS(t *testing.T) { }) t.Run("expect to fetch oasApiSpec from API", func(t *testing.T) { - envs := config.EnvironmentVariables{ + options := LoadOptions{ TargetServiceHost: "localhost:3000", TargetServiceOASPath: "/documentation/json", } @@ -220,7 +219,7 @@ func TestLoadOAS(t *testing.T) { Reply(200). File("../mocks/simplifiedMock.json") - openApiSpec, err := LoadOASFromFileOrNetwork(log, envs) + openApiSpec, err := LoadOASFromFileOrNetwork(log, options) require.True(t, gock.IsDone(), "Mock has not been invoked") require.NoError(t, err, "unexpected error") require.NotNil(t, openApiSpec, "unexpected nil result") @@ -264,13 +263,13 @@ func TestLoadOAS(t *testing.T) { }) t.Run("expect to throw if TargetServiceOASPath or APIPermissionsFilePath is not set", func(t *testing.T) { - envs := config.EnvironmentVariables{ + options := LoadOptions{ TargetServiceHost: "localhost:3000", } - _, err := LoadOASFromFileOrNetwork(log, envs) + _, err := LoadOASFromFileOrNetwork(log, options) t.Logf("Expected error occurred: %s", err.Error()) - require.True(t, err != nil, fmt.Errorf("missing environment variables one of %s or %s is required", config.TargetServiceOASPathEnvKey, config.APIPermissionsFilePathEnvKey)) + require.ErrorContains(t, err, "missing openapi config: one of TargetServiceOASPath or APIPermissionsFilePath is required") }) } diff --git a/openapi/routerinfo_middleware_test.go b/openapi/routerinfo_middleware_test.go index 2418d4a4..fbe5fcb6 100644 --- a/openapi/routerinfo_middleware_test.go +++ b/openapi/routerinfo_middleware_test.go @@ -21,14 +21,11 @@ import ( "testing" "github.com/gorilla/mux" - "github.com/rond-authz/rond/internal/config" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) -var envs = config.EnvironmentVariables{} - func TestRouterInfoContext(t *testing.T) { nullLogger, _ := test.NewNullLogger() logger := logrus.NewEntry(nullLogger) diff --git a/service/handler.go b/service/handler.go index d1739fd6..6dcc934e 100644 --- a/service/handler.go +++ b/service/handler.go @@ -26,7 +26,9 @@ import ( "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" + "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" "github.com/sirupsen/logrus" ) @@ -100,30 +102,46 @@ func EvaluateRequest( requestContext := req.Context() logger := glogger.Get(requestContext) - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, env) + userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, types.UserHeadersKeys{ + IDHeaderKey: env.UserIdHeader, + GroupsHeaderKey: env.UserGroupsHeader, + PropertiesHeaderKey: env.UserPropertiesHeader, + }) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed user bindings and roles retrieving") utils.FailResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } - input, err := core.CreateRegoQueryInput(req, env, permission.Options.EnableResourcePermissionsMapOptimization, userInfo, nil) + pathParams := mux.Vars(req) + input, err := core.InputFromRequest(req, userInfo, env.ClientTypeHeader, pathParams, nil) + if err != nil { + return err + } + + regoInput, err := core.CreateRegoQueryInput(logger, input, core.RegoInputOptions{ + EnableResourcePermissionsMapOptimization: permission.Options.EnableResourcePermissionsMapOptimization, + }) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed rego query input creation") utils.FailResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } + evaluatorOptions := &core.EvaluatorOptions{ + EnablePrintStatements: env.IsTraceLogLevel(), + } + var evaluatorAllowPolicy *core.OPAEvaluator if !permission.RequestFlow.GenerateQuery { - evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, input, env) + evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, regoInput, evaluatorOptions) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot find policy evaluator") utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } } else { - evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, env, permission.RequestFlow.PolicyName, input, nil) + evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, permission.RequestFlow.PolicyName, regoInput, nil, evaluatorOptions) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot create evaluator") utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) @@ -191,6 +209,10 @@ func ReverseProxy( }, } + options := &core.EvaluatorOptions{ + EnablePrintStatements: env.IsTraceLogLevel(), + } + // Check on nil is performed to proxy the oas documentation path if permission == nil || permission.ResponseFlow.PolicyName == "" { proxy.ServeHTTP(w, req) @@ -203,7 +225,14 @@ func ReverseProxy( req, permission, partialResultsEvaluators, - env, + + env.ClientTypeHeader, + types.UserHeadersKeys{ + IDHeaderKey: env.UserIdHeader, + GroupsHeaderKey: env.UserGroupsHeader, + PropertiesHeaderKey: env.UserPropertiesHeader, + }, + options, ) proxy.ServeHTTP(w, req) } diff --git a/service/handler_test.go b/service/handler_test.go index 93554e2b..bd9f0d85 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -51,8 +51,6 @@ import ( ) func TestDirectProxyHandler(t *testing.T) { - var envs = config.EnvironmentVariables{} - oas := openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ @@ -97,7 +95,7 @@ func TestDirectProxyHandler(t *testing.T) { })) defer server.Close() - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -126,7 +124,7 @@ func TestDirectProxyHandler(t *testing.T) { mockHeader := "CustomHeader" mockHeaderValue := "mocked value" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -161,7 +159,7 @@ func TestDirectProxyHandler(t *testing.T) { invoked := false mockBodySting := "I am a body" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -210,7 +208,7 @@ func TestDirectProxyHandler(t *testing.T) { todo { input.request.body.hello == "world" }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true @@ -294,7 +292,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -352,7 +350,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -424,7 +422,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -483,7 +481,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -543,7 +541,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -595,7 +593,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -643,7 +641,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -712,7 +710,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -751,7 +749,7 @@ allow { serverURL, _ := url.Parse(server.URL) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -827,7 +825,7 @@ allow { employee.salary < 0 }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -883,8 +881,6 @@ allow { } func TestStandaloneMode(t *testing.T) { - var envs = config.EnvironmentVariables{} - env := config.EnvironmentVariables{Standalone: true} oas := openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ @@ -920,7 +916,7 @@ func TestStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("ok", func(t *testing.T) { - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, context.Background(), @@ -969,7 +965,7 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1021,7 +1017,7 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1073,7 +1069,7 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1124,7 +1120,7 @@ allow { mockBodySting := "I am a body" body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1176,7 +1172,7 @@ allow { ` mockBodySting := "I am a body" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") body := strings.NewReader(mockBodySting) @@ -1204,8 +1200,6 @@ allow { } func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { - var envs = config.EnvironmentVariables{} - userPropertiesHeaderKey := "miauserproperties" mockedUserProperties := map[string]interface{}{ "my": "other", @@ -1276,7 +1270,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { count(input.request.headers["%s"]) != 0 }`, mockHeader), } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1320,7 +1314,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { get_header("x-backdoor", input.request.headers) == "mocked value" }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1381,7 +1375,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { input.clientType == "%s" }`, mockedUserProperties["my"], mockedClientType), } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1465,7 +1459,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, opaModule, envs) + partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1555,7 +1549,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1606,7 +1600,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1696,7 +1690,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -1788,7 +1782,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1895,7 +1889,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -1953,7 +1947,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") ctx := createContext(t, @@ -2017,7 +2011,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -2059,7 +2053,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } func TestPolicyWithMongoBuiltinIntegration(t *testing.T) { - envs := config.EnvironmentVariables{} var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: ` @@ -2108,7 +2101,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -2154,7 +2147,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) @@ -2200,7 +2193,7 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, envs) + mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, nil) require.NoError(t, err, "Unexpected error") serverURL, _ := url.Parse(server.URL) diff --git a/service/router.go b/service/router.go index c2c17422..9dbf05d0 100644 --- a/service/router.go +++ b/service/router.go @@ -157,7 +157,10 @@ func SetupRouter( } } - evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, &env, policiesEvaluators, routesToNotProxy)) + evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, policiesEvaluators, routesToNotProxy, env.TargetServiceOASPath, &core.OPAMiddlewareOptions{ + IsStandalone: env.Standalone, + PathPrefixStandalone: env.PathPrefixStandalone, + })) if mongoClient != nil { evalRouter.Use(mongoclient.MongoClientInjectorMiddleware(mongoClient)) diff --git a/service/router_test.go b/service/router_test.go index 4a5517d1..744e85d4 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -283,7 +283,7 @@ func TestSetupRoutesIntegration(t *testing.T) { log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) t.Run("invokes known API", func(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -364,7 +364,7 @@ func TestSetupRoutesIntegration(t *testing.T) { Content: `package policies todo { false }`, } - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) router := mux.NewRouter() setupRoutes(router, oas, envs) @@ -395,7 +395,7 @@ func TestSetupRoutesIntegration(t *testing.T) { var mockOPAModule = &core.OPAModuleConfig{ Content: "FAILING POLICY!!!!", } - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, envs) + mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) router := mux.NewRouter() setupRoutes(router, oas, envs) diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 0b898116..63ce945b 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -90,7 +90,6 @@ func TestStatusRoutes(testCase *testing.T) { } func TestStatusRoutesIntegration(t *testing.T) { - envs := config.EnvironmentVariables{} log, _ := test.NewNullLogger() ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) @@ -113,7 +112,7 @@ test_policy { true } } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, envs) + evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { diff --git a/types/rbactypes.go b/types/rbactypes.go index f8343c68..6c790755 100644 --- a/types/rbactypes.go +++ b/types/rbactypes.go @@ -23,6 +23,13 @@ type User struct { UserGroups []string UserRoles []Role UserBindings []Binding + Properties map[string]any +} + +type UserHeadersKeys struct { + GroupsHeaderKey string + IDHeaderKey string + PropertiesHeaderKey string } type MongoClientContextKey struct{} From 08a63bd40b963c5e25fb9d8cb2450d2e797997d3 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:20:05 +0200 Subject: [PATCH 104/172] fix: added back panic guard for resource-less bindings in optmized map creation (#205) --- core/opaevaluator.go | 4 ++ core/opaevaluator_test.go | 94 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 776a0f9a..a65f797b 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -461,6 +461,10 @@ func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, e permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) rolesMap := buildRolesMap(user.Roles) for _, binding := range user.Bindings { + if binding.Resource == nil { + continue + } + for _, role := range binding.Roles { rolePermissions, ok := rolesMap[role] if !ok { diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 7d345abf..771ee224 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -125,6 +125,99 @@ func TestCreateRegoInput(t *testing.T) { require.Equal(t, expected, input.User.ResourcePermissionsMap) }) + t.Run("support bindings without resources", func(t *testing.T) { + input := Input{ + User: InputUser{ + Roles: []types.Role{ + {RoleID: "role1", Permissions: []string{"permission1", "permission2"}}, + {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, + }, + } + + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission1:type1:resource1": true, + "permission2:type1:resource1": true, + "permissionNotInRole1:type1:resource1": true, + "permission1:type3:resource3": true, + "permission2:type3:resource3": true, + "permission3:type3:resource3": true, + "permission4:type3:resource3": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) + }) + + t.Run("ignores unknown roles received from bindings", func(t *testing.T) { + input := Input{ + User: InputUser{ + Roles: []types.Role{ + {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type2", + ResourceID: "resource2", + }, + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, + }, + } + + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission3:type2:resource2": true, + "permission3:type3:resource3": true, + "permission4:type2:resource2": true, + "permission4:type3:resource3": true, + "permissionNotInRole1:type1:resource1": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) + }) + t.Run("do nothing if enableResourcePermissionsMapOptimization is false", func(t *testing.T) { input := Input{ User: user, @@ -134,7 +227,6 @@ func TestCreateRegoInput(t *testing.T) { require.Nil(t, input.User.ResourcePermissionsMap) }) }) - } func TestCreatePolicyEvaluators(t *testing.T) { From 67adb740953c2cd5e86ac4d9ceefb1b70ffd5ee4 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 19 Jun 2023 13:20:56 +0200 Subject: [PATCH 105/172] feat: first sdk implementation (#204) * feat: remove env from opa middleware * fix: remove internal types since it is a duplication * feat: remove config from transport * feat: remove config from evaluator * feat: remove config from openapi * test: add test * Update internal/config/env_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * refactor: transport use user header keys struct * feat: add env check in config package * draft: first draft of sdk implementation * draft: move sdk under core * feat: add first sdk experimental implementation * feat: removed Evaluators method from sdk * feat: removed Registry method from sdk * feat: remove use of logger from context in some core fn * fix: merge * fix: typo * add license in files * rename permission in config * docs: add docs * Apply suggestions from code review * Update core/sdk.go * Update core/sdk.go * refactor: move fake under internal folder --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/input.go | 158 +++++++++++++ core/input_test.go | 318 ++++++++++++++++++++++++++ core/opaevaluator.go | 167 ++------------ core/opaevaluator_test.go | 316 +------------------------- core/opamiddleware.go | 20 +- core/opamiddleware_test.go | 76 ++++++- core/sdk.go | 156 +++++++++++++ core/sdk_test.go | 187 ++++++++++++++++ internal/fake/sdk.go | 62 ++++++ main.go | 11 +- main_test.go | 17 +- service/handler.go | 40 ++-- service/handler_test.go | 382 +++++++++++++------------------- service/router.go | 14 +- service/router_test.go | 146 +++++++----- service/standalone_apis_test.go | 6 +- service/statusroutes_test.go | 9 +- 17 files changed, 1259 insertions(+), 826 deletions(-) create mode 100644 core/input.go create mode 100644 core/input_test.go create mode 100644 core/sdk.go create mode 100644 core/sdk_test.go create mode 100644 internal/fake/sdk.go diff --git a/core/input.go b/core/input.go new file mode 100644 index 00000000..b4b2ab03 --- /dev/null +++ b/core/input.go @@ -0,0 +1,158 @@ +// Copyright 2023 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 core + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" + + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/types" + + "github.com/sirupsen/logrus" +) + +type Input struct { + Request InputRequest `json:"request"` + Response InputResponse `json:"response"` + ClientType string `json:"clientType,omitempty"` + User InputUser `json:"user"` +} + +type InputRequest struct { + Body interface{} `json:"body,omitempty"` + Headers http.Header `json:"headers,omitempty"` + Query url.Values `json:"query,omitempty"` + PathParams map[string]string `json:"pathParams,omitempty"` + Method string `json:"method"` + Path string `json:"path"` +} + +type InputResponse struct { + Body interface{} `json:"body,omitempty"` +} + +type InputUser struct { + Properties map[string]interface{} `json:"properties,omitempty"` + Groups []string `json:"groups,omitempty"` + Bindings []types.Binding `json:"bindings,omitempty"` + Roles []types.Role `json:"roles,omitempty"` + ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` +} + +func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, enableResourcePermissionsMapOptimization bool) { + if !enableResourcePermissionsMapOptimization { + return + } + logger.Info("preparing optimized resourcePermissionMap for OPA evaluator") + opaPermissionsMapTime := time.Now() + + user := input.User + permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) + rolesMap := buildRolesMap(user.Roles) + for _, binding := range user.Bindings { + if binding.Resource == nil { + continue + } + + for _, role := range binding.Roles { + rolePermissions, ok := rolesMap[role] + if !ok { + continue + } + for _, permission := range rolePermissions { + key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) + permissionsOnResourceMap[key] = true + } + } + for _, permission := range binding.Permissions { + key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) + permissionsOnResourceMap[key] = true + } + } + input.User.ResourcePermissionsMap = permissionsOnResourceMap + logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Tracef("resource permission map creation") +} + +type RegoInputOptions struct { + EnableResourcePermissionsMapOptimization bool +} + +func CreateRegoQueryInput( + logger *logrus.Entry, + input Input, + options RegoInputOptions, +) ([]byte, error) { + opaInputCreationTime := time.Now() + + input.buildOptimizedResourcePermissionsMap(logger, options.EnableResourcePermissionsMapOptimization) + + inputBytes, err := json.Marshal(input) + if err != nil { + return nil, fmt.Errorf("failed input JSON encode: %v", err) + } + logger.Tracef("OPA input rego creation in: %+v", time.Since(opaInputCreationTime)) + return inputBytes, nil +} + +func InputFromRequest( + req *http.Request, + user types.User, + clientTypeHeaderKey string, + pathParams map[string]string, + responseBody any, +) (Input, error) { + shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && + req.ContentLength > 0 && + (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) + + var requestBody any + if shouldParseJSONBody { + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + return Input{}, fmt.Errorf("failed request body parse: %s", err.Error()) + } + if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { + return Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error()) + } + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + } + + return Input{ + ClientType: req.Header.Get(clientTypeHeaderKey), + Request: InputRequest{ + Method: req.Method, + Path: req.URL.Path, + Headers: req.Header, + Query: req.URL.Query(), + PathParams: pathParams, + Body: requestBody, + }, + Response: InputResponse{ + Body: responseBody, + }, + User: InputUser{ + Properties: user.Properties, + Groups: user.UserGroups, + Bindings: user.UserBindings, + Roles: user.UserRoles, + }, + }, nil +} diff --git a/core/input_test.go b/core/input_test.go new file mode 100644 index 00000000..b71974c5 --- /dev/null +++ b/core/input_test.go @@ -0,0 +1,318 @@ +// Copyright 2023 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 core + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/types" + + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestCreateRegoInput(t *testing.T) { + logrusLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(logrusLogger) + + t.Run("returns correctly", func(t *testing.T) { + actual, err := CreateRegoQueryInput(logger, Input{}, RegoInputOptions{}) + require.NoError(t, err) + require.Equal(t, "{\"request\":{\"method\":\"\",\"path\":\"\"},\"response\":{},\"user\":{}}", string(actual)) + }) + + t.Run("buildOptimizedResourcePermissionsMap", func(t *testing.T) { + user := InputUser{ + Roles: []types.Role{ + { + RoleID: "role1", + Permissions: []string{"permission1", "permission2"}, + }, + { + RoleID: "role2", + Permissions: []string{"permission3", "permission4"}, + }, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type2", + ResourceID: "resource2", + }, + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, + } + + t.Run("insert map", func(t *testing.T) { + input := Input{ + User: user, + } + + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission1:type1:resource1": true, + "permission2:type1:resource1": true, + "permissionNotInRole1:type1:resource1": true, + "permission3:type2:resource2": true, + "permission4:type2:resource2": true, + "permission1:type3:resource3": true, + "permission2:type3:resource3": true, + "permission3:type3:resource3": true, + "permission4:type3:resource3": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) + }) + + t.Run("do nothing if enableResourcePermissionsMapOptimization is false", func(t *testing.T) { + input := Input{ + User: user, + } + + input.buildOptimizedResourcePermissionsMap(logger, false) + require.Nil(t, input.User.ResourcePermissionsMap) + }) + + t.Run("support bindings without resources", func(t *testing.T) { + input := Input{ + User: InputUser{ + Roles: []types.Role{ + {RoleID: "role1", Permissions: []string{"permission1", "permission2"}}, + {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, + }, + } + + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission1:type1:resource1": true, + "permission2:type1:resource1": true, + "permissionNotInRole1:type1:resource1": true, + "permission1:type3:resource3": true, + "permission2:type3:resource3": true, + "permission3:type3:resource3": true, + "permission4:type3:resource3": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) + }) + + t.Run("ignores unknown roles received from bindings", func(t *testing.T) { + input := Input{ + User: InputUser{ + Roles: []types.Role{ + {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, + }, + Bindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "type1", + ResourceID: "resource1", + }, + Roles: []string{"role1"}, + Permissions: []string{"permissionNotInRole1"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type2", + ResourceID: "resource2", + }, + Roles: []string{"role2"}, + }, + { + Resource: &types.Resource{ + ResourceType: "type3", + ResourceID: "resource3", + }, + Roles: []string{"role1", "role2"}, + Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, + }, + }, + }, + } + + input.buildOptimizedResourcePermissionsMap(logger, true) + expected := PermissionsOnResourceMap{ + "permission3:type2:resource2": true, + "permission3:type3:resource3": true, + "permission4:type2:resource2": true, + "permission4:type3:resource3": true, + "permissionNotInRole1:type1:resource1": true, + "permissionNotInRole2:type3:resource3": true, + "permissionNotInRole3:type3:resource3": true, + } + require.Equal(t, expected, input.User.ResourcePermissionsMap) + }) + }) +} + +func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { + var roles []types.Role + for i := 0; i < 20; i++ { + role := types.Role{ + RoleID: fmt.Sprintf("role%d", i), + Permissions: []string{fmt.Sprintf("permission%d", i), fmt.Sprintf("permission%d", i+1)}, + } + roles = append(roles, role) + + } + var bindings []types.Binding + for i := 0; i < 100; i++ { + binding := types.Binding{ + Resource: &types.Resource{ + ResourceType: fmt.Sprintf("type%d", i), + ResourceID: fmt.Sprintf("resource%d", i), + }, + Roles: []string{fmt.Sprintf("role%d", i)}, + Permissions: []string{fmt.Sprintf("permissionRole%d", i)}, + } + bindings = append(bindings, binding) + + } + user := InputUser{ + Roles: roles, + Bindings: bindings, + } + + logrusLogger, _ := test.NewNullLogger() + logger := logrus.NewEntry(logrusLogger) + input := Input{ + User: user, + } + + b.ResetTimer() + for n := 0; n < b.N; n++ { + b.StartTimer() + input.buildOptimizedResourcePermissionsMap(logger, true) + b.StopTimer() + } +} + +func TestInputFromRequest(t *testing.T) { + user := types.User{} + clientTypeHeaderKey := "clienttypeheader" + pathParams := map[string]string{} + + t.Run("request body integration", func(t *testing.T) { + expectedRequestBody := map[string]interface{}{ + "Key": float64(42), + } + reqBody := struct{ Key int }{ + Key: 42, + } + reqBodyBytes, err := json.Marshal(reqBody) + require.Nil(t, err, "Unexpected error") + + t.Run("ignored on method GET", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("ignore nil body on method POST", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("added on accepted methods", func(t *testing.T) { + acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} + + for _, method := range acceptedMethods { + req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + } + }) + + t.Run("added with content-type specifying charset", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + }) + + t.Run("reject on method POST but with invalid body", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + _, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.ErrorContains(t, err, "failed request body deserialization:") + }) + + t.Run("ignore body on method POST but with another content type", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") + + input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + }) +} diff --git a/core/opaevaluator.go b/core/opaevaluator.go index a65f797b..750b2498 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -15,13 +15,11 @@ package core import ( - "bytes" "context" "encoding/json" "fmt" "io" "net/http" - "net/url" "os" "path/filepath" "strings" @@ -36,7 +34,6 @@ import ( "github.com/rond-authz/rond/custom_builtins" - "github.com/mia-platform/glogger/v2" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/topdown/print" @@ -64,13 +61,13 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(policy string, ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { - glogger.Get(ctx).Infof("precomputing rego query for allow policy: %s", policy) +func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { + logger.Infof("precomputing rego query for allow policy: %s", policy) policyEvaluatorTime := time.Now() partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, mongoClient, options) if err == nil { - glogger.Get(ctx).Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime)) + logger.Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime)) return &PartialEvaluator{ PartialEvaluator: partialResultEvaluator, }, nil @@ -78,7 +75,10 @@ func createPartialEvaluator(policy string, ctx context.Context, mongoClient type return nil, err } -func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, logger *logrus.Entry, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { + if oas == nil { + return nil, fmt.Errorf("oas must not be nil") + } policyEvaluators := PartialResultsEvaluators{} for path, OASContent := range oas.Paths { for verb, verbConfig := range OASContent { @@ -89,14 +89,14 @@ func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *o allowPolicy := verbConfig.PermissionV2.RequestFlow.PolicyName responsePolicy := verbConfig.PermissionV2.ResponseFlow.PolicyName - glogger.Get(ctx).Infof("precomputing rego queries for API: %s %s. Allow policy: %s. Response policy: %s.", verb, path, allowPolicy, responsePolicy) + logger.Infof("precomputing rego queries for API: %s %s. Allow policy: %s. Response policy: %s.", verb, path, allowPolicy, responsePolicy) if allowPolicy == "" { // allow policy is required, if missing assume the API has no valid x-rond configuration. continue } if _, ok := policyEvaluators[allowPolicy]; !ok { - evaluator, err := createPartialEvaluator(allowPolicy, ctx, mongoClient, oas, opaModuleConfig, options) + evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, mongoClient, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -107,7 +107,7 @@ func SetupEvaluators(ctx context.Context, mongoClient types.IMongoClient, oas *o if responsePolicy != "" { if _, ok := policyEvaluators[responsePolicy]; !ok { - evaluator, err := createPartialEvaluator(responsePolicy, ctx, mongoClient, oas, opaModuleConfig, options) + evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, mongoClient, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -215,6 +215,9 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf if evaluatorOptions == nil { evaluatorOptions = &EvaluatorOptions{} } + if opaModuleConfig == nil { + return nil, fmt.Errorf("OPAModuleConfig must not be nil") + } sanitizedPolicy := strings.Replace(policy, ".", "_", -1) queryString := fmt.Sprintf("data.policies.%s", sanitizedPolicy) @@ -260,7 +263,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con Context: ctx, }, nil } - return nil, fmt.Errorf("policy evaluator not found") + return nil, fmt.Errorf("policy evaluator not found: %s", policy) } func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitive.M, error) { @@ -378,27 +381,6 @@ func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission return dataFromEvaluation, nil, nil } -type RegoInputOptions struct { - EnableResourcePermissionsMapOptimization bool -} - -func CreateRegoQueryInput( - logger *logrus.Entry, - input Input, - options RegoInputOptions, -) ([]byte, error) { - opaInputCreationTime := time.Now() - - input.buildOptimizedResourcePermissionsMap(logger, options.EnableResourcePermissionsMapOptimization) - - inputBytes, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed input JSON encode: %v", err) - } - logger.Tracef("OPA input rego creation in: %+v", time.Since(opaInputCreationTime)) - return inputBytes, nil -} - func buildRolesMap(roles []types.Role) map[string][]string { var rolesMap = make(map[string][]string, 0) for _, role := range roles { @@ -407,20 +389,6 @@ func buildRolesMap(roles []types.Role) map[string][]string { return rolesMap } -func WithPartialResultsEvaluators(requestContext context.Context, evaluators PartialResultsEvaluators) context.Context { - return context.WithValue(requestContext, PartialResultsEvaluatorConfigKey{}, evaluators) -} - -// GetPartialResultsEvaluators can be used by a request handler to get PartialResult evaluator instance from context. -func GetPartialResultsEvaluators(requestContext context.Context) (PartialResultsEvaluators, error) { - evaluators, ok := requestContext.Value(PartialResultsEvaluatorConfigKey{}).(PartialResultsEvaluators) - if !ok { - return nil, fmt.Errorf("no policy evaluators found in request context") - } - - return evaluators, nil -} - // TODO: This should be made private in the future. type OPAModuleConfigKey struct{} @@ -443,68 +411,6 @@ func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error return permission, nil } -type Input struct { - Request InputRequest `json:"request"` - Response InputResponse `json:"response"` - ClientType string `json:"clientType,omitempty"` - User InputUser `json:"user"` -} - -func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, enableResourcePermissionsMapOptimization bool) { - if !enableResourcePermissionsMapOptimization { - return - } - logger.Info("preparing optimized resourcePermissionMap for OPA evaluator") - opaPermissionsMapTime := time.Now() - - user := input.User - permissionsOnResourceMap := make(PermissionsOnResourceMap, 0) - rolesMap := buildRolesMap(user.Roles) - for _, binding := range user.Bindings { - if binding.Resource == nil { - continue - } - - for _, role := range binding.Roles { - rolePermissions, ok := rolesMap[role] - if !ok { - continue - } - for _, permission := range rolePermissions { - key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) - permissionsOnResourceMap[key] = true - } - } - for _, permission := range binding.Permissions { - key := buildPermissionOnResourceKey(permission, binding.Resource.ResourceType, binding.Resource.ResourceID) - permissionsOnResourceMap[key] = true - } - } - input.User.ResourcePermissionsMap = permissionsOnResourceMap - logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Tracef("resource permission map creation") -} - -type InputRequest struct { - Body interface{} `json:"body,omitempty"` - Headers http.Header `json:"headers,omitempty"` - Query url.Values `json:"query,omitempty"` - PathParams map[string]string `json:"pathParams,omitempty"` - Method string `json:"method"` - Path string `json:"path"` -} - -type InputResponse struct { - Body interface{} `json:"body,omitempty"` -} - -type InputUser struct { - Properties map[string]interface{} `json:"properties,omitempty"` - Groups []string `json:"groups,omitempty"` - Bindings []types.Binding `json:"bindings,omitempty"` - Roles []types.Role `json:"roles,omitempty"` - ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` -} - type PermissionOnResourceKey string type PermissionsOnResourceMap map[PermissionOnResourceKey]bool @@ -543,48 +449,3 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { Content: string(fileContent), }, nil } - -func InputFromRequest( - req *http.Request, - user types.User, - clientTypeHeaderKey string, - pathParams map[string]string, - responseBody any, -) (Input, error) { - shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && - req.ContentLength > 0 && - (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) - - var requestBody any - if shouldParseJSONBody { - bodyBytes, err := io.ReadAll(req.Body) - if err != nil { - return Input{}, fmt.Errorf("failed request body parse: %s", err.Error()) - } - if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { - return Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error()) - } - req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - } - - return Input{ - ClientType: req.Header.Get(clientTypeHeaderKey), - Request: InputRequest{ - Method: req.Method, - Path: req.URL.Path, - Headers: req.Header, - Query: req.URL.Query(), - PathParams: pathParams, - Body: requestBody, - }, - Response: InputResponse{ - Body: responseBody, - }, - User: InputUser{ - Properties: user.Properties, - Groups: user.UserGroups, - Bindings: user.UserBindings, - Roles: user.UserRoles, - }, - }, nil -} diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 771ee224..32c472d9 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -18,16 +18,13 @@ import ( "bytes" "context" "encoding/json" - "fmt" "net/http" - "net/http/httptest" "regexp" "testing" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" - "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" @@ -54,185 +51,11 @@ func TestNewOPAEvaluator(t *testing.T) { }) } -func TestCreateRegoInput(t *testing.T) { - logrusLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(logrusLogger) - - t.Run("returns correctly", func(t *testing.T) { - actual, err := CreateRegoQueryInput(logger, Input{}, RegoInputOptions{}) - require.NoError(t, err) - require.Equal(t, "{\"request\":{\"method\":\"\",\"path\":\"\"},\"response\":{},\"user\":{}}", string(actual)) - }) - - t.Run("buildOptimizedResourcePermissionsMap", func(t *testing.T) { - user := InputUser{ - Roles: []types.Role{ - { - RoleID: "role1", - Permissions: []string{"permission1", "permission2"}, - }, - { - RoleID: "role2", - Permissions: []string{"permission3", "permission4"}, - }, - }, - Bindings: []types.Binding{ - { - Resource: &types.Resource{ - ResourceType: "type1", - ResourceID: "resource1", - }, - Roles: []string{"role1"}, - Permissions: []string{"permissionNotInRole1"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type2", - ResourceID: "resource2", - }, - Roles: []string{"role2"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type3", - ResourceID: "resource3", - }, - Roles: []string{"role1", "role2"}, - Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, - }, - }, - } - - t.Run("insert map", func(t *testing.T) { - input := Input{ - User: user, - } - - input.buildOptimizedResourcePermissionsMap(logger, true) - expected := PermissionsOnResourceMap{ - "permission1:type1:resource1": true, - "permission2:type1:resource1": true, - "permissionNotInRole1:type1:resource1": true, - "permission3:type2:resource2": true, - "permission4:type2:resource2": true, - "permission1:type3:resource3": true, - "permission2:type3:resource3": true, - "permission3:type3:resource3": true, - "permission4:type3:resource3": true, - "permissionNotInRole2:type3:resource3": true, - "permissionNotInRole3:type3:resource3": true, - } - require.Equal(t, expected, input.User.ResourcePermissionsMap) - }) - - t.Run("support bindings without resources", func(t *testing.T) { - input := Input{ - User: InputUser{ - Roles: []types.Role{ - {RoleID: "role1", Permissions: []string{"permission1", "permission2"}}, - {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, - }, - Bindings: []types.Binding{ - { - Resource: &types.Resource{ - ResourceType: "type1", - ResourceID: "resource1", - }, - Roles: []string{"role1"}, - Permissions: []string{"permissionNotInRole1"}, - }, - { - Roles: []string{"role2"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type3", - ResourceID: "resource3", - }, - Roles: []string{"role1", "role2"}, - Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, - }, - }, - }, - } - - input.buildOptimizedResourcePermissionsMap(logger, true) - expected := PermissionsOnResourceMap{ - "permission1:type1:resource1": true, - "permission2:type1:resource1": true, - "permissionNotInRole1:type1:resource1": true, - "permission1:type3:resource3": true, - "permission2:type3:resource3": true, - "permission3:type3:resource3": true, - "permission4:type3:resource3": true, - "permissionNotInRole2:type3:resource3": true, - "permissionNotInRole3:type3:resource3": true, - } - require.Equal(t, expected, input.User.ResourcePermissionsMap) - }) - - t.Run("ignores unknown roles received from bindings", func(t *testing.T) { - input := Input{ - User: InputUser{ - Roles: []types.Role{ - {RoleID: "role2", Permissions: []string{"permission3", "permission4"}}, - }, - Bindings: []types.Binding{ - { - Resource: &types.Resource{ - ResourceType: "type1", - ResourceID: "resource1", - }, - Roles: []string{"role1"}, - Permissions: []string{"permissionNotInRole1"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type2", - ResourceID: "resource2", - }, - Roles: []string{"role2"}, - }, - { - Resource: &types.Resource{ - ResourceType: "type3", - ResourceID: "resource3", - }, - Roles: []string{"role1", "role2"}, - Permissions: []string{"permissionNotInRole2", "permissionNotInRole3"}, - }, - }, - }, - } - - input.buildOptimizedResourcePermissionsMap(logger, true) - expected := PermissionsOnResourceMap{ - "permission3:type2:resource2": true, - "permission3:type3:resource3": true, - "permission4:type2:resource2": true, - "permission4:type3:resource3": true, - "permissionNotInRole1:type1:resource1": true, - "permissionNotInRole2:type3:resource3": true, - "permissionNotInRole3:type3:resource3": true, - } - require.Equal(t, expected, input.User.ResourcePermissionsMap) - }) - - t.Run("do nothing if enableResourcePermissionsMapOptimization is false", func(t *testing.T) { - input := Input{ - User: user, - } - - input.buildOptimizedResourcePermissionsMap(logger, false) - require.Nil(t, input.User.ResourcePermissionsMap) - }) - }) -} - func TestCreatePolicyEvaluators(t *testing.T) { t.Run("with simplified mock", func(t *testing.T) { log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) + logger := logrus.NewEntry(log) + ctx := context.Background() opaModuleDirectory := "../mocks/rego-policies" loadOptions := openapi.LoadOptions{ @@ -244,14 +67,15 @@ func TestCreatePolicyEvaluators(t *testing.T) { opaModuleConfig, err := LoadRegoModule(opaModuleDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, nil) + policyEvals, err := SetupEvaluators(ctx, logger, nil, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) t.Run("with complete oas mock", func(t *testing.T) { log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) + logger := logrus.NewEntry(log) + ctx := context.Background() opaModulesDirectory := "../mocks/rego-policies" @@ -264,7 +88,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { opaModuleConfig, err := LoadRegoModule(opaModulesDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, nil, openApiSpec, opaModuleConfig, nil) + policyEvals, err := SetupEvaluators(ctx, logger, nil, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -339,48 +163,6 @@ column_policy{ }) } -func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { - var roles []types.Role - for i := 0; i < 20; i++ { - role := types.Role{ - RoleID: fmt.Sprintf("role%d", i), - Permissions: []string{fmt.Sprintf("permission%d", i), fmt.Sprintf("permission%d", i+1)}, - } - roles = append(roles, role) - - } - var bindings []types.Binding - for i := 0; i < 100; i++ { - binding := types.Binding{ - Resource: &types.Resource{ - ResourceType: fmt.Sprintf("type%d", i), - ResourceID: fmt.Sprintf("resource%d", i), - }, - Roles: []string{fmt.Sprintf("role%d", i)}, - Permissions: []string{fmt.Sprintf("permissionRole%d", i)}, - } - bindings = append(bindings, binding) - - } - user := InputUser{ - Roles: roles, - Bindings: bindings, - } - - logrusLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(logrusLogger) - input := Input{ - User: user, - } - - b.ResetTimer() - for n := 0; n < b.N; n++ { - b.StartTimer() - input.buildOptimizedResourcePermissionsMap(logger, true) - b.StopTimer() - } -} - func TestPrint(t *testing.T) { var buf bytes.Buffer h := NewPrintHook(&buf, "policy-name") @@ -492,89 +274,3 @@ func TestGetOPAModuleConfig(t *testing.T) { require.True(t, opaEval != nil, "OPA Module config not found.") }) } - -func TestGetPolicyEvaluators(t *testing.T) { - t.Run(`GetPolicyEvaluators fails because no key has been passed`, func(t *testing.T) { - ctx := context.Background() - env, err := GetPartialResultsEvaluators(ctx) - require.True(t, err != nil, "An error was expected.") - t.Logf("Expected error: %s - env: %+v", err.Error(), env) - }) - - t.Run(`GetPartialResultsEvaluators returns PartialResultsEvaluators from context`, func(t *testing.T) { - ctx := context.WithValue(context.Background(), PartialResultsEvaluatorConfigKey{}, PartialResultsEvaluators{}) - opaEval, err := GetPartialResultsEvaluators(ctx) - require.True(t, err == nil, "Unexpected error.") - require.True(t, opaEval != nil, "OPA Module config not found.") - }) -} - -func TestInputFromRequest(t *testing.T) { - user := types.User{} - clientTypeHeaderKey := "clienttypeheader" - pathParams := map[string]string{} - - t.Run("request body integration", func(t *testing.T) { - expectedRequestBody := map[string]interface{}{ - "Key": float64(42), - } - reqBody := struct{ Key int }{ - Key: 42, - } - reqBodyBytes, err := json.Marshal(reqBody) - require.Nil(t, err, "Unexpected error") - - t.Run("ignored on method GET", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - - t.Run("ignore nil body on method POST", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", nil) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - - t.Run("added on accepted methods", func(t *testing.T) { - acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} - - for _, method := range acceptedMethods { - req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.NoError(t, err, "Unexpected error") - require.Equal(t, expectedRequestBody, input.Request.Body) - } - }) - - t.Run("added with content-type specifying charset", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.NoError(t, err, "Unexpected error") - require.Equal(t, expectedRequestBody, input.Request.Body) - }) - - t.Run("reject on method POST but with invalid body", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - _, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.ErrorContains(t, err, "failed request body deserialization:") - }) - - t.Run("ignore body on method POST but with another content type", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") - - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - }) -} diff --git a/core/opamiddleware.go b/core/opamiddleware.go index 72e2b7b5..870eb1e8 100644 --- a/core/opamiddleware.go +++ b/core/opamiddleware.go @@ -16,7 +16,6 @@ package core import ( "errors" - "fmt" "net/http" "strings" @@ -35,17 +34,11 @@ type OPAMiddlewareOptions struct { func OPAMiddleware( opaModuleConfig *OPAModuleConfig, - openAPISpec *openapi.OpenAPISpec, - policyEvaluators PartialResultsEvaluators, + sdk SDK, routesToNotProxy []string, targetServiceOASPath string, options *OPAMiddlewareOptions, ) mux.MiddlewareFunc { - OASrouter, err := openAPISpec.PrepareOASRouter() - if err != nil { - panic(fmt.Sprintf("Fatal: invalid OAS configuration: %s", err.Error())) - } - return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if utils.Contains(routesToNotProxy, r.URL.RequestURI()) { @@ -60,7 +53,8 @@ func OPAMiddleware( logger := glogger.Get(r.Context()) - permission, err := openAPISpec.FindPermission(OASrouter, path, r.Method) + evaluator, err := sdk.FindEvaluator(logger, r.Method, path) + permission := evaluator.Config() if r.Method == http.MethodGet && r.URL.Path == targetServiceOASPath && permission.RequestFlow.PolicyName == "" { fields := logrus.Fields{} if err != nil { @@ -93,16 +87,16 @@ func OPAMiddleware( return } + // TODO: remove me ctx := openapi.WithXPermission( WithOPAModuleConfig( - WithPartialResultsEvaluators( - openapi.WithRouterInfo(logger, r.Context(), r), - policyEvaluators, - ), + openapi.WithRouterInfo(logger, r.Context(), r), opaModuleConfig, ), &permission, ) + ctx = WithEvaluatorSKD(ctx, evaluator) + next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/core/opamiddleware_test.go b/core/opamiddleware_test.go index a5f48253..e99bcf3f 100644 --- a/core/opamiddleware_test.go +++ b/core/opamiddleware_test.go @@ -15,6 +15,7 @@ package core import ( + "context" "encoding/json" "io" "net/http" @@ -26,11 +27,31 @@ import ( "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" + + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) func TestOPAMiddleware(t *testing.T) { - var partialEvaluators = PartialResultsEvaluators{} + getSDK := func(t *testing.T, oas *openapi.OpenAPISpec, opaModule *OPAModuleConfig) SDK { + t.Helper() + + logger, _ := test.NewNullLogger() + sdk, err := NewSDK( + context.Background(), + logrus.NewEntry(logger), + nil, + oas, + opaModule, + nil, + nil, + "", + ) + require.NoError(t, err) + + return sdk + } routesNotToProxy := make([]string, 0) t.Run(`strict mode failure`, func(t *testing.T) { @@ -44,7 +65,9 @@ todo { true }`, require.NoError(t, err) err = json.Unmarshal(openAPISpecContent, &openAPISpec) require.NoError(t, err) - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) + sdk := getSDK(t, openAPISpec, opaModule) + + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) t.Run(`missing oas paths`, func(t *testing.T) { builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -106,7 +129,9 @@ foobar { true }`, openAPISpec, err := openapi.LoadOASFile("../mocks/documentationPathMock.json") require.NoError(t, err) targetServiceOASPath := "/documentation/json" - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) + sdk := getSDK(t, openAPISpec, opaModule) + + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -122,7 +147,9 @@ foobar { true }`, openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) targetServiceOASPath := "/documentation/json" - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) + sdk := getSDK(t, openAPISpec, opaModule) + + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -138,7 +165,9 @@ foobar { true }`, openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) targetServiceOASPath := "/documentation/custom/json" - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, targetServiceOASPath, nil) + sdk := getSDK(t, openAPISpec, opaModule) + + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, targetServiceOASPath, nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -161,8 +190,9 @@ foobar { true }`, Content: `package policies todo { true }`, } + sdk := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -183,8 +213,9 @@ todo { true }`, Content: `package policies foobar { true }`, } + sdk := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -205,8 +236,9 @@ foobar { true }`, Content: `package policies very_very_composed_permission { true }`, } + sdk := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -232,8 +264,9 @@ very_very_composed_permission_with_eval { true }`, IsStandalone: false, PathPrefixStandalone: "/eval", } + sdk := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -251,7 +284,6 @@ very_very_composed_permission_with_eval { true }`, } func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { - var partialEvaluators = PartialResultsEvaluators{} var routesNotToProxy = []string{} openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") @@ -260,6 +292,24 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { IsStandalone: true, PathPrefixStandalone: "/eval", } + getSdk := func(t *testing.T, opaModule *OPAModuleConfig) SDK { + t.Helper() + + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + sdk, err := NewSDK( + context.Background(), + logger, + nil, + openAPISpec, + opaModule, + nil, + nil, + "", + ) + require.NoError(t, err) + return sdk + } t.Run("injects correct path removing prefix", func(t *testing.T) { opaModule := &OPAModuleConfig{ @@ -268,7 +318,8 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { very_very_composed_permission { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) + sdk := getSdk(t, opaModule) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") @@ -290,7 +341,8 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { very_very_composed_permission_with_eval { true }`, } - middleware := OPAMiddleware(opaModule, openAPISpec, partialEvaluators, routesNotToProxy, "", options) + sdk := getSdk(t, opaModule) + middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { permission, err := openapi.GetXPermission(r.Context()) require.True(t, err == nil, "Unexpected error") diff --git a/core/sdk.go b/core/sdk.go new file mode 100644 index 00000000..bb55f9f6 --- /dev/null +++ b/core/sdk.go @@ -0,0 +1,156 @@ +// Copyright 2023 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 core + +import ( + "context" + "fmt" + + "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + "github.com/uptrace/bunrouter" +) + +type PolicyResult struct { + QueryToProxy []byte + Allowed bool +} + +// Warning: This interface is experimental, and it could change with breaking also in rond patches. +// Does not use outside this repository until it is not ready. +type SDK interface { + // Warning: this method will be removed in the near future. Do not use it outside Rond. + Metrics() metrics.Metrics + + FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) + EvaluatorFromConfig(logger *logrus.Entry, config openapi.RondConfig) SDKEvaluator +} + +// Warning: This interface is experimental, and it could change with breaking also in rond patches. +// Do not use outside this repository until it is not ready. +type SDKEvaluator interface { + Config() openapi.RondConfig + PartialResultsEvaluators() PartialResultsEvaluators +} + +type evaluator struct { + rond rondImpl + logger *logrus.Entry + rondConfig openapi.RondConfig +} + +func (e evaluator) Config() openapi.RondConfig { + return e.rondConfig +} + +func (e evaluator) PartialResultsEvaluators() PartialResultsEvaluators { + return e.rond.evaluator +} + +// Current implementation of the SDK +type rondImpl struct { + evaluator PartialResultsEvaluators + evaluatorOptions *EvaluatorOptions + oasRouter *bunrouter.CompatRouter + oas *openapi.OpenAPISpec + + metrics metrics.Metrics + registry *prometheus.Registry + + clientTypeHeaderKey string +} + +func (r rondImpl) FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) { + permission, err := r.oas.FindPermission(r.oasRouter, path, method) + return evaluator{ + rondConfig: permission, + logger: logger, + rond: r, + }, err +} + +func (r rondImpl) EvaluatorFromConfig(logger *logrus.Entry, config openapi.RondConfig) SDKEvaluator { + return evaluator{ + rondConfig: config, + logger: logger, + rond: r, + } +} + +func (r rondImpl) Metrics() metrics.Metrics { + return r.metrics +} + +// The SDK is now into core because there are coupled function here which should use the SDK itself +// (which uses core, so it will result in a cyclic dependency). In the future, sdk should be in a +// specific package. +func NewSDK( + ctx context.Context, + logger *logrus.Entry, + mongoClient types.IMongoClient, + oas *openapi.OpenAPISpec, + opaModuleConfig *OPAModuleConfig, + evaluatorOptions *EvaluatorOptions, + registry *prometheus.Registry, + clientTypeHeaderKey string, +) (SDK, error) { + evaluator, err := SetupEvaluators(ctx, logger, mongoClient, oas, opaModuleConfig, evaluatorOptions) + if err != nil { + return nil, err + } + + logger.WithField("policiesLength", len(evaluator)).Debug("policies evaluators partial results computed") + + oasRouter, err := oas.PrepareOASRouter() + if err != nil { + return nil, fmt.Errorf("invalid OAS configuration: %s", err) + } + + m := metrics.SetupMetrics("rond") + if registry != nil { + m.MustRegister(registry) + } + + return rondImpl{ + evaluator: evaluator, + oasRouter: oasRouter, + evaluatorOptions: evaluatorOptions, + oas: oas, + + metrics: m, + registry: registry, + + clientTypeHeaderKey: clientTypeHeaderKey, + }, nil +} + +type sdkKey struct{} + +func WithEvaluatorSKD(ctx context.Context, evaluator SDKEvaluator) context.Context { + return context.WithValue(ctx, sdkKey{}, evaluator) +} + +func GetEvaluatorSKD(ctx context.Context) (SDKEvaluator, error) { + sdk, ok := ctx.Value(sdkKey{}).(SDKEvaluator) + if !ok { + return nil, fmt.Errorf("no SDKEvaluator found in request context") + } + + return sdk, nil +} diff --git a/core/sdk_test.go b/core/sdk_test.go new file mode 100644 index 00000000..895ee739 --- /dev/null +++ b/core/sdk_test.go @@ -0,0 +1,187 @@ +// Copyright 2023 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 core + +import ( + "context" + "net/http" + "testing" + + "github.com/rond-authz/rond/openapi" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestNewSDK(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.Nil(t, err) + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + + t.Run("fails if oas is nil", func(t *testing.T) { + sdk, err := NewSDK(context.Background(), logger, nil, nil, nil, nil, nil, "") + require.ErrorContains(t, err, "oas must not be nil") + require.Nil(t, sdk) + }) + + t.Run("fails if opaModuleConfig is nil", func(t *testing.T) { + sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, nil, nil, nil, "") + require.ErrorContains(t, err, "OPAModuleConfig must not be nil") + require.Nil(t, sdk) + }) + + t.Run("fails if oas is invalid", func(t *testing.T) { + oas, err := openapi.LoadOASFile("../mocks/invalidOASConfiguration.json") + require.NoError(t, err) + sdk, err := NewSDK(context.Background(), logger, nil, oas, opaModule, nil, nil, "") + require.ErrorContains(t, err, "invalid OAS configuration:") + require.Nil(t, sdk) + }) + + t.Run("creates sdk correctly", func(t *testing.T) { + sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, nil, "") + require.NoError(t, err) + require.NotEmpty(t, sdk) + }) + + t.Run("if registry is passed, setup metrics", func(t *testing.T) { + registry := prometheus.NewRegistry() + sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, registry, "") + require.NoError(t, err) + require.NotEmpty(t, sdk) + }) +} + +func TestSDK(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.Nil(t, err) + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + registry := prometheus.NewRegistry() + sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, registry, "") + require.NoError(t, err) + + rond, ok := sdk.(rondImpl) + require.True(t, ok, "rondImpl is not sdk") + + t.Run("metrics", func(t *testing.T) { + require.Equal(t, rond.metrics, sdk.Metrics()) + }) + + t.Run("FindEvaluator", func(t *testing.T) { + t.Run("throws if path and method not found", func(t *testing.T) { + actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/not-existent/path") + require.ErrorContains(t, err, "not found oas definition: GET /not-existent/path") + require.Equal(t, evaluator{ + rondConfig: openapi.RondConfig{}, + logger: logger, + rond: rond, + }, actual) + }) + + t.Run("returns correct evaluator", func(t *testing.T) { + actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + require.Equal(t, evaluator{ + rondConfig: openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + }, + }, + logger: logger, + rond: rond, + }, actual) + + t.Run("get permissions", func(t *testing.T) { + require.Equal(t, openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + }, + }, actual.Config()) + }) + + t.Run("get partial evaluators", func(t *testing.T) { + require.Equal(t, rond.evaluator, actual.PartialResultsEvaluators()) + }) + }) + }) + + t.Run("EvaluatorFromConfig", func(t *testing.T) { + rondConfig := openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + GenerateQuery: true, + }, + ResponseFlow: openapi.ResponseFlow{ + PolicyName: "other", + }, + } + + t.Run("returns evaluator passing RondConfig", func(t *testing.T) { + actual := sdk.EvaluatorFromConfig(logger, rondConfig) + require.Equal(t, evaluator{ + rondConfig: rondConfig, + logger: logger, + rond: rond, + }, actual) + }) + }) +} + +func TestContext(t *testing.T) { + t.Run("ok", func(t *testing.T) { + ctx := context.Background() + rondConfig := openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + GenerateQuery: true, + }, + ResponseFlow: openapi.ResponseFlow{ + PolicyName: "other", + }, + } + + expectedEvaluator := evaluator{ + rondConfig: rondConfig, + } + + ctx = WithEvaluatorSKD(ctx, expectedEvaluator) + + actualEvaluator, err := GetEvaluatorSKD(ctx) + require.NoError(t, err) + require.Equal(t, expectedEvaluator, actualEvaluator) + }) + + t.Run("throws if not in context", func(t *testing.T) { + actualEvaluator, err := GetEvaluatorSKD(context.Background()) + require.EqualError(t, err, "no SDKEvaluator found in request context") + require.Nil(t, actualEvaluator) + }) +} diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go new file mode 100644 index 00000000..a488e19a --- /dev/null +++ b/internal/fake/sdk.go @@ -0,0 +1,62 @@ +// Copyright 2023 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 fake + +import ( + "net/http" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" +) + +type RequestPolicyEvaluatorResult struct { + Err error +} + +type SDKEvaluator struct { + partialEvaluator core.PartialResultsEvaluators + permission openapi.RondConfig + + requestPolicyEvaluatorResult *RequestPolicyEvaluatorResult +} + +func NewSDKEvaluator( + partialEvaluator core.PartialResultsEvaluators, + permission openapi.RondConfig, + requestPolicyEvaluatorResult *RequestPolicyEvaluatorResult, +) core.SDKEvaluator { + return SDKEvaluator{ + partialEvaluator: partialEvaluator, + permission: permission, + + requestPolicyEvaluatorResult: requestPolicyEvaluatorResult, + } +} + +func (s SDKEvaluator) EvaluateRequestPolicy(req *http.Request, userInfo types.User, permission *openapi.RondConfig) (core.PolicyResult, error) { + if s.requestPolicyEvaluatorResult == nil { + return core.PolicyResult{}, nil + } + return core.PolicyResult{}, s.requestPolicyEvaluatorResult.Err +} + +func (s SDKEvaluator) Config() openapi.RondConfig { + return s.permission +} + +func (s SDKEvaluator) PartialResultsEvaluators() core.PartialResultsEvaluators { + return s.partialEvaluator +} diff --git a/main.go b/main.go index 5fbfa0c4..1c7aa2bb 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "syscall" "time" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" @@ -97,19 +98,19 @@ func entrypoint(shutdown chan os.Signal) { logrus.NewEntry(log), ) - policiesEvaluators, err := core.SetupEvaluators(ctx, mongoClient, oas, opaModuleConfig, &core.EvaluatorOptions{ + registry := prometheus.NewRegistry() + sdk, err := core.NewSDK(ctx, logrus.NewEntry(log), mongoClient, oas, opaModuleConfig, &core.EvaluatorOptions{ EnablePrintStatements: env.IsTraceLogLevel(), - }) + }, registry, env.ClientTypeHeader) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, - }).Errorf("failed to create evaluators") + }).Errorf("failed to create sdk") return } - log.WithField("policiesLength", len(policiesEvaluators)).Debug("policies evaluators partial results computed") // Routing - router, err := service.SetupRouter(log, env, opaModuleConfig, oas, policiesEvaluators, mongoClient) + router, err := service.SetupRouter(log, env, opaModuleConfig, oas, sdk, mongoClient, registry) if mongoClient != nil { defer mongoClient.Disconnect() } diff --git a/main_test.go b/main_test.go index f98d21a1..50c53131 100644 --- a/main_test.go +++ b/main_test.go @@ -29,6 +29,7 @@ import ( "time" "github.com/mia-platform/glogger/v2" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" @@ -86,7 +87,7 @@ func TestProxyOASPath(t *testing.T) { resp, err := http.DefaultClient.Get("http://localhost:3000/custom/documentation/json") - require.Equal(t, nil, err, "error calling docs") + require.NoError(t, err, "error calling docs") require.Equal(t, http.StatusOK, resp.StatusCode) require.True(t, gock.IsDone(), "the proxy does not blocks the request for documentations path.") }) @@ -1037,7 +1038,7 @@ func TestEntrypoint(t *testing.T) { {name: "MONGODB_URL", value: fmt.Sprintf("mongodb://%s/%s", mongoHost, mongoDBName)}, {name: "BINDINGS_COLLECTION_NAME", value: "bindings"}, {name: "ROLES_COLLECTION_NAME", value: "roles"}, - {name: "LOG_LEVEL", value: "trace"}, + {name: "LOG_LEVEL", value: "fatal"}, }) clientOpts := options.Client().ApplyURI(fmt.Sprintf("mongodb://%s", mongoHost)) @@ -1792,10 +1793,12 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) + registry := prometheus.NewRegistry() + logger, _ := test.NewNullLogger() + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") require.NoError(t, err, "unexpected error") - router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) require.NoError(t, err, "unexpected error") t.Run("some eval API", func(t *testing.T) { @@ -1947,10 +1950,12 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) + registry := prometheus.NewRegistry() + logger, _ := test.NewNullLogger() + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") require.NoError(t, err, "unexpected error") - router, err := service.SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) require.NoError(t, err, "unexpected error") t.Run("metrics API exposed correctly", func(t *testing.T) { diff --git a/service/handler.go b/service/handler.go index 6dcc934e..cf631b3b 100644 --- a/service/handler.go +++ b/service/handler.go @@ -41,9 +41,15 @@ func ReverseProxyOrResponse( env config.EnvironmentVariables, w http.ResponseWriter, req *http.Request, - permission *openapi.RondConfig, - partialResultsEvaluators core.PartialResultsEvaluators, + evaluatorSdk core.SDKEvaluator, ) { + var permission openapi.RondConfig + var partialResultsEvaluators core.PartialResultsEvaluators + if evaluatorSdk != nil { + permission = evaluatorSdk.Config() + partialResultsEvaluators = evaluatorSdk.PartialResultsEvaluators() + } + if env.Standalone { if permission.RequestFlow.GenerateQuery { queryHeaderKey := BASE_ROW_FILTER_HEADER_KEY @@ -59,7 +65,7 @@ func ReverseProxyOrResponse( } return } - ReverseProxy(logger, env, w, req, permission, partialResultsEvaluators) + ReverseProxy(logger, env, w, req, &permission, partialResultsEvaluators) } func rbacHandler(w http.ResponseWriter, req *http.Request) { @@ -73,35 +79,31 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { return } - permission, err := openapi.GetXPermission(requestContext) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no policy permission found in context") - utils.FailResponse(w, "no policy permission found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return - } - partialResultEvaluators, err := core.GetPartialResultsEvaluators(requestContext) + evaluatorSdk, err := core.GetEvaluatorSKD(requestContext) if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no partialResult evaluators found in context") - utils.FailResponse(w, "no partialResult evaluators found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) + logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no evaluatorSdk found in context") + utils.FailResponse(w, "no evaluators sdk found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } - if err := EvaluateRequest(req, env, w, partialResultEvaluators, permission); err != nil { + if err := EvaluateRequest(req, env, w, evaluatorSdk); err != nil { return } - ReverseProxyOrResponse(logger, env, w, req, permission, partialResultEvaluators) + ReverseProxyOrResponse(logger, env, w, req, evaluatorSdk) } func EvaluateRequest( req *http.Request, env config.EnvironmentVariables, w http.ResponseWriter, - partialResultsEvaluators core.PartialResultsEvaluators, - permission *openapi.RondConfig, + evaluatorSdk core.SDKEvaluator, ) error { requestContext := req.Context() logger := glogger.Get(requestContext) + permission := evaluatorSdk.Config() + partialResultsEvaluators := evaluatorSdk.PartialResultsEvaluators() + userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, types.UserHeadersKeys{ IDHeaderKey: env.UserIdHeader, GroupsHeaderKey: env.UserGroupsHeader, @@ -149,7 +151,7 @@ func EvaluateRequest( } } - _, query, err := evaluatorAllowPolicy.PolicyEvaluation(logger, permission) + _, query, err := evaluatorAllowPolicy.PolicyEvaluation(logger, &permission) if err != nil { if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) @@ -204,7 +206,7 @@ func ReverseProxy( req.URL.Scheme = URL_SCHEME if _, ok := req.Header["User-Agent"]; !ok { // explicitly disable User-Agent so it's not set to default value - req.Header.Set("User-Agent", "") + req.Header.Del("User-Agent") } }, } @@ -246,5 +248,5 @@ func alwaysProxyHandler(w http.ResponseWriter, req *http.Request) { utils.FailResponse(w, "no environment found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } - ReverseProxyOrResponse(logger, env, w, req, nil, nil) + ReverseProxyOrResponse(logger, env, w, req, nil) } diff --git a/service/handler_test.go b/service/handler_test.go index bd9f0d85..57ec82a6 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -25,14 +25,17 @@ import ( "net/url" "reflect" "strings" - "testing" + "github.com/gorilla/mux" + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/rego" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/fake" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" @@ -41,17 +44,24 @@ import ( "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) +var mockRondConfigWithQueryGen = openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "allow", + GenerateQuery: true, + QueryOptions: openapi.QueryOptions{ + HeaderName: "rowfilterquery", + }, + }, +} + func TestDirectProxyHandler(t *testing.T) { - oas := openapi.OpenAPISpec{ + oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ @@ -63,7 +73,7 @@ func TestDirectProxyHandler(t *testing.T) { }, } - oasWithFilter := openapi.OpenAPISpec{ + oasWithFilter := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ @@ -95,17 +105,15 @@ func TestDirectProxyHandler(t *testing.T) { })) defer server.Close() - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) ctx := createContext(t, - ctx, + context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, + evaluator, mockOPAModule, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -124,9 +132,6 @@ func TestDirectProxyHandler(t *testing.T) { mockHeader := "CustomHeader" mockHeaderValue := "mocked value" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true require.Equal(t, mockHeaderValue, r.Header.Get(mockHeader), "Mocked Backend: Mocked Header not found") @@ -135,13 +140,13 @@ func TestDirectProxyHandler(t *testing.T) { defer server.Close() serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, + evaluator, mockOPAModule, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) @@ -159,9 +164,6 @@ func TestDirectProxyHandler(t *testing.T) { invoked := false mockBodySting := "I am a body" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true defer r.Body.Close() @@ -176,13 +178,13 @@ func TestDirectProxyHandler(t *testing.T) { body := strings.NewReader(mockBodySting) serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) ctx := createContext(t, - ctx, + context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, + evaluator, mockOPAModule, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -205,11 +207,9 @@ func TestDirectProxyHandler(t *testing.T) { OPAModuleConfig := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies - todo { input.request.body.hello == "world" }`, + todo { input.request.body.hello == "world" }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true defer r.Body.Close() @@ -224,13 +224,16 @@ func TestDirectProxyHandler(t *testing.T) { body := strings.NewReader(mockBodySting) serverURL, _ := url.Parse(server.URL) + + rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, rondConfig, oas) + ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + evaluator, + mockOPAModule, nil, - &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, - OPAModuleConfig, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://www.example.com:8080/api", body) @@ -292,17 +295,14 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -350,17 +350,14 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -422,15 +419,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -481,17 +476,14 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -541,17 +533,15 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -593,15 +583,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -641,15 +629,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -710,15 +696,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -749,16 +733,13 @@ allow { serverURL, _ := url.Parse(server.URL) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) ctx := createContext(t, ctx, config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, + evaluator, mockOPAModule, - partialEvaluators, + nil, ) log, hook := test.NewNullLogger() @@ -825,16 +806,14 @@ allow { employee.salary < 0 }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, OPAModuleConfig, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, ctx, config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockRondConfigWithQueryGen, + evaluator, OPAModuleConfig, - partialEvaluators, + nil, ) log, hook := test.NewNullLogger() @@ -882,7 +861,7 @@ allow { func TestStandaloneMode(t *testing.T) { env := config.EnvironmentVariables{Standalone: true} - oas := openapi.OpenAPISpec{ + oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ @@ -894,7 +873,7 @@ func TestStandaloneMode(t *testing.T) { }, } - oasWithFilter := openapi.OpenAPISpec{ + oasWithFilter := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ @@ -916,15 +895,13 @@ func TestStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("ok", func(t *testing.T) { - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) ctx := createContext(t, context.Background(), env, - nil, - mockXPermission, + evaluator, mockOPAModule, - partialEvaluators, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -965,16 +942,14 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - + opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), env, + evaluator, + opaModuleConfig, nil, - mockRondConfigWithQueryGen, - &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1017,16 +992,14 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - + opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), env, + evaluator, + opaModuleConfig, nil, - mockRondConfigWithQueryGen, - &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1069,16 +1042,14 @@ allow { body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - + opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), env, + evaluator, + opaModuleConfig, nil, - mockRondConfigWithQueryGen, - &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1120,16 +1091,15 @@ allow { mockBodySting := "I am a body" body := strings.NewReader(mockBodySting) - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") + opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), env, + evaluator, + opaModuleConfig, nil, - mockRondConfigWithQueryGen, - &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1172,18 +1142,16 @@ allow { ` mockBodySting := "I am a body" - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oasWithFilter, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - body := strings.NewReader(mockBodySting) + opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) ctx := createContext(t, context.Background(), env, + evaluator, + opaModuleConfig, nil, - mockRondConfigWithQueryGen, - &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - partialEvaluators, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1270,16 +1238,14 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { count(input.request.headers["%s"]) != 0 }`, mockHeader), } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + evaluator := getEvaluator(t, ctx, opaModule, nil, rondConfig, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, + evaluator, opaModule, - partialEvaluators, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1314,16 +1280,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { get_header("x-backdoor", input.request.headers) == "mocked value" }`, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, + evaluator, opaModule, - partialEvaluators, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1375,9 +1338,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { input.clientType == "%s" }`, mockedUserProperties["my"], mockedClientType), } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") + evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1386,10 +1348,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { UserGroupsHeader: userGroupsHeaderKey, ClientTypeHeader: clientTypeHeaderKey, }, - nil, - mockXPermission, + evaluator, opaModule, - partialEvaluators, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1447,7 +1408,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { `, mockHeader), } - oas := openapi.OpenAPISpec{ + oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ @@ -1459,16 +1420,14 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - partialEvaluators, err := core.SetupEvaluators(ctx, nil, &oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + rondConfig := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + evaluator := getEvaluator(t, ctx, opaModule, nil, *rondConfig, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, + evaluator, opaModule, - partialEvaluators, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1544,14 +1503,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("Something went wrong"), UserBindings: nil, UserRoles: nil, UserRolesError: errors.New("Something went wrong")} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1564,10 +1518,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - mongoclientMock, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -1595,14 +1548,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("MongoDB Error"), UserRolesError: errors.New("MongoDB Error")} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1615,10 +1563,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - mongoclientMock, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -1685,14 +1632,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1705,10 +1647,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - mongoclientMock, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -1778,31 +1719,25 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - log, _ := test.NewNullLogger() + serverURL, _ := url.Parse(server.URL) mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ TargetServiceHost: serverURL.Host, UserPropertiesHeader: userPropertiesHeaderKey, UserGroupsHeader: userGroupsHeaderKey, - UserIdHeader: userIdHeaderKey, ClientTypeHeader: clientTypeHeaderKey, + UserIdHeader: userIdHeaderKey, MongoDBUrl: "mongodb://test", RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - // opaEvaluator, - &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles}, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -1884,31 +1819,25 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ TargetServiceHost: serverURL.Host, UserPropertiesHeader: userPropertiesHeaderKey, UserGroupsHeader: userGroupsHeaderKey, - UserIdHeader: userIdHeaderKey, ClientTypeHeader: clientTypeHeaderKey, + UserIdHeader: userIdHeaderKey, MongoDBUrl: "mongodb://test", RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles}, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -1942,14 +1871,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindings: nil} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1962,10 +1886,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - mongoclientMock, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -2006,31 +1929,25 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { userBindings := []types.Binding{} userRoles := []types.Role{} - log, _ := test.NewNullLogger() mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoclientMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, opaModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ TargetServiceHost: serverURL.Host, UserPropertiesHeader: userPropertiesHeaderKey, UserGroupsHeader: userGroupsHeaderKey, - UserIdHeader: userIdHeaderKey, ClientTypeHeader: clientTypeHeaderKey, + UserIdHeader: userIdHeaderKey, MongoDBUrl: "mongodb://test", RolesCollectionName: "roles", BindingsCollectionName: "bindings", }, - mongoclientMock, - mockXPermission, + evaluator, opaModule, - mockPartialEvaluators, + mongoclientMock, ) w := httptest.NewRecorder() @@ -2062,7 +1979,7 @@ project := find_one("projects", {"projectId": "1234"}) project.tenantId == "1234" }`, } - var mockXPermission = &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + var mockXPermission = openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ @@ -2100,18 +2017,15 @@ project.tenantId == "1234" mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoclientMock, oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - mongoMock, - mockXPermission, + evaluator, mockOPAModule, - mockPartialEvaluators, + mongoclientMock, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2147,17 +2061,14 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - mongoMock, - mockXPermission, + evaluator, mockOPAModule, - mockPartialEvaluators, + mongoMock, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2193,17 +2104,14 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - mockPartialEvaluators, err := core.SetupEvaluators(ctxForPartial, mongoMock, oas, mockOPAModule, nil) - require.NoError(t, err, "Unexpected error") - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - mongoMock, - mockXPermission, + evaluator, mockOPAModule, - mockPartialEvaluators, + mongoMock, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2243,6 +2151,12 @@ func BenchmarkEvaluateRequest(b *testing.B) { permission.RequestFlow.PolicyName: core.PartialEvaluator{PartialEvaluator: &pr}, } + sdk := fake.NewSDKEvaluator( + partialEvaluators, + *permission, + nil, + ) + envs := config.EnvironmentVariables{ UserGroupsHeader: "miausergroups", UserIdHeader: "miauserid", @@ -2284,7 +2198,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { }) recorder := httptest.NewRecorder() b.StartTimer() - EvaluateRequest(req, envs, recorder, partialEvaluators, permission) + EvaluateRequest(req, envs, recorder, sdk) b.StopTimer() require.Equal(b, http.StatusOK, recorder.Code) } diff --git a/service/router.go b/service/router.go index 9dbf05d0..dbfdd151 100644 --- a/service/router.go +++ b/service/router.go @@ -101,21 +101,17 @@ func SetupRouter( env config.EnvironmentVariables, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, - policiesEvaluators core.PartialResultsEvaluators, + sdk core.SDK, mongoClient *mongoclient.MongoClient, + registry *prometheus.Registry, ) (*mux.Router, error) { router := mux.NewRouter().UseEncodedPath() router.Use(glogger.RequestMiddlewareLogger(log, []string{"/-/"})) serviceName := "rönd" StatusRoutes(router, serviceName, env.ServiceVersion) - registry := prometheus.NewRegistry() - m := metrics.SetupMetrics("rond") - if env.ExposeMetrics { - m.MustRegister(registry) - metrics.MetricsRoute(router, registry) - } - router.Use(metrics.RequestMiddleware(m)) + metrics.MetricsRoute(router, registry) + router.Use(metrics.RequestMiddleware(sdk.Metrics())) router.Use(config.RequestMiddlewareEnvironments(env)) @@ -157,7 +153,7 @@ func SetupRouter( } } - evalRouter.Use(core.OPAMiddleware(opaModuleConfig, oas, policiesEvaluators, routesToNotProxy, env.TargetServiceOASPath, &core.OPAMiddlewareOptions{ + evalRouter.Use(core.OPAMiddleware(opaModuleConfig, sdk, routesToNotProxy, env.TargetServiceOASPath, &core.OPAMiddlewareOptions{ IsStandalone: env.Standalone, PathPrefixStandalone: env.PathPrefixStandalone, })) diff --git a/service/router_test.go b/service/router_test.go index 744e85d4..63fb501e 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -16,6 +16,7 @@ package service import ( "context" + "fmt" "net/http" "net/http/httptest" "net/url" @@ -25,6 +26,7 @@ import ( "github.com/mia-platform/glogger/v2" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/fake" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" @@ -229,31 +231,33 @@ func createContext( t *testing.T, originalCtx context.Context, env config.EnvironmentVariables, - mongoClient *mocks.MongoClientMock, - permission *openapi.RondConfig, + evaluator core.SDKEvaluator, opaModuleConfig *core.OPAModuleConfig, - partialResultEvaluators core.PartialResultsEvaluators, + mongoClient *mocks.MongoClientMock, ) context.Context { t.Helper() var partialContext context.Context partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) - partialContext = context.WithValue(partialContext, openapi.XPermissionKey{}, permission) - partialContext = context.WithValue(partialContext, core.OPAModuleConfigKey{}, opaModuleConfig) - if mongoClient != nil { - partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) - } - partialContext = context.WithValue(partialContext, core.PartialResultsEvaluatorConfigKey{}, partialResultEvaluators) - log, _ := test.NewNullLogger() - partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(log)) + partialContext = core.WithEvaluatorSKD(partialContext, evaluator) + // TODO: remove me + partialContext = context.WithValue(partialContext, core.OPAModuleConfigKey{}, opaModuleConfig) + // TODO: remove me partialContext = context.WithValue(partialContext, openapi.RouterInfoKey{}, openapi.RouterInfo{ MatchedPath: "/matched/path", RequestedPath: "/requested/path", Method: "GET", }) + if mongoClient != nil { + partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) + } + + partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(logrus.New())) + + // TODO: remove me partialContext = metrics.WithValue(partialContext, metrics.SetupMetrics("test_rond")) return partialContext @@ -264,16 +268,34 @@ var mockOPAModule = &core.OPAModuleConfig{ Content: `package policies todo { true }`, } -var mockXPermission = &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} - -var mockRondConfigWithQueryGen = &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ - PolicyName: "allow", - GenerateQuery: true, - QueryOptions: openapi.QueryOptions{ - HeaderName: "rowfilterquery", - }, - }, +var mockXPermission = openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + +func getEvaluator( + t *testing.T, + ctx context.Context, + opaModule *core.OPAModuleConfig, + mongoClient *mocks.MongoClientMock, + rondConfig openapi.RondConfig, + oas *openapi.OpenAPISpec, +) core.SDKEvaluator { + t.Helper() + + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + sdk, err := core.NewSDK( + ctx, + logger, + mongoClient, + oas, + opaModule, + &core.EvaluatorOptions{}, + nil, + "", + ) + require.NoError(t, err) + + return sdk.EvaluatorFromConfig(logger, rondConfig) } func TestSetupRoutesIntegration(t *testing.T) { @@ -281,9 +303,9 @@ func TestSetupRoutesIntegration(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/simplifiedMock.json") log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) + logger := logrus.NewEntry(log) + ctx := glogger.WithLogger(context.Background(), logger) - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) t.Run("invokes known API", func(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -298,13 +320,14 @@ func TestSetupRoutesIntegration(t *testing.T) { setupRoutes(router, oas, envs) serverURL, _ := url.Parse(server.URL) + + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -330,18 +353,18 @@ func TestSetupRoutesIntegration(t *testing.T) { w.WriteHeader(http.StatusOK) })) defer server.Close() + serverURL, _ := url.Parse(server.URL) router := mux.NewRouter() setupRoutes(router, oas, envs) - serverURL, _ := url.Parse(server.URL) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/unknown/path?foo=bar", nil) @@ -364,17 +387,16 @@ func TestSetupRoutesIntegration(t *testing.T) { Content: `package policies todo { false }`, } - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) router := mux.NewRouter() setupRoutes(router, oas, envs) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + ctx := createContext(t, - ctx, + context.Background(), config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -387,26 +409,22 @@ func TestSetupRoutesIntegration(t *testing.T) { w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - require.Equal(t, http.StatusForbidden, w.Result().StatusCode) + require.Equal(t, http.StatusForbidden, w.Result().StatusCode, w.Body.String()) }) t.Run("blocks request on policy evaluation error", func(t *testing.T) { - - var mockOPAModule = &core.OPAModuleConfig{ - Content: "FAILING POLICY!!!!", - } - mockPartialEvaluators, _ := core.SetupEvaluators(ctx, nil, oas, mockOPAModule, nil) - router := mux.NewRouter() setupRoutes(router, oas, envs) + evaluator := fake.NewSDKEvaluator(nil, mockXPermission, &fake.RequestPolicyEvaluatorResult{ + Err: fmt.Errorf("fails to evaluate policy"), + }) + ctx := createContext(t, context.Background(), - config.EnvironmentVariables{TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/users/?foo=bar", nil) @@ -424,6 +442,12 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") + rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "foo"}} + var mockOPAModule = &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + foo { true }`, + } var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -436,13 +460,13 @@ func TestSetupRoutesIntegration(t *testing.T) { setupRoutes(router, oas, envs) serverURL, _ := url.Parse(server.URL) + + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/foo/route-not-registered-explicitly", nil) @@ -461,6 +485,12 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes a specific API within a nested path", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") + rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "foo"}} + var mockOPAModule = &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + foo { true }`, + } var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -473,13 +503,13 @@ func TestSetupRoutesIntegration(t *testing.T) { setupRoutes(router, oas, envs) serverURL, _ := url.Parse(server.URL) + + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, - nil, - mockXPermission, - mockOPAModule, - mockPartialEvaluators, + evaluator, + nil, nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/foo/bar/nested", nil) diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 8c520bf5..1859230a 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -37,8 +37,7 @@ func TestRevokeHandler(t *testing.T) { context.Background(), config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, - nil, - nil, + mockOPAModule, nil, ) @@ -514,8 +513,7 @@ func TestGrantHandler(t *testing.T) { context.Background(), config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, - nil, - nil, + mockOPAModule, nil, ) diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 63ce945b..75035772 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/mia-platform/glogger/v2" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" @@ -112,7 +113,9 @@ test_policy { true } } var mongoClient *mongoclient.MongoClient - evaluatorsMap, err := core.SetupEvaluators(ctx, mongoClient, oas, opa, nil) + registry := prometheus.NewRegistry() + logger, _ := test.NewNullLogger() + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { @@ -121,7 +124,7 @@ test_policy { true } TargetServiceHost: "my-service:4444", PathPrefixStandalone: "/my-prefix", } - router, err := SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { @@ -154,7 +157,7 @@ test_policy { true } PathPrefixStandalone: "/my-prefix", ServiceVersion: "latest", } - router, err := SetupRouter(log, env, opa, oas, evaluatorsMap, mongoClient) + router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() From 4c27fa6a127f68b8670a39c792b0e40dac52dafa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:01:53 +0200 Subject: [PATCH 106/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.11.7 to 1.12.0 (#206) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.7 to 1.12.0. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.7...v1.12.0) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 7 +++---- go.sum | 26 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 1ba3eafc..df01a6e2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.11.7 + go.mongodb.org/mongo-driver v1.12.0 gopkg.in/h2non/gock.v1 v1.1.2 ) @@ -55,7 +55,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/perimeterx/marshmallow v1.1.4 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect @@ -69,8 +68,8 @@ require ( github.com/subosito/gotenv v1.4.1 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.1 // indirect - github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 27bd3c72..61d7ebc7 100644 --- a/go.sum +++ b/go.sum @@ -480,7 +480,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -492,8 +491,6 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= @@ -504,10 +501,10 @@ github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/T github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -527,12 +524,13 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= -go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= 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= @@ -567,6 +565,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= @@ -608,6 +607,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -647,6 +647,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 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= @@ -668,6 +669,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -730,11 +732,14 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -745,6 +750,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -802,6 +809,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 4c0d6fd2ce04c9869137700b7133ceec7ae6652a Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Wed, 28 Jun 2023 14:46:02 +0200 Subject: [PATCH 107/172] sdk: add evaluate request and response function (#209) * feat: remove env from opa middleware * fix: remove internal types since it is a duplication * feat: remove config from transport * feat: remove config from evaluator * feat: remove config from openapi * test: add test * Update internal/config/env_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * refactor: transport use user header keys struct * feat: add env check in config package * draft: first draft of sdk implementation * draft: move sdk under core * feat: add first sdk experimental implementation * feat: removed Evaluators method from sdk * feat: removed Registry method from sdk * feat: remove use of logger from context in some core fn * fix: merge * fix: typo * add license in files * rename permission in config * draft * docs: add docs * Apply suggestions from code review * Update core/sdk.go * Update core/sdk.go * refactor: move fake under internal folder * feat: first evaluate impl * feat: add evaluate request - it misses management of mongo client * feat: support mongo utility inside rego with sdk * remove mongoclient from sdk * feat: add evaluate request and response with tests * tests cleanup * feat: response filter now uses sdk * clean context * feat: clean interface * feat: remove openapi and metrcs middleware * remove unused sdk fn * test: add metrics tests * change after PR review * feat: revert metrics inside core * improve metrics tests * refactor: renamed interface param --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/input.go | 30 +- core/input_test.go | 20 +- core/opa_transport.go | 72 +-- core/opa_transport_test.go | 80 +-- core/opaevaluator.go | 113 ++-- core/opaevaluator_test.go | 78 +-- core/opamiddleware.go | 22 +- core/opamiddleware_test.go | 66 ++- core/sdk.go | 170 ++++-- core/sdk_test.go | 724 ++++++++++++++++++++++++-- custom_builtins/mongo.go | 8 + internal/fake/sdk.go | 12 +- internal/metrics/routes.go | 29 -- internal/metrics/routes_test.go | 42 +- internal/mocks/mongoclient.go | 6 + main.go | 3 +- main_test.go | 8 +- mocks/bench.json | 79 +++ mocks/rondOasConfig.json | 2 +- openapi/openapi_utils.go | 39 +- openapi/openapi_utils_test.go | 172 +++++- openapi/routerinfo_middleware.go | 65 --- openapi/routerinfo_middleware_test.go | 127 ----- service/handler.go | 71 +-- service/handler_test.go | 349 +++---------- service/router.go | 1 - service/router_test.go | 75 +-- service/standalone_apis_test.go | 2 - service/statusroutes_test.go | 4 +- 29 files changed, 1428 insertions(+), 1041 deletions(-) create mode 100644 mocks/bench.json delete mode 100644 openapi/routerinfo_middleware.go delete mode 100644 openapi/routerinfo_middleware_test.go diff --git a/core/input.go b/core/input.go index b4b2ab03..4a2b9318 100644 --- a/core/input.go +++ b/core/input.go @@ -112,13 +112,17 @@ func CreateRegoQueryInput( return inputBytes, nil } -func InputFromRequest( - req *http.Request, - user types.User, - clientTypeHeaderKey string, - pathParams map[string]string, - responseBody any, -) (Input, error) { +type RondInput interface { + Input(user types.User, responseBody any) (Input, error) +} + +type requestInfo struct { + *http.Request + clientTypeHeaderKey string + pathParams map[string]string +} + +func (req requestInfo) Input(user types.User, responseBody any) (Input, error) { shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && req.ContentLength > 0 && (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) @@ -136,13 +140,13 @@ func InputFromRequest( } return Input{ - ClientType: req.Header.Get(clientTypeHeaderKey), + ClientType: req.Header.Get(req.clientTypeHeaderKey), Request: InputRequest{ Method: req.Method, Path: req.URL.Path, Headers: req.Header, Query: req.URL.Query(), - PathParams: pathParams, + PathParams: req.pathParams, Body: requestBody, }, Response: InputResponse{ @@ -156,3 +160,11 @@ func InputFromRequest( }, }, nil } + +func NewRondInput(req *http.Request, clientTypeHeaderKey string, pathParams map[string]string) RondInput { + return requestInfo{ + Request: req, + clientTypeHeaderKey: clientTypeHeaderKey, + pathParams: pathParams, + } +} diff --git a/core/input_test.go b/core/input_test.go index b71974c5..5da56d4f 100644 --- a/core/input_test.go +++ b/core/input_test.go @@ -247,7 +247,7 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { } } -func TestInputFromRequest(t *testing.T) { +func TestRondInput(t *testing.T) { user := types.User{} clientTypeHeaderKey := "clienttypeheader" pathParams := map[string]string{} @@ -265,7 +265,8 @@ func TestInputFromRequest(t *testing.T) { t.Run("ignored on method GET", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) @@ -274,7 +275,8 @@ func TestInputFromRequest(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", nil) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) @@ -285,7 +287,8 @@ func TestInputFromRequest(t *testing.T) { for _, method := range acceptedMethods { req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) require.NoError(t, err, "Unexpected error") require.Equal(t, expectedRequestBody, input.Request.Body) } @@ -294,7 +297,8 @@ func TestInputFromRequest(t *testing.T) { t.Run("added with content-type specifying charset", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) require.NoError(t, err, "Unexpected error") require.Equal(t, expectedRequestBody, input.Request.Body) }) @@ -302,7 +306,8 @@ func TestInputFromRequest(t *testing.T) { t.Run("reject on method POST but with invalid body", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - _, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + _, err := rondRequest.Input(user, nil) require.ErrorContains(t, err, "failed request body deserialization:") }) @@ -310,7 +315,8 @@ func TestInputFromRequest(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") - input, err := InputFromRequest(req, user, clientTypeHeaderKey, pathParams, nil) + rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) diff --git a/core/opa_transport.go b/core/opa_transport.go index 37cc47ac..0620aceb 100644 --- a/core/opa_transport.go +++ b/core/opa_transport.go @@ -25,7 +25,6 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" @@ -35,15 +34,13 @@ import ( type OPATransport struct { http.RoundTripper // FIXME: this overlaps with the req.Context used during RoundTrip. - context context.Context - logger *logrus.Entry - request *http.Request - permission *openapi.RondConfig - partialResultsEvaluators PartialResultsEvaluators - - clientHeaderKey string - userHeaders types.UserHeadersKeys - evaluatorOptions *EvaluatorOptions + context context.Context + logger *logrus.Entry + request *http.Request + + clientHeaderKey string + userHeaders types.UserHeadersKeys + evaluatorSDK SDKEvaluator } func NewOPATransport( @@ -51,23 +48,19 @@ func NewOPATransport( context context.Context, logger *logrus.Entry, req *http.Request, - permission *openapi.RondConfig, - partialResultsEvaluators PartialResultsEvaluators, clientHeaderKey string, userHeadersKeys types.UserHeadersKeys, - evaluatorOptions *EvaluatorOptions, + evaluatorSDK SDKEvaluator, ) *OPATransport { return &OPATransport{ - RoundTripper: transport, - context: req.Context(), - logger: logger, - request: req, - permission: permission, - partialResultsEvaluators: partialResultsEvaluators, - - clientHeaderKey: clientHeaderKey, - userHeaders: userHeadersKeys, - evaluatorOptions: evaluatorOptions, + RoundTripper: transport, + context: req.Context(), + logger: logger, + request: req, + + clientHeaderKey: clientHeaderKey, + userHeaders: userHeadersKeys, + evaluatorSDK: evaluatorSDK, } } @@ -115,42 +108,15 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er } pathParams := mux.Vars(t.request) - input, err := InputFromRequest(t.request, userInfo, t.clientHeaderKey, pathParams, decodedBody) - if err != nil { - t.responseWithError(resp, err, http.StatusInternalServerError) - return resp, nil - } - - regoInput, err := CreateRegoQueryInput(t.logger, input, RegoInputOptions{ - EnableResourcePermissionsMapOptimization: t.permission.Options.EnableResourcePermissionsMapOptimization, - }) - if err != nil { - t.responseWithError(resp, err, http.StatusInternalServerError) - return resp, nil - } - - evaluator, err := t.partialResultsEvaluators.GetEvaluatorFromPolicy(t.context, t.permission.ResponseFlow.PolicyName, regoInput, t.evaluatorOptions) - if err != nil { - t.logger.WithField("error", logrus.Fields{ - "policyName": t.permission.ResponseFlow.PolicyName, - "message": err.Error(), - }).Error("RBAC policy evaluation on response failed") - t.responseWithError(resp, err, http.StatusInternalServerError) - return resp, nil - } + input := NewRondInput(t.request, t.clientHeaderKey, pathParams) - bodyToProxy, err := evaluator.Evaluate(t.logger) + responseBody, err := t.evaluatorSDK.EvaluateResponsePolicy(t.context, input, userInfo, decodedBody) if err != nil { t.responseWithError(resp, err, http.StatusForbidden) return resp, nil } - marshalledBody, err := json.Marshal(bodyToProxy) - if err != nil { - t.responseWithError(resp, err, http.StatusInternalServerError) - return resp, nil - } - overwriteResponse(resp, marshalledBody) + overwriteResponse(resp, responseBody) return resp, nil } diff --git a/core/opa_transport_test.go b/core/opa_transport_test.go index 2f80d11d..faa1ee87 100644 --- a/core/opa_transport_test.go +++ b/core/opa_transport_test.go @@ -25,11 +25,9 @@ import ( "strconv" "testing" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" @@ -53,17 +51,15 @@ func TestRoundTripErrors(t *testing.T) { JSON(responseBody) req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) - transport := &OPATransport{ + transport := NewOPATransport( http.DefaultTransport, req.Context(), logrus.NewEntry(logger), req, - nil, - nil, "", types.UserHeadersKeys{}, nil, - } + ) resp, err := transport.RoundTrip(req) require.NoError(t, err, "unexpected error") @@ -92,17 +88,15 @@ func TestOPATransportResponseWithError(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) - transport := &OPATransport{ + transport := NewOPATransport( http.DefaultTransport, req.Context(), logrus.NewEntry(logger), req, - nil, - nil, "", types.UserHeadersKeys{}, nil, - } + ) t.Run("generic business error message", func(t *testing.T) { resp := &http.Response{ @@ -151,7 +145,7 @@ func TestOPATransportResponseWithError(t *testing.T) { func TestOPATransportRoundTrip(t *testing.T) { logger, _ := test.NewNullLogger() - req := httptest.NewRequest(http.MethodPost, "http://example.com/some-api", nil) + req := httptest.NewRequest(http.MethodGet, "/users", nil) t.Run("returns error on RoundTrip error", func(t *testing.T) { transport := NewOPATransport( @@ -159,7 +153,6 @@ func TestOPATransportRoundTrip(t *testing.T) { req.Context(), logrus.NewEntry(logger), req, - nil, nil, "", types.UserHeadersKeys{ IDHeaderKey: "useridheader", @@ -279,35 +272,36 @@ func TestOPATransportRoundTrip(t *testing.T) { }) t.Run("ok with filter response", func(t *testing.T) { - resp := http.Response{ + resp := &http.Response{ StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader([]byte(`{"some":"field"}`))), ContentLength: 16, Header: http.Header{"Content-Type": []string{"application/json"}}, } - req = req.Clone(metrics.WithValue(openapi.WithRouterInfo(logrus.NewEntry(logger), req.Context(), req), metrics.SetupMetrics(""))) + logEntry := logrus.NewEntry(logger) + req = req.Clone(context.Background()) - partialResult, err := NewPartialResultEvaluator(context.Background(), "my_policy", &OPAModuleConfig{ - Content: "package policies my_policy [resources] { resources := input.response.body }", - }, nil, nil) + evaluator := getSdk(t, &sdkOptions{ + oasFilePath: "../mocks/rondOasConfig.json", + opaModuleContent: "package policies responsepolicy [resources] { resources := input.response.body }", + }) + evaluatorSDK, err := evaluator.FindEvaluator(logEntry, http.MethodGet, "/users/") require.NoError(t, err) - transport := &OPATransport{ - RoundTripper: &MockRoundTrip{Response: &resp}, - context: req.Context(), - logger: logrus.NewEntry(logger), - request: req, - permission: &openapi.RondConfig{ - ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, - }, - partialResultsEvaluators: PartialResultsEvaluators{"my_policy": PartialEvaluator{partialResult}}, - userHeaders: types.UserHeadersKeys{ + transport := NewOPATransport( + &MockRoundTrip{Response: resp}, + req.Context(), + logrus.NewEntry(logger), + req, + "", + types.UserHeadersKeys{ IDHeaderKey: "useridheader", GroupsHeaderKey: "usergroupsheader", PropertiesHeaderKey: "userpropertiesheader", }, - } + evaluatorSDK, + ) actualResp, err := transport.RoundTrip(req) require.NoError(t, err, "response body is not valid") @@ -419,22 +413,28 @@ func TestOPATransportRoundTrip(t *testing.T) { ContentLength: 0, Header: http.Header{"Content-Type": []string{"application/json"}}, } - transport := &OPATransport{ - RoundTripper: &MockRoundTrip{Response: resp}, - context: req.Context(), - logger: logrus.NewEntry(logger), - request: req, - permission: &openapi.RondConfig{ - ResponseFlow: openapi.ResponseFlow{PolicyName: "my_policy"}, - }, - partialResultsEvaluators: PartialResultsEvaluators{"my_policy": {}}, - userHeaders: types.UserHeadersKeys{ + evaluator := getSdk(t, &sdkOptions{ + oasFilePath: "../mocks/rondOasConfig.json", + opaModuleContent: "package policies responsepolicy [resources] { resources := input.response.body }", + }) + logEntry := logrus.NewEntry(logger) + evaluatorSDK, err := evaluator.FindEvaluator(logEntry, http.MethodGet, "/users/") + require.NoError(t, err) + + transport := NewOPATransport( + &MockRoundTrip{Response: resp}, + req.Context(), + logrus.NewEntry(logger), + req, + "", + types.UserHeadersKeys{ IDHeaderKey: "useridheader", GroupsHeaderKey: "usergroupsheader", PropertiesHeaderKey: "userpropertiesheader", }, - } - resp, err := transport.RoundTrip(req) + evaluatorSDK, + ) + resp, err = transport.RoundTrip(req) require.Nil(t, err) require.Equal(t, http.StatusInternalServerError, resp.StatusCode) bodyBytes, err := io.ReadAll(resp.Body) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 750b2498..8beffd58 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -19,7 +19,6 @@ import ( "encoding/json" "fmt" "io" - "net/http" "os" "path/filepath" "strings" @@ -27,6 +26,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" @@ -52,6 +52,9 @@ type OPAEvaluator struct { PolicyEvaluator Evaluator PolicyName string Context context.Context + + m *metrics.Metrics + routerInfo openapi.RouterInfo } type PartialResultsEvaluatorConfigKey struct{} @@ -61,11 +64,11 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { +func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { logger.Infof("precomputing rego query for allow policy: %s", policy) policyEvaluatorTime := time.Now() - partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, mongoClient, options) + partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, options) if err == nil { logger.Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime)) return &PartialEvaluator{ @@ -75,7 +78,7 @@ func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy st return nil, err } -func SetupEvaluators(ctx context.Context, logger *logrus.Entry, mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { if oas == nil { return nil, fmt.Errorf("oas must not be nil") } @@ -96,7 +99,7 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, mongoClient type } if _, ok := policyEvaluators[allowPolicy]; !ok { - evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, mongoClient, oas, opaModuleConfig, options) + evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -107,7 +110,7 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, mongoClient type if responsePolicy != "" { if _, ok := policyEvaluators[responsePolicy]; !ok { - evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, mongoClient, oas, opaModuleConfig, options) + evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, oas, opaModuleConfig, options) if err != nil { return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) @@ -157,6 +160,20 @@ func (h printHook) Print(_ print.Context, message string) error { type EvaluatorOptions struct { EnablePrintStatements bool + MongoClient types.IMongoClient + + Metrics *metrics.Metrics + RouterInfo openapi.RouterInfo +} + +func (e *EvaluatorOptions) WithMetrics(metrics metrics.Metrics) *EvaluatorOptions { + e.Metrics = &metrics + return e +} + +func (e *EvaluatorOptions) WithRouterInfo(routerInfo openapi.RouterInfo) *EvaluatorOptions { + e.RouterInfo = routerInfo + return e } func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { @@ -183,26 +200,26 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod custom_builtins.MongoFindMany, ) + ctx = mongoclient.WithMongoClient(ctx, options.MongoClient) + return &OPAEvaluator{ PolicyEvaluator: query, PolicyName: policy, Context: ctx, + + m: options.Metrics, + routerInfo: options.RouterInfo, }, nil } -func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.Request, policy string, input []byte, responseBody interface{}, options *EvaluatorOptions) (*OPAEvaluator, error) { - opaModuleConfig, err := GetOPAModuleConfig(req.Context()) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no OPA module configuration found in context") - return nil, fmt.Errorf("no OPA module configuration found in context") - } - +func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, policy string, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { + // TODO: remove logger and set in sdk logger.WithFields(logrus.Fields{ "policyName": policy, }).Info("Policy to be evaluated") opaEvaluatorInstanceTime := time.Now() - evaluator, err := NewOPAEvaluator(ctx, policy, opaModuleConfig, input, options) + evaluator, err := NewOPAEvaluator(ctx, policy, config, input, options) if err != nil { logger.WithError(err).Error("failed RBAC policy creation") return nil, err @@ -211,7 +228,7 @@ func CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, req *http.R return evaluator, nil } -func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, mongoClient types.IMongoClient, evaluatorOptions *EvaluatorOptions) (*rego.PartialResult, error) { +func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, evaluatorOptions *EvaluatorOptions) (*rego.PartialResult, error) { if evaluatorOptions == nil { evaluatorOptions = &EvaluatorOptions{} } @@ -231,7 +248,8 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf rego.Capabilities(ast.CapabilitiesForThisVersion()), custom_builtins.GetHeaderFunction, } - if mongoClient != nil { + if evaluatorOptions.MongoClient != nil { + ctx = mongoclient.WithMongoClient(ctx, evaluatorOptions.MongoClient) options = append(options, custom_builtins.MongoFindOne, custom_builtins.MongoFindMany) } regoInstance := rego.New(options...) @@ -261,29 +279,31 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con PolicyName: policy, PolicyEvaluator: evaluator, Context: ctx, + + m: options.Metrics, + routerInfo: options.RouterInfo, }, nil } return nil, fmt.Errorf("policy evaluator not found: %s", policy) } +func (evaluator *OPAEvaluator) metrics() metrics.Metrics { + if evaluator.m != nil { + return *evaluator.m + } + return metrics.SetupMetrics("rond") +} + func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitive.M, error) { opaEvaluationTimeStart := time.Now() partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.Context) if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error()) } - routerInfo, err := openapi.GetRouterInfo(evaluator.Context) - if err != nil { - return nil, err - } - m, err := metrics.GetFromContext(evaluator.Context) - if err != nil { - return nil, err - } opaEvaluationTime := time.Since(opaEvaluationTimeStart) - m.PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + evaluator.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -292,9 +312,9 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv "policyName": evaluator.PolicyName, "partialEval": true, "allowed": true, - "matchedPath": routerInfo.MatchedPath, - "requestedPath": routerInfo.RequestedPath, - "method": routerInfo.Method, + "matchedPath": evaluator.routerInfo.MatchedPath, + "requestedPath": evaluator.routerInfo.RequestedPath, + "method": evaluator.routerInfo.Method, }).Debug("policy evaluation completed") client := opatranslator.OPAClient{} @@ -313,22 +333,14 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, error) { opaEvaluationTimeStart := time.Now() + results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context) if err != nil { return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error()) } - routerInfo, err := openapi.GetRouterInfo(evaluator.Context) - if err != nil { - return nil, err - } - m, err := metrics.GetFromContext(evaluator.Context) - if err != nil { - return nil, err - } opaEvaluationTime := time.Since(opaEvaluationTimeStart) - - m.PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + evaluator.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -337,9 +349,10 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro "policyName": evaluator.PolicyName, "partialEval": false, "allowed": results.Allowed(), - "matchedPath": routerInfo.MatchedPath, - "requestedPath": routerInfo.RequestedPath, - "method": routerInfo.Method, + "resultsLength": len(results), + "matchedPath": evaluator.routerInfo.MatchedPath, + "requestedPath": evaluator.routerInfo.RequestedPath, + "method": evaluator.routerInfo.Method, }).Debug("policy evaluation completed") if results.Allowed() { @@ -350,7 +363,6 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro }).Tracef("policy results") return nil, nil } - // The results returned by OPA are a list of Results object with fields: // - Expressions: list of list // - Bindings: object @@ -389,28 +401,11 @@ func buildRolesMap(roles []types.Role) map[string][]string { return rolesMap } -// TODO: This should be made private in the future. -type OPAModuleConfigKey struct{} - type OPAModuleConfig struct { Name string Content string } -func WithOPAModuleConfig(requestContext context.Context, permission *OPAModuleConfig) context.Context { - return context.WithValue(requestContext, OPAModuleConfigKey{}, permission) -} - -// GetOPAModuleConfig can be used by a request handler to get OPAModuleConfig instance from its context. -func GetOPAModuleConfig(requestContext context.Context) (*OPAModuleConfig, error) { - permission, ok := requestContext.Value(OPAModuleConfigKey{}).(*OPAModuleConfig) - if !ok { - return nil, fmt.Errorf("no opa module config found in request context") - } - - return permission, nil -} - type PermissionOnResourceKey string type PermissionsOnResourceMap map[PermissionOnResourceKey]bool diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 32c472d9..5bed8b78 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -22,13 +22,9 @@ import ( "regexp" "testing" - "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/metrics" - "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/mia-platform/glogger/v2" "github.com/open-policy-agent/opa/topdown/print" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" @@ -67,7 +63,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { opaModuleConfig, err := LoadRegoModule(opaModuleDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, logger, nil, openApiSpec, opaModuleConfig, nil) + policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -88,7 +84,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { opaModuleConfig, err := LoadRegoModule(opaModulesDirectory) require.NoError(t, err, "unexpected error") - policyEvals, err := SetupEvaluators(ctx, logger, nil, openApiSpec, opaModuleConfig, nil) + policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") require.Len(t, policyEvals, 4, "unexpected length") }) @@ -129,35 +125,22 @@ column_policy{ }, } - ctx := createContext(t, - context.Background(), - config.EnvironmentVariables{TargetServiceHost: "test"}, - nil, - &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "allow"}, - ResponseFlow: openapi.ResponseFlow{PolicyName: "column_policy"}, - }, - - &OPAModuleConfig{Name: "mypolicy.rego", Content: policy}, - nil, - ) + opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) - require.NoError(t, err, "Unexpected error") log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) input := Input{Request: InputRequest{}, Response: InputResponse{}} inputBytes, _ := json.Marshal(input) - t.Run("create evaluator with allowPolicy", func(t *testing.T) { - evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, permission.AllowPermission, inputBytes, nil, nil) + t.Run("create evaluator with allowPolicy", func(t *testing.T) { + evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.AllowPermission, inputBytes, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) t.Run("create evaluator with policy for column filtering", func(t *testing.T) { - evaluator, err := CreateQueryEvaluator(context.Background(), logger, r, permission.ResponseFilter.Policy, inputBytes, nil, nil) + evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.ResponseFilter.Policy, inputBytes, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) @@ -174,39 +157,6 @@ func TestPrint(t *testing.T) { require.JSONEq(t, `{"level":10,"msg":"the print message","time":123,"policyName":"policy-name"}`, string(re.ReplaceAll(buf.Bytes(), []byte("\"time\":123")))) } -func createContext( - t *testing.T, - originalCtx context.Context, - env config.EnvironmentVariables, - mongoClient *mocks.MongoClientMock, - permission *openapi.RondConfig, - opaModuleConfig *OPAModuleConfig, - partialResultEvaluators PartialResultsEvaluators, -) context.Context { - t.Helper() - - var partialContext context.Context - partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) - partialContext = context.WithValue(partialContext, openapi.XPermissionKey{}, permission) - partialContext = context.WithValue(partialContext, OPAModuleConfigKey{}, opaModuleConfig) - if mongoClient != nil { - partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) - } - partialContext = context.WithValue(partialContext, PartialResultsEvaluatorConfigKey{}, partialResultEvaluators) - - log, _ := test.NewNullLogger() - partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(log)) - - partialContext = context.WithValue(partialContext, openapi.RouterInfoKey{}, openapi.RouterInfo{ - MatchedPath: "/matched/path", - RequestedPath: "/requested/path", - Method: "GET", - }) - - partialContext = metrics.WithValue(partialContext, metrics.SetupMetrics("test_rond")) - - return partialContext -} func TestGetHeaderFunction(t *testing.T) { headerKeyMocked := "exampleKey" headerValueMocked := "value" @@ -258,19 +208,3 @@ func TestGetHeaderFunction(t *testing.T) { require.Len(t, partialResults.Queries, 0, "Rego policy allows illegal input") }) } - -func TestGetOPAModuleConfig(t *testing.T) { - t.Run(`GetOPAModuleConfig fails because no key has been passed`, func(t *testing.T) { - ctx := context.Background() - env, err := GetOPAModuleConfig(ctx) - require.True(t, err != nil, "An error was expected.") - t.Logf("Expected error: %s - env: %+v", err.Error(), env) - }) - - t.Run(`GetOPAModuleConfig returns OPAEvaluator from context`, func(t *testing.T) { - ctx := context.WithValue(context.Background(), OPAModuleConfigKey{}, &OPAModuleConfig{}) - opaEval, err := GetOPAModuleConfig(ctx) - require.True(t, err == nil, "Unexpected error.") - require.True(t, opaEval != nil, "OPA Module config not found.") - }) -} diff --git a/core/opamiddleware.go b/core/opamiddleware.go index 870eb1e8..62adc022 100644 --- a/core/opamiddleware.go +++ b/core/opamiddleware.go @@ -54,8 +54,12 @@ func OPAMiddleware( logger := glogger.Get(r.Context()) evaluator, err := sdk.FindEvaluator(logger, r.Method, path) - permission := evaluator.Config() - if r.Method == http.MethodGet && r.URL.Path == targetServiceOASPath && permission.RequestFlow.PolicyName == "" { + rondConfig := openapi.RondConfig{} + if err == nil { + rondConfig = evaluator.Config() + } + + if r.Method == http.MethodGet && r.URL.Path == targetServiceOASPath && rondConfig.RequestFlow.PolicyName == "" { fields := logrus.Fields{} if err != nil { fields["error"] = logrus.Fields{"message": err.Error()} @@ -65,13 +69,13 @@ func OPAMiddleware( return } - if err != nil || permission.RequestFlow.PolicyName == "" { + if err != nil || rondConfig.RequestFlow.PolicyName == "" { errorMessage := "User is not allowed to request the API" statusCode := http.StatusForbidden fields := logrus.Fields{ "originalRequestPath": utils.SanitizeString(r.URL.Path), "method": utils.SanitizeString(r.Method), - "allowPermission": utils.SanitizeString(permission.RequestFlow.PolicyName), + "allowPermission": utils.SanitizeString(rondConfig.RequestFlow.PolicyName), } technicalError := "" if err != nil { @@ -87,15 +91,7 @@ func OPAMiddleware( return } - // TODO: remove me - ctx := openapi.WithXPermission( - WithOPAModuleConfig( - openapi.WithRouterInfo(logger, r.Context(), r), - opaModuleConfig, - ), - &permission, - ) - ctx = WithEvaluatorSKD(ctx, evaluator) + ctx := WithEvaluatorSDK(r.Context(), evaluator) next.ServeHTTP(w, r.WithContext(ctx)) }) diff --git a/core/opamiddleware_test.go b/core/opamiddleware_test.go index e99bcf3f..c61806e3 100644 --- a/core/opamiddleware_test.go +++ b/core/opamiddleware_test.go @@ -27,7 +27,6 @@ import ( "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -41,7 +40,6 @@ func TestOPAMiddleware(t *testing.T) { sdk, err := NewSDK( context.Background(), logrus.NewEntry(logger), - nil, oas, opaModule, nil, @@ -184,19 +182,18 @@ foobar { true }`, openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) - t.Run(`rego package doesn't contain expected permission`, func(t *testing.T) { + t.Run(`rego package doesn't contain expected policy`, func(t *testing.T) { opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -todo { true }`, + Name: "example.rego", + Content: `package policies another { true }`, } sdk := getSDK(t, openAPISpec, opaModule) middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, permission, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err, "Unexpected error") + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -209,17 +206,16 @@ todo { true }`, t.Run(`rego package contains expected permission`, func(t *testing.T) { opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies -foobar { true }`, + Name: "example.rego", + Content: `package policies todo { true }`, } sdk := getSDK(t, openAPISpec, opaModule) middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, permission) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err) + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -240,9 +236,9 @@ very_very_composed_permission { true }`, middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err) + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -268,9 +264,9 @@ very_very_composed_permission_with_eval { true }`, middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err) + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -281,6 +277,21 @@ very_very_composed_permission_with_eval { true }`, require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) }) + + t.Run("with route to not proxy", func(t *testing.T) { + routesNotToProxy := []string{"/not/proxy"} + middleware := OPAMiddleware(nil, nil, routesNotToProxy, "", nil) + builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := GetEvaluatorSKD(r.Context()) + require.EqualError(t, err, "no SDKEvaluator found in request context") + })) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "http://example.com/not/proxy", nil) + builtHandler.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + }) } func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { @@ -300,7 +311,6 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { sdk, err := NewSDK( context.Background(), logger, - nil, openAPISpec, opaModule, nil, @@ -321,9 +331,9 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { sdk := getSdk(t, opaModule) middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, permission) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err) + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -344,9 +354,9 @@ very_very_composed_permission_with_eval { true }`, sdk := getSdk(t, opaModule) middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - permission, err := openapi.GetXPermission(r.Context()) - require.True(t, err == nil, "Unexpected error") - require.Equal(t, &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, permission) + actual, err := GetEvaluatorSKD(r.Context()) + require.NoError(t, err) + require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) diff --git a/core/sdk.go b/core/sdk.go index bb55f9f6..f8e92714 100644 --- a/core/sdk.go +++ b/core/sdk.go @@ -16,6 +16,7 @@ package core import ( "context" + "encoding/json" "fmt" "github.com/rond-authz/rond/internal/metrics" @@ -35,82 +36,161 @@ type PolicyResult struct { // Warning: This interface is experimental, and it could change with breaking also in rond patches. // Does not use outside this repository until it is not ready. type SDK interface { - // Warning: this method will be removed in the near future. Do not use it outside Rond. - Metrics() metrics.Metrics - FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) - EvaluatorFromConfig(logger *logrus.Entry, config openapi.RondConfig) SDKEvaluator } // Warning: This interface is experimental, and it could change with breaking also in rond patches. // Do not use outside this repository until it is not ready. type SDKEvaluator interface { Config() openapi.RondConfig - PartialResultsEvaluators() PartialResultsEvaluators + + EvaluateRequestPolicy(ctx context.Context, input RondInput, userInfo types.User) (PolicyResult, error) + EvaluateResponsePolicy(ctx context.Context, input RondInput, userInfo types.User, decodedBody any) ([]byte, error) } type evaluator struct { - rond rondImpl - logger *logrus.Entry - rondConfig openapi.RondConfig + logger *logrus.Entry + rondConfig openapi.RondConfig + opaModuleConfig *OPAModuleConfig + partialResultEvaluators PartialResultsEvaluators + + evaluatorOptions *EvaluatorOptions } func (e evaluator) Config() openapi.RondConfig { return e.rondConfig } -func (e evaluator) PartialResultsEvaluators() PartialResultsEvaluators { - return e.rond.evaluator +func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req RondInput, userInfo types.User) (PolicyResult, error) { + if req == nil { + return PolicyResult{}, fmt.Errorf("RondInput cannot be empty") + } + + rondConfig := e.Config() + + input, err := req.Input(userInfo, nil) + if err != nil { + return PolicyResult{}, err + } + + regoInput, err := CreateRegoQueryInput(e.logger, input, RegoInputOptions{ + EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, + }) + if err != nil { + return PolicyResult{}, nil + } + + var evaluatorAllowPolicy *OPAEvaluator + if !rondConfig.RequestFlow.GenerateQuery { + evaluatorAllowPolicy, err = e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, rondConfig.RequestFlow.PolicyName, regoInput, e.evaluatorOptions) + if err != nil { + return PolicyResult{}, err + } + } else { + evaluatorAllowPolicy, err = e.opaModuleConfig.CreateQueryEvaluator(ctx, e.logger, rondConfig.RequestFlow.PolicyName, regoInput, e.evaluatorOptions) + if err != nil { + return PolicyResult{}, err + } + } + + _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, &rondConfig) + + if err != nil { + e.logger.WithField("error", logrus.Fields{ + "policyName": rondConfig.RequestFlow.PolicyName, + "message": err.Error(), + }).Error("RBAC policy evaluation failed") + return PolicyResult{}, err + } + + var queryToProxy = []byte{} + if query != nil { + queryToProxy, err = json.Marshal(query) + if err != nil { + return PolicyResult{}, err + } + } + + return PolicyResult{ + Allowed: true, + QueryToProxy: queryToProxy, + }, nil } -// Current implementation of the SDK -type rondImpl struct { - evaluator PartialResultsEvaluators - evaluatorOptions *EvaluatorOptions - oasRouter *bunrouter.CompatRouter - oas *openapi.OpenAPISpec +func (e evaluator) EvaluateResponsePolicy(ctx context.Context, rondInput RondInput, userInfo types.User, decodedBody any) ([]byte, error) { + if rondInput == nil { + return nil, fmt.Errorf("RondInput cannot be empty") + } + + rondConfig := e.Config() + + input, err := rondInput.Input(userInfo, decodedBody) + if err != nil { + return nil, err + } - metrics metrics.Metrics - registry *prometheus.Registry + regoInput, err := CreateRegoQueryInput(e.logger, input, RegoInputOptions{ + EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, + }) + if err != nil { + return nil, err + } + + evaluator, err := e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, e.rondConfig.ResponseFlow.PolicyName, regoInput, e.evaluatorOptions) + if err != nil { + return nil, err + } + + bodyToProxy, err := evaluator.Evaluate(e.logger) + if err != nil { + return nil, err + } + + marshalledBody, err := json.Marshal(bodyToProxy) + if err != nil { + return nil, err + } + + return marshalledBody, nil +} + +type rondImpl struct { + partialResultEvaluators PartialResultsEvaluators + evaluatorOptions *EvaluatorOptions + oasRouter *bunrouter.CompatRouter + oas *openapi.OpenAPISpec + opaModuleConfig *OPAModuleConfig clientTypeHeaderKey string } func (r rondImpl) FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) { - permission, err := r.oas.FindPermission(r.oasRouter, path, method) + permission, routerInfo, err := r.oas.FindPermission(r.oasRouter, path, method) + if err != nil { + return nil, err + } return evaluator{ - rondConfig: permission, - logger: logger, - rond: r, + rondConfig: permission, + logger: logger, + opaModuleConfig: r.opaModuleConfig, + partialResultEvaluators: r.partialResultEvaluators, + evaluatorOptions: r.evaluatorOptions.WithRouterInfo(routerInfo), }, err } -func (r rondImpl) EvaluatorFromConfig(logger *logrus.Entry, config openapi.RondConfig) SDKEvaluator { - return evaluator{ - rondConfig: config, - logger: logger, - rond: r, - } -} - -func (r rondImpl) Metrics() metrics.Metrics { - return r.metrics -} - // The SDK is now into core because there are coupled function here which should use the SDK itself // (which uses core, so it will result in a cyclic dependency). In the future, sdk should be in a // specific package. func NewSDK( ctx context.Context, logger *logrus.Entry, - mongoClient types.IMongoClient, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, evaluatorOptions *EvaluatorOptions, registry *prometheus.Registry, clientTypeHeaderKey string, ) (SDK, error) { - evaluator, err := SetupEvaluators(ctx, logger, mongoClient, oas, opaModuleConfig, evaluatorOptions) + evaluator, err := SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) if err != nil { return nil, err } @@ -126,15 +206,17 @@ func NewSDK( if registry != nil { m.MustRegister(registry) } + if evaluatorOptions == nil { + evaluatorOptions = &EvaluatorOptions{} + } + evaluatorOptions.WithMetrics(m) return rondImpl{ - evaluator: evaluator, - oasRouter: oasRouter, - evaluatorOptions: evaluatorOptions, - oas: oas, - - metrics: m, - registry: registry, + partialResultEvaluators: evaluator, + oasRouter: oasRouter, + evaluatorOptions: evaluatorOptions, + oas: oas, + opaModuleConfig: opaModuleConfig, clientTypeHeaderKey: clientTypeHeaderKey, }, nil @@ -142,7 +224,7 @@ func NewSDK( type sdkKey struct{} -func WithEvaluatorSKD(ctx context.Context, evaluator SDKEvaluator) context.Context { +func WithEvaluatorSDK(ctx context.Context, evaluator SDKEvaluator) context.Context { return context.WithValue(ctx, sdkKey{}, evaluator) } diff --git a/core/sdk_test.go b/core/sdk_test.go index 895ee739..8d7cfcac 100644 --- a/core/sdk_test.go +++ b/core/sdk_test.go @@ -15,13 +15,19 @@ package core import ( + "bytes" "context" + "fmt" "net/http" + "net/http/httptest" "testing" + "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/expfmt" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -40,13 +46,13 @@ func TestNewSDK(t *testing.T) { } t.Run("fails if oas is nil", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, nil, nil, nil, nil, nil, "") + sdk, err := NewSDK(context.Background(), logger, nil, nil, nil, nil, "") require.ErrorContains(t, err, "oas must not be nil") require.Nil(t, sdk) }) t.Run("fails if opaModuleConfig is nil", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, nil, nil, nil, "") + sdk, err := NewSDK(context.Background(), logger, openAPISpec, nil, nil, nil, "") require.ErrorContains(t, err, "OPAModuleConfig must not be nil") require.Nil(t, sdk) }) @@ -54,20 +60,20 @@ func TestNewSDK(t *testing.T) { t.Run("fails if oas is invalid", func(t *testing.T) { oas, err := openapi.LoadOASFile("../mocks/invalidOASConfiguration.json") require.NoError(t, err) - sdk, err := NewSDK(context.Background(), logger, nil, oas, opaModule, nil, nil, "") + sdk, err := NewSDK(context.Background(), logger, oas, opaModule, nil, nil, "") require.ErrorContains(t, err, "invalid OAS configuration:") require.Nil(t, sdk) }) t.Run("creates sdk correctly", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, nil, "") + sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, nil, "") require.NoError(t, err) require.NotEmpty(t, sdk) }) t.Run("if registry is passed, setup metrics", func(t *testing.T) { registry := prometheus.NewRegistry() - sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, registry, "") + sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, registry, "") require.NoError(t, err) require.NotEmpty(t, sdk) }) @@ -85,38 +91,40 @@ func TestSDK(t *testing.T) { very_very_composed_permission { true }`, } registry := prometheus.NewRegistry() - sdk, err := NewSDK(context.Background(), logger, nil, openAPISpec, opaModule, nil, registry, "") + sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, registry, "") require.NoError(t, err) rond, ok := sdk.(rondImpl) require.True(t, ok, "rondImpl is not sdk") - t.Run("metrics", func(t *testing.T) { - require.Equal(t, rond.metrics, sdk.Metrics()) - }) - t.Run("FindEvaluator", func(t *testing.T) { t.Run("throws if path and method not found", func(t *testing.T) { actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/not-existent/path") require.ErrorContains(t, err, "not found oas definition: GET /not-existent/path") - require.Equal(t, evaluator{ - rondConfig: openapi.RondConfig{}, - logger: logger, - rond: rond, - }, actual) + require.Nil(t, actual) }) t.Run("returns correct evaluator", func(t *testing.T) { actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") require.NoError(t, err) + evaluatorOptions := &EvaluatorOptions{ + Metrics: rond.evaluatorOptions.Metrics, + RouterInfo: openapi.RouterInfo{ + MatchedPath: "/users/", + RequestedPath: "/users/", + Method: http.MethodGet, + }, + } require.Equal(t, evaluator{ rondConfig: openapi.RondConfig{ RequestFlow: openapi.RequestFlow{ PolicyName: "todo", }, }, - logger: logger, - rond: rond, + opaModuleConfig: opaModule, + partialResultEvaluators: rond.partialResultEvaluators, + logger: logger, + evaluatorOptions: evaluatorOptions, }, actual) t.Run("get permissions", func(t *testing.T) { @@ -126,32 +134,497 @@ func TestSDK(t *testing.T) { }, }, actual.Config()) }) + }) + }) +} + +func TestEvaluateRequestPolicy(t *testing.T) { + logger := logrus.NewEntry(logrus.New()) - t.Run("get partial evaluators", func(t *testing.T) { - require.Equal(t, rond.evaluator, actual.PartialResultsEvaluators()) + clientTypeHeaderKey := "client-header-key" + + t.Run("throws without RondInput", func(t *testing.T) { + sdk := getSdk(t, nil) + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + + _, err = evaluator.EvaluateRequestPolicy(context.Background(), nil, types.User{}) + require.EqualError(t, err, "RondInput cannot be empty") + }) + + type testCase struct { + method string + path string + opaModuleContent string + oasFilePath string + user types.User + reqHeaders map[string]string + mongoClient types.IMongoClient + + expectedPolicy PolicyResult + expectedErr bool + expectedErrMessage string + } + + t.Run("evaluate request", func(t *testing.T) { + testCases := map[string]testCase{ + "with empty user with policy true": { + method: http.MethodGet, + path: "/users/", + + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte{}, + }, + }, + "with user with policy true": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserID: "my-user", + }, + + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte{}, + }, + }, + "not allow if not existing policy": { + method: http.MethodPost, + path: "/users/", + user: types.User{ + UserID: "my-user", + }, + + expectedPolicy: PolicyResult{}, + expectedErr: true, + expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", + }, + "not allowed policy result": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserID: "my-user", + }, + opaModuleContent: `package policies todo { false }`, + + expectedPolicy: PolicyResult{}, + expectedErr: true, + expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", + }, + "with empty filter query": { + method: http.MethodGet, + path: "/users/", + oasFilePath: "../mocks/rondOasConfig.json", + user: types.User{ + UserGroups: []string{"my-group"}, + }, + reqHeaders: map[string]string{ + "my-header-key": "ok", + }, + opaModuleContent: ` + package policies + generate_filter { + input.user.groups[0] == "my-group" + get_header("my-header-key", input.request.headers) == "ok" + + query := data.resources[_] + }`, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, + }, + "with filter query": { + method: http.MethodGet, + path: "/users/", + oasFilePath: "../mocks/rondOasConfig.json", + user: types.User{ + UserGroups: []string{"my-group"}, + }, + reqHeaders: map[string]string{ + "my-header-key": "ok", + }, + opaModuleContent: ` + package policies + generate_filter { + query := data.resources[_] + query.filterField == "my-filter-value" + }`, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte(`{"$or":[{"$and":[{"filterField":{"$eq":"my-filter-value"}}]}]}`), + }, + }, + "check user": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserGroups: []string{"my-group"}, + UserRoles: []types.Role{ + { + RoleID: "rid", + }, + }, + UserBindings: []types.Binding{ + { + Resource: &types.Resource{ + ResourceType: "my-resource", + }, + }, + }, + Properties: map[string]any{ + "prop1": "my-user-field", + }, + }, + opaModuleContent: `package policies + todo { + input.user.groups[0] == "my-group" + input.user.roles[0].roleId == "rid" + input.user.bindings[0].resource.resourceType == "my-resource" + input.user.properties.prop1 == "my-user-field" + }`, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, + }, + "with mongo client and find_one": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserID: "my-user", + }, + mongoClient: &mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + opaModuleContent: `package policies + todo { + project := find_one("my-collection", {"myField": "1234"}) + project.myField == "1234" + } + `, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte{}, + }, + }, + "with mongo client and find_many": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserID: "my-user", + }, + mongoClient: &mocks.MongoClientMock{ + FindManyResult: []interface{}{ + map[string]interface{}{"myField": "1234"}, + }, + FindManyExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + opaModuleContent: `package policies + todo { + project := find_many("my-collection", {"myField": "1234"}) + project[0].myField == "1234" + } + `, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte{}, + }, + }, + "with query and mongo client": { + method: http.MethodGet, + path: "/users/", + oasFilePath: "../mocks/rondOasConfig.json", + user: types.User{ + UserGroups: []string{"my-group"}, + }, + reqHeaders: map[string]string{ + "my-header-key": "ok", + }, + mongoClient: &mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + opaModuleContent: ` + package policies + generate_filter { + project := find_one("my-collection", {"myField": "1234"}) + + query := data.resources[_] + query.filterField == "1234" + }`, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte(`{"$or":[{"$and":[{"filterField":{"$eq":"1234"}}]}]}`), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + sdk := getSdk(t, &sdkOptions{ + opaModuleContent: testCase.opaModuleContent, + oasFilePath: testCase.oasFilePath, + mongoClient: testCase.mongoClient, + registry: registry, + }) + + log, hook := test.NewNullLogger() + log.Level = logrus.DebugLevel + logger := logrus.NewEntry(log) + evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) + require.NoError(t, err) + + req := httptest.NewRequest(testCase.method, testCase.path, nil) + if testCase.reqHeaders != nil { + for k, v := range testCase.reqHeaders { + req.Header.Set(k, v) + } + } + rondInput := NewRondInput(req, clientTypeHeaderKey, nil) + + actual, err := evaluate.EvaluateRequestPolicy(context.Background(), rondInput, testCase.user) + if testCase.expectedErr { + require.EqualError(t, err, testCase.expectedErrMessage) + } else { + require.NoError(t, err) + } + require.Equal(t, testCase.expectedPolicy, actual) + + t.Run("logger", func(t *testing.T) { + var actualEntry *logrus.Entry + for _, entry := range hook.AllEntries() { + if entry.Message == "policy evaluation completed" { + actualEntry = entry + } + } + evaluatorInfo := evaluate.(evaluator) + + require.NotNil(t, actual) + delete(actualEntry.Data, "evaluationTimeMicroseconds") + + resultLength := 1 + if !actual.Allowed { + resultLength = 0 + } + + fields := logrus.Fields{ + "allowed": actual.Allowed, + "requestedPath": testCase.path, + "matchedPath": evaluatorInfo.evaluatorOptions.RouterInfo.MatchedPath, + "method": testCase.method, + "partialEval": evaluate.Config().RequestFlow.GenerateQuery, + "policyName": evaluate.Config().RequestFlow.PolicyName, + } + + if !evaluate.Config().RequestFlow.GenerateQuery { + fields["resultsLength"] = resultLength + } + + require.Equal(t, fields, actualEntry.Data) + }) + + t.Run("metrics", func(t *testing.T) { + expected := fmt.Sprintf(`rond_policy_evaluation_duration_milliseconds_count{policy_name="%s"} 1`, evaluate.Config().RequestFlow.PolicyName) + assertCorrectMetrics(t, registry, expected) + }) }) - }) + } }) +} - t.Run("EvaluatorFromConfig", func(t *testing.T) { - rondConfig := openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ - PolicyName: "todo", - GenerateQuery: true, +func assertCorrectMetrics(t *testing.T, registry *prometheus.Registry, expected string) { + t.Helper() + + g := prometheus.ToTransactionalGatherer(registry) + got, done, err := g.Gather() + defer done() + require.NoError(t, err) + + for _, m := range got { + if m.GetName() == "rond_policy_evaluation_duration_milliseconds" { + var gotBuf bytes.Buffer + enc := expfmt.NewEncoder(&gotBuf, expfmt.FmtText) + err := enc.Encode(m) + require.NoError(t, err) + require.Contains(t, gotBuf.String(), expected) + return + } + } + require.Fail(t, "metrics must be retrieved") +} + +func TestEvaluateResponsePolicy(t *testing.T) { + logger := logrus.NewEntry(logrus.New()) + + clientTypeHeaderKey := "client-header-key" + + t.Run("throws without RondInput", func(t *testing.T) { + sdk := getSdk(t, nil) + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + + _, err = evaluator.EvaluateResponsePolicy(context.Background(), nil, types.User{}, nil) + require.EqualError(t, err, "RondInput cannot be empty") + }) + + type testCase struct { + method string + path string + opaModuleContent string + user types.User + reqHeaders map[string]string + mongoClient types.IMongoClient + + decodedBody any + + expectedBody string + expectedErr bool + expectedErrMessage string + } + + t.Run("evaluate response", func(t *testing.T) { + testCases := map[string]testCase{ + "with empty user and empty object": { + method: http.MethodGet, + path: "/users/", + decodedBody: map[string]interface{}{}, + + expectedBody: "{}", }, - ResponseFlow: openapi.ResponseFlow{ - PolicyName: "other", + "with body unchanged": { + method: http.MethodGet, + path: "/users/", + + decodedBody: map[string]interface{}{"foo": "bar", "f1": "b1"}, + + expectedBody: `{"f1":"b1","foo":"bar"}`, + }, + "with body changed": { + method: http.MethodGet, + path: "/users/", + opaModuleContent: ` + package policies + responsepolicy [body] { + originalBody := input.response.body + + body := json.patch(originalBody, [{"op": "replace", "path": "f1", "value": "censored"}]) + }`, + + decodedBody: map[string]interface{}{"foo": "bar", "f1": "b1"}, + + expectedBody: `{"f1":"censored","foo":"bar"}`, + }, + "with mongo query and body changed": { + method: http.MethodGet, + path: "/users/", + opaModuleContent: ` + package policies + responsepolicy [body] { + originalBody := input.response.body + project := find_one("my-collection", {"myField": "1234"}) + + body := json.patch(originalBody, [ + {"op": "replace", "path": "f1", "value": "censored"}, + {"op": "add", "path": "some", "value": project.myField} + ]) + }`, + mongoClient: &mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + + decodedBody: map[string]interface{}{"foo": "bar", "f1": "b1"}, + + expectedBody: `{"f1":"censored","foo":"bar","some":"1234"}`, }, } - t.Run("returns evaluator passing RondConfig", func(t *testing.T) { - actual := sdk.EvaluatorFromConfig(logger, rondConfig) - require.Equal(t, evaluator{ - rondConfig: rondConfig, - logger: logger, - rond: rond, - }, actual) - }) + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + opaModuleContent := ` + package policies + responsepolicy [body] { + body := input.response.body + }` + + if testCase.opaModuleContent != "" { + opaModuleContent = testCase.opaModuleContent + } + + log, hook := test.NewNullLogger() + log.Level = logrus.DebugLevel + logger := logrus.NewEntry(log) + registry := prometheus.NewPedanticRegistry() + sdk := getSdk(t, &sdkOptions{ + opaModuleContent: opaModuleContent, + oasFilePath: "../mocks/rondOasConfig.json", + mongoClient: testCase.mongoClient, + registry: registry, + }) + + evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) + require.NoError(t, err) + + req := httptest.NewRequest(testCase.method, testCase.path, nil) + if testCase.reqHeaders != nil { + for k, v := range testCase.reqHeaders { + req.Header.Set(k, v) + } + } + rondInput := NewRondInput(req, clientTypeHeaderKey, nil) + + actual, err := evaluate.EvaluateResponsePolicy(context.Background(), rondInput, testCase.user, testCase.decodedBody) + if testCase.expectedErr { + require.EqualError(t, err, testCase.expectedErrMessage) + } else { + require.NoError(t, err) + } + require.JSONEq(t, testCase.expectedBody, string(actual)) + + t.Run("logger", func(t *testing.T) { + var actual *logrus.Entry + for _, entry := range hook.AllEntries() { + if entry.Message == "policy evaluation completed" { + actual = entry + } + } + evaluatorInfo := evaluate.(evaluator) + + require.NotNil(t, actual) + delete(actual.Data, "evaluationTimeMicroseconds") + require.Equal(t, logrus.Fields{ + "allowed": false, + "requestedPath": testCase.path, + "matchedPath": evaluatorInfo.evaluatorOptions.RouterInfo.MatchedPath, + "method": testCase.method, + "partialEval": false, + "policyName": evaluate.Config().ResponseFlow.PolicyName, + "resultsLength": 1, + }, actual.Data) + }) + + t.Run("metrics", func(t *testing.T) { + expected := fmt.Sprintf(`rond_policy_evaluation_duration_milliseconds_count{policy_name="%s"} 1`, evaluate.Config().ResponseFlow.PolicyName) + assertCorrectMetrics(t, registry, expected) + }) + }) + } }) } @@ -172,7 +645,7 @@ func TestContext(t *testing.T) { rondConfig: rondConfig, } - ctx = WithEvaluatorSKD(ctx, expectedEvaluator) + ctx = WithEvaluatorSDK(ctx, expectedEvaluator) actualEvaluator, err := GetEvaluatorSKD(ctx) require.NoError(t, err) @@ -185,3 +658,182 @@ func TestContext(t *testing.T) { require.Nil(t, actualEvaluator) }) } + +func BenchmarkEvaluateRequest(b *testing.B) { + moduleConfig, err := LoadRegoModule("../mocks/bench-policies") + require.NoError(b, err, "Unexpected error") + + openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") + require.NoError(b, err) + + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + sdk, err := NewSDK(context.Background(), logger, openAPISpec, moduleConfig, &EvaluatorOptions{ + MongoClient: testmongoMock, + }, nil, "") + require.NoError(b, err) + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + b.StopTimer() + req := httptest.NewRequest(http.MethodGet, "/projects/project123", nil) + req.Header.Set("my-header", "value") + recorder := httptest.NewRecorder() + rondInput := NewRondInput(req, "", map[string]string{ + "projectId": "project123", + }) + b.StartTimer() + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/project123") + require.NoError(b, err) + evaluator.EvaluateRequestPolicy(context.Background(), rondInput, types.User{}) + b.StopTimer() + require.Equal(b, http.StatusOK, recorder.Code) + } +} + +type sdkOptions struct { + opaModuleContent string + oasFilePath string + + mongoClient types.IMongoClient + registry *prometheus.Registry +} + +type tHelper interface { + Helper() +} + +func getSdk(t require.TestingT, options *sdkOptions) SDK { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + logger := logrus.NewEntry(logrus.New()) + if options == nil { + options = &sdkOptions{} + } + + var oasFilePath = "../mocks/simplifiedMock.json" + if options.oasFilePath != "" { + oasFilePath = options.oasFilePath + } + + openAPISpec, err := openapi.LoadOASFile(oasFilePath) + require.NoError(t, err) + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + todo { true }`, + } + if options.opaModuleContent != "" { + opaModule.Content = options.opaModuleContent + } + sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, &EvaluatorOptions{ + EnablePrintStatements: true, + MongoClient: options.mongoClient, + }, options.registry, "") + require.NoError(t, err) + + return sdk +} + +var testmongoMock = &mocks.MongoClientMock{ + UserBindings: []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"admin"}, + Groups: []string{"area_rocket"}, + Permissions: []string{"permission4"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "project123", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding3", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission10", "permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding4", + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission11"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFiltering", + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFilteringFromSubject", + Subjects: []string{"filter_test"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "notUsedByAnyone", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permissionNotUsed"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "notUsedByAnyone2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role6"}, + Permissions: []string{"permissionNotUsed"}, + CRUDDocumentState: "PRIVATE", + }, + }, + UserRoles: []types.Role{ + { + RoleID: "admin", + Permissions: []string{"console.project.view", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role6", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PRIVATE", + }, + { + RoleID: "notUsedByAnyone", + Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, + CRUDDocumentState: "PUBLIC", + }, + }, +} diff --git a/custom_builtins/mongo.go b/custom_builtins/mongo.go index 970d8f35..30856b61 100644 --- a/custom_builtins/mongo.go +++ b/custom_builtins/mongo.go @@ -15,6 +15,8 @@ package custom_builtins import ( + "fmt" + "github.com/rond-authz/rond/internal/mongoclient" "github.com/open-policy-agent/opa/ast" @@ -43,6 +45,9 @@ var MongoFindOne = rego.Function2( if err != nil { return nil, err } + if mongoClient == nil { + return nil, fmt.Errorf("mongo client not set") + } var collectionName string if err := ast.As(collectionNameTerm.Value, &collectionName); err != nil { @@ -89,6 +94,9 @@ var MongoFindMany = rego.Function2( if err != nil { return nil, err } + if mongoClient == nil { + return nil, fmt.Errorf("mongo client not set") + } var collectionName string if err := ast.As(collectionNameTerm.Value, &collectionName); err != nil { diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go index a488e19a..3d4eb8a6 100644 --- a/internal/fake/sdk.go +++ b/internal/fake/sdk.go @@ -15,7 +15,7 @@ package fake import ( - "net/http" + "context" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/openapi" @@ -46,17 +46,17 @@ func NewSDKEvaluator( } } -func (s SDKEvaluator) EvaluateRequestPolicy(req *http.Request, userInfo types.User, permission *openapi.RondConfig) (core.PolicyResult, error) { +func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (core.PolicyResult, error) { if s.requestPolicyEvaluatorResult == nil { return core.PolicyResult{}, nil } return core.PolicyResult{}, s.requestPolicyEvaluatorResult.Err } -func (s SDKEvaluator) Config() openapi.RondConfig { - return s.permission +func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) { + return nil, nil } -func (s SDKEvaluator) PartialResultsEvaluators() core.PartialResultsEvaluators { - return s.partialEvaluator +func (s SDKEvaluator) Config() openapi.RondConfig { + return s.permission } diff --git a/internal/metrics/routes.go b/internal/metrics/routes.go index 60bb122c..cca09386 100644 --- a/internal/metrics/routes.go +++ b/internal/metrics/routes.go @@ -15,10 +15,6 @@ package metrics import ( - "context" - "fmt" - "net/http" - "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -35,28 +31,3 @@ func MetricsRoute(r *mux.Router, registry *prometheus.Registry) { }), )) } - -type metricsContextKey struct{} - -// RequestMiddleware is a gorilla/mux middleware used to inject -// metrics struct into requests. -func RequestMiddleware(m Metrics) mux.MiddlewareFunc { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := WithValue(r.Context(), m) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} - -func WithValue(ctx context.Context, m Metrics) context.Context { - return context.WithValue(ctx, metricsContextKey{}, m) -} - -func GetFromContext(ctx context.Context) (Metrics, error) { - m, ok := ctx.Value(metricsContextKey{}).(Metrics) - if !ok { - return Metrics{}, fmt.Errorf("invalid metrics in context") - } - return m, nil -} diff --git a/internal/metrics/routes_test.go b/internal/metrics/routes_test.go index 9f1046f5..2c3085ce 100644 --- a/internal/metrics/routes_test.go +++ b/internal/metrics/routes_test.go @@ -15,48 +15,26 @@ package metrics import ( - "context" "net/http" "net/http/httptest" "testing" + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" ) -func TestRequestMiddleware(t *testing.T) { - expectedMetrics := SetupMetrics("test_prefix") - - t.Run("set metrics in context", func(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - m, err := GetFromContext(r.Context()) - require.NoError(t, err) - require.Equal(t, expectedMetrics, m) - - w.WriteHeader(202) - }) - - handlerToTest := RequestMiddleware(expectedMetrics).Middleware(handler) +func TestMetricsRoute(t *testing.T) { + t.Run("exposes metrics route", func(t *testing.T) { + router := mux.NewRouter() + registry := prometheus.NewRegistry() + MetricsRoute(router, registry) + req := httptest.NewRequest(http.MethodGet, MetricsRoutePath, nil) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/path", nil) - handlerToTest.ServeHTTP(w, req) - require.Equal(t, http.StatusAccepted, w.Result().StatusCode) - }) -} - -func TestGetFromContext(t *testing.T) { - t.Run("ok", func(t *testing.T) { - expectedMetrics := SetupMetrics("test_prefix") - ctx := WithValue(context.Background(), expectedMetrics) - m, err := GetFromContext(ctx) - require.NoError(t, err) - require.Equal(t, expectedMetrics, m) - }) + router.ServeHTTP(w, req) - t.Run("metrics not in context", func(t *testing.T) { - m, err := GetFromContext(context.Background()) - require.EqualError(t, err, "invalid metrics in context") - require.Empty(t, m) + require.Equal(t, http.StatusOK, w.Result().StatusCode) }) } diff --git a/internal/mocks/mongoclient.go b/internal/mocks/mongoclient.go index 3b4a78f5..bfb84b55 100644 --- a/internal/mocks/mongoclient.go +++ b/internal/mocks/mongoclient.go @@ -61,6 +61,9 @@ func (mongoClient MongoClientMock) RetrieveUserRolesByRolesID(ctx context.Contex } func (mongoClient MongoClientMock) FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) { + if mongoClient.FindOneExpectation == nil { + panic("FindOneExpectation is required") + } mongoClient.FindOneExpectation(collectionName, query) if mongoClient.FindOneError != nil { return nil, mongoClient.FindOneError @@ -70,6 +73,9 @@ func (mongoClient MongoClientMock) FindOne(ctx context.Context, collectionName s } func (mongoClient MongoClientMock) FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) { + if mongoClient.FindManyExpectation == nil { + panic("FindManyExpectation is required") + } mongoClient.FindManyExpectation(collectionName, query) if mongoClient.FindManyError != nil { return nil, mongoClient.FindManyError diff --git a/main.go b/main.go index 1c7aa2bb..22ace354 100644 --- a/main.go +++ b/main.go @@ -99,8 +99,9 @@ func entrypoint(shutdown chan os.Signal) { ) registry := prometheus.NewRegistry() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(log), mongoClient, oas, opaModuleConfig, &core.EvaluatorOptions{ + sdk, err := core.NewSDK(ctx, logrus.NewEntry(log), oas, opaModuleConfig, &core.EvaluatorOptions{ EnablePrintStatements: env.IsTraceLogLevel(), + MongoClient: mongoClient, }, registry, env.ClientTypeHeader) if err != nil { log.WithFields(logrus.Fields{ diff --git a/main_test.go b/main_test.go index 50c53131..78040950 100644 --- a/main_test.go +++ b/main_test.go @@ -1795,7 +1795,9 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ + MongoClient: mongoClient, + }, registry, "") require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) @@ -1952,7 +1954,9 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ + MongoClient: mongoClient, + }, registry, "") require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) diff --git a/mocks/bench.json b/mocks/bench.json new file mode 100644 index 00000000..ad8ff2de --- /dev/null +++ b/mocks/bench.json @@ -0,0 +1,79 @@ +{ + "swagger": "2.0", + "info": { + "title": "Crud Service", + "description": "HTTP interface to perform CRUD operations on MongoDB collections defined in the API Console", + "version": "3.2.3" + }, + "paths": { + "/projects/:projectId": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "allow_view_project" + } + }, + "summary": "Get a list of users", + "description": "The list can be filtered specifying the following parameters", + "tags": [ + "Users" + ], + "parameters": [ + { + "type": "string", + "required": false, + "name": "projectId", + "in": "params" + } + ], + "responses": { + "200": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "pattern": "^[a-fA-F\\d]{24}$", + "description": "_id", + "example": "617973697254f500156168e2" + }, + "creatorId": { + "type": "string", + "description": "creatorId" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "example": "2020-09-16T12:00:00.000Z", + "description": "createdAt" + }, + "updaterId": { + "type": "string", + "description": "updaterId" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "example": "2020-09-16T12:00:00.000Z", + "description": "updatedAt" + }, + "__STATE__": { + "type": "string", + "description": "__STATE__" + }, + "name": { + "type": "string", + "description": "name of the user" + } + } + } + }, + "description": "Default Response" + } + } + } + } + } +} diff --git a/mocks/rondOasConfig.json b/mocks/rondOasConfig.json index 3d99de4b..22026422 100644 --- a/mocks/rondOasConfig.json +++ b/mocks/rondOasConfig.json @@ -35,4 +35,4 @@ "get": {} } } -} \ No newline at end of file +} diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 03e71f34..689c1c29 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -51,6 +51,12 @@ var ( var ErrNotFoundOASDefinition = errors.New("not found oas definition") +type RouterInfo struct { + MatchedPath string + RequestedPath string + Method string +} + type XPermissionKey struct{} type PermissionOptions struct { @@ -144,7 +150,7 @@ func (rMap RoutesMap) contains(path string, method string) bool { return hasRoute } -func createOasHandler(scopedMethodContent VerbConfig) func(http.ResponseWriter, *http.Request) { +func createOasHandler(scopedMethodContent VerbConfig, oasPathCleaned string) func(http.ResponseWriter, *http.Request) { permission := scopedMethodContent.PermissionV2 return func(w http.ResponseWriter, r *http.Request) { header := w.Header() @@ -154,6 +160,7 @@ func createOasHandler(scopedMethodContent VerbConfig) func(http.ResponseWriter, header.Set("responseFilter.policy", permission.ResponseFlow.PolicyName) header.Set("options.enableResourcePermissionsMapOptimization", strconv.FormatBool(permission.Options.EnableResourcePermissionsMapOptimization)) header.Set("options.ignoreTrailingSlash", strconv.FormatBool(permission.Options.IgnoreTrailingSlash)) + header.Set("pathTemplate", oasPathCleaned) } } @@ -167,10 +174,12 @@ func (oas *OpenAPISpec) PrepareOASRouter() (*bunrouter.CompatRouter, error) { } for OASPath, OASContent := range oas.Paths { - OASPathCleaned := ConvertPathVariablesToColons(cleanWildcard(OASPath)) + pathWithPathVariablesToColons := ConvertPathVariablesToColons(OASPath) + OASPathCleaned := cleanWildcard(pathWithPathVariablesToColons) + for method, methodContent := range OASContent { scopedMethod := strings.ToUpper(method) - handler := createOasHandler(methodContent) + handler := createOasHandler(methodContent, pathWithPathVariablesToColons) if scopedMethod != strings.ToUpper(AllHTTPMethod) { registerLaxPath(OASRouter, scopedMethod, methodContent, OASPathCleaned, handler) @@ -202,28 +211,38 @@ func registerLaxPath(OASRouter *bunrouter.CompatRouter, method string, methodCon } // FIXME: This is not a logic method of OAS, but could be a method of OASRouter -func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path string, method string) (RondConfig, error) { +func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path string, method string) (RondConfig, RouterInfo, error) { + routerInfo := RouterInfo{ + Method: method, + RequestedPath: path, + } + recorder := httptest.NewRecorder() responseReader := strings.NewReader("request-permissions") - request, _ := http.NewRequest(method, path, responseReader) + request, err := http.NewRequest(method, path, responseReader) + if err != nil { + return RondConfig{}, routerInfo, err + } OASRouter.ServeHTTP(recorder, request) if recorder.Code != http.StatusOK { - return RondConfig{}, fmt.Errorf("%w: %s %s", ErrNotFoundOASDefinition, utils.SanitizeString(method), utils.SanitizeString(path)) + return RondConfig{}, routerInfo, fmt.Errorf("%w: %s %s", ErrNotFoundOASDefinition, utils.SanitizeString(method), utils.SanitizeString(path)) } recorderResult := recorder.Result() + pathTemplate := recorderResult.Header.Get("pathTemplate") + routerInfo.MatchedPath = pathTemplate rowFilterEnabled, err := strconv.ParseBool(recorderResult.Header.Get("resourceFilter.rowFilter.enabled")) if err != nil { - return RondConfig{}, fmt.Errorf("error while parsing rowFilter.enabled: %s", err) + return RondConfig{}, routerInfo, fmt.Errorf("error while parsing rowFilter.enabled: %s", err) } enableResourcePermissionsMapOptimization, err := strconv.ParseBool(recorderResult.Header.Get("options.enableResourcePermissionsMapOptimization")) if err != nil { - return RondConfig{}, fmt.Errorf("error while parsing options.enableResourcePermissionsMapOptimization: %s", err) + return RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.enableResourcePermissionsMapOptimization: %s", err) } ignoreTrailingSlash, err := strconv.ParseBool(recorderResult.Header.Get("options.ignoreTrailingSlash")) if err != nil { - return RondConfig{}, fmt.Errorf("error while parsing options.ignoreTrailingSlash: %s", err) + return RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.ignoreTrailingSlash: %s", err) } return RondConfig{ RequestFlow: RequestFlow{ @@ -240,7 +259,7 @@ func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path s EnableResourcePermissionsMapOptimization: enableResourcePermissionsMapOptimization, IgnoreTrailingSlash: ignoreTrailingSlash, }, - }, nil + }, routerInfo, nil } func newRondConfigFromPermissionV1(v1Permission *XPermission) *RondConfig { diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index b4936c8e..f5276cf1 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -291,19 +291,39 @@ func TestFindPermission(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") OASRouter, _ := oas.PrepareOASRouter() - found, err := oas.FindPermission(OASRouter, "/not/existing/route", "GET") + found, matchedPath, err := oas.FindPermission(OASRouter, "/not/existing/route", "/invalid-method") + require.Empty(t, RondConfig{}, found) + require.EqualError(t, err, "net/http: invalid method \"/invalid-method\"") + require.Equal(t, RouterInfo{ + Method: "/invalid-method", + RequestedPath: "/not/existing/route", + }, matchedPath) + + found, matchedPath, err = oas.FindPermission(OASRouter, "/not/existing/route", "GET") require.Empty(t, RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: GET /not/existing/route", ErrNotFoundOASDefinition)) + require.Equal(t, RouterInfo{ + Method: "GET", + RequestedPath: "/not/existing/route", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/no/method", "PUT") + found, matchedPath, err = oas.FindPermission(OASRouter, "/no/method", "PUT") require.Equal(t, RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: PUT /no/method", ErrNotFoundOASDefinition)) + require.Equal(t, RouterInfo{ + Method: "PUT", + RequestedPath: "/no/method", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/use/method/that/not/existing/put", "PUT") + found, matchedPath, err = oas.FindPermission(OASRouter, "/use/method/that/not/existing/put", "PUT") require.Equal(t, RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: PUT /use/method/that/not/existing/put", ErrNotFoundOASDefinition)) + require.Equal(t, RouterInfo{ + Method: "PUT", + RequestedPath: "/use/method/that/not/existing/put", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/foo/bar/barId", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/barId", "GET") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ @@ -314,8 +334,13 @@ func TestFindPermission(t *testing.T) { }, }, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/foo/bar/:params", + RequestedPath: "/foo/bar/barId", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/foo/bar/barId/another-params-not-configured", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/barId/another-params-not-configured", "GET") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ @@ -326,12 +351,22 @@ func TestFindPermission(t *testing.T) { }, }, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/foo/bar/*", + RequestedPath: "/foo/bar/barId/another-params-not-configured", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/foo/bar/nested/case/really/nested", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/nested/case/really/nested", "GET") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/foo/bar/nested/case/*", + RequestedPath: "/foo/bar/nested/case/really/nested", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/foo/bar/nested", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/nested", "GET") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ @@ -342,8 +377,13 @@ func TestFindPermission(t *testing.T) { }, }, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/foo/bar/nested", + RequestedPath: "/foo/bar/nested", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/foo/simble", "PATCH") + found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/simple", "PATCH") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{ @@ -354,89 +394,173 @@ func TestFindPermission(t *testing.T) { }, }, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/foo/*", + RequestedPath: "/foo/simple", + Method: "PATCH", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all", "GET") require.Equal(t, RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: GET /test/all", ErrNotFoundOASDefinition)) + require.Equal(t, RouterInfo{ + Method: "GET", + RequestedPath: "/test/all", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/", "GET") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "GET") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "POST") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "POST") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "POST", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PUT") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "PUT") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "PUT", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "PATCH") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "PATCH") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "PATCH", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "DELETE") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "DELETE") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "DELETE", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/test/all/verb", "HEAD") + found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "HEAD") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/test/all/*", + RequestedPath: "/test/all/verb", + Method: "HEAD", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/projects/", "POST") + found, matchedPath, err = oas.FindPermission(OASRouter, "/projects/", "POST") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/projects/", + RequestedPath: "/projects/", + Method: "POST", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/projects/", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/projects/", "GET") require.NoError(t, err) require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_get"}}, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/projects/", + RequestedPath: "/projects/", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/with/trailing/slash/", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/with/trailing/slash/", "GET") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{PolicyName: "foo_bar"}, ResponseFlow: ResponseFlow{PolicyName: "original_path"}, Options: PermissionOptions{IgnoreTrailingSlash: true}, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/with/trailing/slash/", + RequestedPath: "/with/trailing/slash/", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/with/trailing/slash", "GET") + found, matchedPath, err = oas.FindPermission(OASRouter, "/with/trailing/slash", "GET") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{PolicyName: "foo_bar"}, ResponseFlow: ResponseFlow{PolicyName: "original_path"}, Options: PermissionOptions{IgnoreTrailingSlash: true}, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/with/trailing/slash/", + RequestedPath: "/with/trailing/slash", + Method: "GET", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/without/trailing/slash", "POST") + found, matchedPath, err = oas.FindPermission(OASRouter, "/without/trailing/slash", "POST") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{PolicyName: "foo_bar"}, Options: PermissionOptions{IgnoreTrailingSlash: true}, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/without/trailing/slash", + RequestedPath: "/without/trailing/slash", + Method: "POST", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/without/trailing/slash/", "POST") + found, matchedPath, err = oas.FindPermission(OASRouter, "/without/trailing/slash/", "POST") require.NoError(t, err) require.Equal(t, RondConfig{ RequestFlow: RequestFlow{PolicyName: "foo_bar"}, Options: PermissionOptions{IgnoreTrailingSlash: true}, }, found) + require.Equal(t, RouterInfo{ + MatchedPath: "/without/trailing/slash", + RequestedPath: "/without/trailing/slash/", + Method: "POST", + }, matchedPath) }) t.Run("encoded cases", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/mockForEncodedTest.json") OASRouter, _ := oas.PrepareOASRouter() - found, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") + found, matchedPath, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/api/backend/projects/:projectId/branches/:branchName/files/:filePath", + Method: "POST", + RequestedPath: "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", + }, matchedPath) - found, err = oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%2Fcms-backend%2FcmsProperties.json", "POST") + found, matchedPath, err = oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%2Fcms-backend%2FcmsProperties.json", "POST") require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) require.NoError(t, err) + require.Equal(t, RouterInfo{ + MatchedPath: "/api/backend/projects/:projectId/branches/:branchName/files/:filePath", + Method: "POST", + RequestedPath: "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%2Fcms-backend%2FcmsProperties.json", + }, matchedPath) }) } diff --git a/openapi/routerinfo_middleware.go b/openapi/routerinfo_middleware.go deleted file mode 100644 index 64900b9f..00000000 --- a/openapi/routerinfo_middleware.go +++ /dev/null @@ -1,65 +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 openapi - -import ( - "context" - "fmt" - "net/http" - - "github.com/rond-authz/rond/internal/utils" - - "github.com/gorilla/mux" - "github.com/sirupsen/logrus" -) - -// TODO: This should be made private in the future. -type RouterInfoKey struct{} - -type RouterInfo struct { - MatchedPath string - RequestedPath string - Method string -} - -func WithRouterInfo(logger *logrus.Entry, requestContext context.Context, req *http.Request) context.Context { - pathTemplate := getPathTemplateOrDefaultToEmptyString(logger, req) - return context.WithValue(requestContext, RouterInfoKey{}, RouterInfo{ - MatchedPath: utils.SanitizeString(pathTemplate), - RequestedPath: utils.SanitizeString(req.URL.Path), - Method: utils.SanitizeString(req.Method), - }) -} - -func getPathTemplateOrDefaultToEmptyString(logger *logrus.Entry, req *http.Request) string { - var pathTemplate string - route := mux.CurrentRoute(req) - if route != nil { - var err error - if pathTemplate, err = route.GetPathTemplate(); err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("path template is empty") - return "" - } - } - return pathTemplate -} - -func GetRouterInfo(requestContext context.Context) (RouterInfo, error) { - routerInfo, ok := requestContext.Value(RouterInfoKey{}).(RouterInfo) - if !ok { - return RouterInfo{}, fmt.Errorf("no router info found") - } - return routerInfo, nil -} diff --git a/openapi/routerinfo_middleware_test.go b/openapi/routerinfo_middleware_test.go deleted file mode 100644 index fbe5fcb6..00000000 --- a/openapi/routerinfo_middleware_test.go +++ /dev/null @@ -1,127 +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 openapi - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gorilla/mux" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/require" -) - -func TestRouterInfoContext(t *testing.T) { - nullLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(nullLogger) - - t.Run("GetRouterInfo fails because no key has been set", func(t *testing.T) { - ctx := context.Background() - routerInfo, err := GetRouterInfo(ctx) - require.EqualError(t, err, "no router info found") - require.Empty(t, routerInfo) - }) - - t.Run("WithRouterInfo not inside mux router - empty matched path", func(t *testing.T) { - ctx := context.Background() - req := httptest.NewRequest("GET", "/hello", nil) - ctx = WithRouterInfo(logger, ctx, req) - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "", - RequestedPath: "/hello", - Method: "GET", - }, routerInfo) - }) - - t.Run("WithRouterInfo without router path - matched path is empty", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.NewRoute().HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "", - RequestedPath: "/hello", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) - - t.Run("correctly get router info", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.HandleFunc("/hello/{name}", func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "/hello/{name}", - RequestedPath: "/hello/my-username", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello/my-username", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) - - t.Run("correctly get router info with path prefix", func(t *testing.T) { - ctx := context.Background() - router := mux.NewRouter() - - router.PathPrefix("/hello/").HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := WithRouterInfo(logger, ctx, req) - - routerInfo, err := GetRouterInfo(ctx) - require.NoError(t, err) - require.Equal(t, RouterInfo{ - MatchedPath: "/hello/", - RequestedPath: "/hello/my-username", - Method: "GET", - }, routerInfo) - - w.Write([]byte("ok")) - }) - - w := httptest.NewRecorder() - req := httptest.NewRequest("GET", "/hello/my-username", nil) - router.ServeHTTP(w, req) - - require.Equal(t, 200, w.Result().StatusCode) - }) -} diff --git a/service/handler.go b/service/handler.go index cf631b3b..01b3b5f7 100644 --- a/service/handler.go +++ b/service/handler.go @@ -15,7 +15,6 @@ package service import ( - "encoding/json" "errors" "net/http" "net/http/httputil" @@ -44,10 +43,8 @@ func ReverseProxyOrResponse( evaluatorSdk core.SDKEvaluator, ) { var permission openapi.RondConfig - var partialResultsEvaluators core.PartialResultsEvaluators if evaluatorSdk != nil { permission = evaluatorSdk.Config() - partialResultsEvaluators = evaluatorSdk.PartialResultsEvaluators() } if env.Standalone { @@ -65,7 +62,7 @@ func ReverseProxyOrResponse( } return } - ReverseProxy(logger, env, w, req, &permission, partialResultsEvaluators) + ReverseProxy(logger, env, w, req, &permission, evaluatorSdk) } func rbacHandler(w http.ResponseWriter, req *http.Request) { @@ -102,7 +99,6 @@ func EvaluateRequest( logger := glogger.Get(requestContext) permission := evaluatorSdk.Config() - partialResultsEvaluators := evaluatorSdk.PartialResultsEvaluators() userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, types.UserHeadersKeys{ IDHeaderKey: env.UserIdHeader, @@ -115,43 +111,8 @@ func EvaluateRequest( return err } - pathParams := mux.Vars(req) - input, err := core.InputFromRequest(req, userInfo, env.ClientTypeHeader, pathParams, nil) - if err != nil { - return err - } - - regoInput, err := core.CreateRegoQueryInput(logger, input, core.RegoInputOptions{ - EnableResourcePermissionsMapOptimization: permission.Options.EnableResourcePermissionsMapOptimization, - }) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed rego query input creation") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "RBAC input creation failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return err - } - - evaluatorOptions := &core.EvaluatorOptions{ - EnablePrintStatements: env.IsTraceLogLevel(), - } - - var evaluatorAllowPolicy *core.OPAEvaluator - if !permission.RequestFlow.GenerateQuery { - evaluatorAllowPolicy, err = partialResultsEvaluators.GetEvaluatorFromPolicy(requestContext, permission.RequestFlow.PolicyName, regoInput, evaluatorOptions) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot find policy evaluator") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed partial evaluator retrieval", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return err - } - } else { - evaluatorAllowPolicy, err = core.CreateQueryEvaluator(requestContext, logger, req, permission.RequestFlow.PolicyName, regoInput, nil, evaluatorOptions) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("cannot create evaluator") - utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluator creation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) - return err - } - } - - _, query, err := evaluatorAllowPolicy.PolicyEvaluation(logger, &permission) + rondInput := core.NewRondInput(req, env.ClientTypeHeader, mux.Vars(req)) + result, err := evaluatorSdk.EvaluateRequestPolicy(req.Context(), rondInput, userInfo) if err != nil { if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) @@ -164,28 +125,18 @@ func EvaluateRequest( } logger.WithField("error", logrus.Fields{ - "policyName": permission.RequestFlow.PolicyName, - "message": err.Error(), + "message": err.Error(), }).Error("RBAC policy evaluation failed") utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) return err } - var queryToProxy = []byte{} - if query != nil { - queryToProxy, err = json.Marshal(query) - if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("Error while marshaling row filter query") - utils.FailResponseWithCode(w, http.StatusForbidden, "Error while marshaling row filter query", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return err - } - } queryHeaderKey := BASE_ROW_FILTER_HEADER_KEY if permission.RequestFlow.QueryOptions.HeaderName != "" { queryHeaderKey = permission.RequestFlow.QueryOptions.HeaderName } - if query != nil { - req.Header.Set(queryHeaderKey, string(queryToProxy)) + if result.QueryToProxy != nil { + req.Header.Set(queryHeaderKey, string(result.QueryToProxy)) } return nil } @@ -196,7 +147,7 @@ func ReverseProxy( w http.ResponseWriter, req *http.Request, permission *openapi.RondConfig, - partialResultsEvaluators core.PartialResultsEvaluators, + evaluatorSdk core.SDKEvaluator, ) { targetHostFromEnv := env.TargetServiceHost proxy := httputil.ReverseProxy{ @@ -211,10 +162,6 @@ func ReverseProxy( }, } - options := &core.EvaluatorOptions{ - EnablePrintStatements: env.IsTraceLogLevel(), - } - // Check on nil is performed to proxy the oas documentation path if permission == nil || permission.ResponseFlow.PolicyName == "" { proxy.ServeHTTP(w, req) @@ -225,8 +172,6 @@ func ReverseProxy( req.Context(), logger, req, - permission, - partialResultsEvaluators, env.ClientTypeHeader, types.UserHeadersKeys{ @@ -234,7 +179,7 @@ func ReverseProxy( GroupsHeaderKey: env.UserGroupsHeader, PropertiesHeaderKey: env.UserPropertiesHeader, }, - options, + evaluatorSdk, ) proxy.ServeHTTP(w, req) } diff --git a/service/handler_test.go b/service/handler_test.go index 57ec82a6..63fa2dc6 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -27,16 +27,8 @@ import ( "strings" "testing" - "github.com/gorilla/mux" - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/testutil" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/fake" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" @@ -44,7 +36,10 @@ import ( "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" + "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -107,12 +102,11 @@ func TestDirectProxyHandler(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, nil, ) @@ -140,12 +134,11 @@ func TestDirectProxyHandler(t *testing.T) { defer server.Close() serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, nil, ) @@ -178,12 +171,11 @@ func TestDirectProxyHandler(t *testing.T) { body := strings.NewReader(mockBodySting) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, nil, ) @@ -226,13 +218,12 @@ func TestDirectProxyHandler(t *testing.T) { serverURL, _ := url.Parse(server.URL) rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, rondConfig, oas) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, nil, ) @@ -296,12 +287,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -351,12 +341,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -419,12 +408,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -476,13 +464,12 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -533,14 +520,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -583,12 +569,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -629,12 +614,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -696,12 +680,11 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) @@ -733,19 +716,24 @@ allow { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + log, hook := test.NewNullLogger() + log.Level = logrus.TraceLevel + logger := logrus.NewEntry(log) + ctx = glogger.WithLogger(ctx, logger) + + registry := prometheus.NewRegistry() + + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", &evaluatorParams{ + logger: logger, + registry: registry, + }) ctx := createContext(t, ctx, config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, nil, ) - log, hook := test.NewNullLogger() - log.Level = logrus.TraceLevel - ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) - r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) require.NoError(t, err, "Unexpected error") @@ -763,23 +751,19 @@ allow { delete(actualLog[0].Data, "evaluationTimeMicroseconds") require.Equal(t, logrus.Fields{ "allowed": true, - "matchedPath": "/matched/path", + "matchedPath": "/api", "method": "GET", "partialEval": false, "policyName": "todo", - "requestedPath": "/requested/path", + "requestedPath": "/api", + "resultsLength": 1, }, actualLog[0].Data) }) t.Run("metrics", func(t *testing.T) { - m, err := metrics.GetFromContext(ctx) - require.NoError(t, err) - registry := prometheus.NewPedanticRegistry() - m.MustRegister(registry) - - problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") + problem, err := testutil.CollectAndLint(registry, "rond_policy_evaluation_duration_milliseconds") require.NoError(t, err, problem) - require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") + require.Equal(t, 1, testutil.CollectAndCount(registry, "rond_policy_evaluation_duration_milliseconds"), "register") }) }) @@ -807,19 +791,24 @@ allow { }`, } - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + log, hook := test.NewNullLogger() + log.Level = logrus.TraceLevel + logger := logrus.NewEntry(log) + ctx = glogger.WithLogger(ctx, logger) + + registry := prometheus.NewRegistry() + + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", &evaluatorParams{ + logger: logger, + registry: registry, + }) ctx := createContext(t, ctx, config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - OPAModuleConfig, nil, ) - log, hook := test.NewNullLogger() - log.Level = logrus.TraceLevel - ctx = glogger.WithLogger(ctx, logrus.NewEntry(log)) - r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) require.NoError(t, err, "Unexpected error") @@ -837,23 +826,18 @@ allow { delete(actualLog[0].Data, "evaluationTimeMicroseconds") require.Equal(t, logrus.Fields{ "allowed": true, - "matchedPath": "/matched/path", + "matchedPath": "/api", "method": "GET", "partialEval": true, "policyName": "allow", - "requestedPath": "/requested/path", + "requestedPath": "/api", }, actualLog[0].Data) }) t.Run("metrics", func(t *testing.T) { - m, err := metrics.GetFromContext(ctx) - require.NoError(t, err) - registry := prometheus.NewPedanticRegistry() - m.MustRegister(registry) - - problem, err := testutil.CollectAndLint(registry, "test_rond_policy_evaluation_duration_milliseconds") + problem, err := testutil.CollectAndLint(registry, "rond_policy_evaluation_duration_milliseconds") require.NoError(t, err, problem) - require.Equal(t, 1, testutil.CollectAndCount(registry, "test_rond_policy_evaluation_duration_milliseconds"), "register") + require.Equal(t, 1, testutil.CollectAndCount(registry, "rond_policy_evaluation_duration_milliseconds"), "register") }) }) }) @@ -895,12 +879,11 @@ func TestStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("ok", func(t *testing.T) { - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - mockOPAModule, nil, ) @@ -943,12 +926,11 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - opaModuleConfig, nil, ) @@ -993,12 +975,11 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - opaModuleConfig, nil, ) @@ -1043,12 +1024,11 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - opaModuleConfig, nil, ) @@ -1093,12 +1073,11 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - opaModuleConfig, nil, ) @@ -1145,12 +1124,11 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, - opaModuleConfig, nil, ) @@ -1209,8 +1187,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) + ctx := context.Background() // TODO: this tests verifies policy execution based on request header evaluation, it is // useful as a documentation because right now headers are provided as-is from the @@ -1239,12 +1216,11 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, opaModule, nil, rondConfig, oas) + evaluator := getEvaluator(t, ctx, opaModule, nil, rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - opaModule, nil, ) @@ -1280,12 +1256,11 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { get_header("x-backdoor", input.request.headers) == "mocked value" }`, } - evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - opaModule, nil, ) @@ -1339,7 +1314,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }`, mockedUserProperties["my"], mockedClientType), } - evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1349,7 +1324,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { ClientTypeHeader: clientTypeHeaderKey, }, evaluator, - opaModule, nil, ) @@ -1421,12 +1395,11 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } rondConfig := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, opaModule, nil, *rondConfig, oas) + evaluator := getEvaluator(t, ctx, opaModule, nil, *rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - opaModule, nil, ) @@ -1505,7 +1478,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("Something went wrong"), UserBindings: nil, UserRoles: nil, UserRolesError: errors.New("Something went wrong")} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1519,7 +1492,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1550,7 +1522,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("MongoDB Error"), UserRolesError: errors.New("MongoDB Error")} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1564,7 +1536,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1634,7 +1605,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1648,7 +1619,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1722,7 +1692,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1736,7 +1706,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1822,7 +1791,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1836,7 +1805,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1873,7 +1841,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: nil} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1887,7 +1855,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -1932,7 +1899,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1946,7 +1913,6 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - opaModule, mongoclientMock, ) @@ -2019,12 +1985,11 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, mongoclientMock, ) @@ -2062,12 +2027,11 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, mongoMock, ) @@ -2105,12 +2069,11 @@ project.tenantId == "1234" ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas) + evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mockOPAModule, mongoMock, ) @@ -2126,184 +2089,6 @@ project.tenantId == "1234" }) } -func BenchmarkEvaluateRequest(b *testing.B) { - moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") - require.NoError(b, err, "Unexpected error") - permission := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "allow_view_project"}} - - queryString := fmt.Sprintf("data.policies.%s", permission.RequestFlow.PolicyName) - query := rego.New( - rego.Query(queryString), - rego.Module(moduleConfig.Name, moduleConfig.Content), - rego.Unknowns(core.Unknowns), - rego.Capabilities(ast.CapabilitiesForThisVersion()), - custom_builtins.GetHeaderFunction, - custom_builtins.MongoFindOne, - custom_builtins.MongoFindMany, - ) - - pr, err := query.PartialResult(context.Background()) - if err != nil { - panic(err) - } - - partialEvaluators := core.PartialResultsEvaluators{ - permission.RequestFlow.PolicyName: core.PartialEvaluator{PartialEvaluator: &pr}, - } - - sdk := fake.NewSDKEvaluator( - partialEvaluators, - *permission, - nil, - ) - - envs := config.EnvironmentVariables{ - UserGroupsHeader: "miausergroups", - UserIdHeader: "miauserid", - } - - nilLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(nilLogger) - b.ResetTimer() - - for n := 0; n < b.N; n++ { - b.StopTimer() - originalRequest := httptest.NewRequest(http.MethodGet, "/projects/project123", nil) - req := originalRequest.WithContext( - glogger.WithLogger( - metrics.WithValue( - context.WithValue( - openapi.WithRouterInfo( - logger, - context.WithValue( - openapi.WithXPermission( - core.WithOPAModuleConfig(originalRequest.Context(), moduleConfig), - permission, - ), - types.MongoClientContextKey{}, testmongoMock, - ), - httptest.NewRequest(http.MethodGet, "/", nil), - ), - config.EnvKey{}, envs, - ), - metrics.SetupMetrics(""), - ), - logger, - ), - ) - req.Header.Set("miausergroups", "area_rocket") - req.Header.Set("miauserid", "user1") - req = mux.SetURLVars(req, map[string]string{ - "projectId": "project123", - }) - recorder := httptest.NewRecorder() - b.StartTimer() - EvaluateRequest(req, envs, recorder, sdk) - b.StopTimer() - require.Equal(b, http.StatusOK, recorder.Code) - } -} - -var testmongoMock = &mocks.MongoClientMock{ - UserBindings: []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"admin"}, - Groups: []string{"area_rocket"}, - Permissions: []string{"permission4"}, - Resource: &types.Resource{ - ResourceType: "project", - ResourceID: "project123", - }, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding3", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission10", "permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding4", - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission11"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFiltering", - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFilteringFromSubject", - Subjects: []string{"filter_test"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role6"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PRIVATE", - }, - }, - UserRoles: []types.Role{ - { - RoleID: "admin", - Permissions: []string{"console.project.view", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role6", - Permissions: []string{"permission3", "permission5"}, - CRUDDocumentState: "PRIVATE", - }, - { - RoleID: "notUsedByAnyone", - Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, - CRUDDocumentState: "PUBLIC", - }, - }, -} - func findLogWithMessage(logs []*logrus.Entry, message string) []*logrus.Entry { logToReturn := []*logrus.Entry{} for _, log := range logs { diff --git a/service/router.go b/service/router.go index dbfdd151..1318c7af 100644 --- a/service/router.go +++ b/service/router.go @@ -111,7 +111,6 @@ func SetupRouter( StatusRoutes(router, serviceName, env.ServiceVersion) metrics.MetricsRoute(router, registry) - router.Use(metrics.RequestMiddleware(sdk.Metrics())) router.Use(config.RequestMiddlewareEnvironments(env)) diff --git a/service/router_test.go b/service/router_test.go index 63fb501e..e0561d1e 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -24,10 +24,10 @@ import ( "testing" "github.com/mia-platform/glogger/v2" + "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/fake" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" @@ -232,7 +232,6 @@ func createContext( originalCtx context.Context, env config.EnvironmentVariables, evaluator core.SDKEvaluator, - opaModuleConfig *core.OPAModuleConfig, mongoClient *mocks.MongoClientMock, ) context.Context { t.Helper() @@ -240,16 +239,7 @@ func createContext( var partialContext context.Context partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) - partialContext = core.WithEvaluatorSKD(partialContext, evaluator) - - // TODO: remove me - partialContext = context.WithValue(partialContext, core.OPAModuleConfigKey{}, opaModuleConfig) - // TODO: remove me - partialContext = context.WithValue(partialContext, openapi.RouterInfoKey{}, openapi.RouterInfo{ - MatchedPath: "/matched/path", - RequestedPath: "/requested/path", - Method: "GET", - }) + partialContext = core.WithEvaluatorSDK(partialContext, evaluator) if mongoClient != nil { partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) @@ -257,9 +247,6 @@ func createContext( partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(logrus.New())) - // TODO: remove me - partialContext = metrics.WithValue(partialContext, metrics.SetupMetrics("test_rond")) - return partialContext } @@ -270,6 +257,11 @@ todo { true }`, } var mockXPermission = openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} +type evaluatorParams struct { + logger *logrus.Entry + registry *prometheus.Registry +} + func getEvaluator( t *testing.T, ctx context.Context, @@ -277,25 +269,38 @@ func getEvaluator( mongoClient *mocks.MongoClientMock, rondConfig openapi.RondConfig, oas *openapi.OpenAPISpec, + method, path string, + options *evaluatorParams, ) core.SDKEvaluator { t.Helper() - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + if options == nil { + options = &evaluatorParams{} + } + + logger := options.logger + if logger == nil { + log, _ := test.NewNullLogger() + logger = logrus.NewEntry(log) + } sdk, err := core.NewSDK( ctx, logger, - mongoClient, oas, opaModule, - &core.EvaluatorOptions{}, - nil, + &core.EvaluatorOptions{ + MongoClient: mongoClient, + }, + options.registry, "", ) require.NoError(t, err) - return sdk.EvaluatorFromConfig(logger, rondConfig) + evaluator, err := sdk.FindEvaluator(logger, method, path) + require.NoError(t, err) + + return evaluator } func TestSetupRoutesIntegration(t *testing.T) { @@ -321,13 +326,13 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/users/", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -358,13 +363,15 @@ func TestSetupRoutesIntegration(t *testing.T) { router := mux.NewRouter() setupRoutes(router, oas, envs) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + eval, err := core.SetupEvaluators(ctx, logger, oas, mockOPAModule, nil) + require.NoError(t, err) + evaluator := fake.NewSDKEvaluator(eval, mockXPermission, nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/unknown/path?foo=bar", nil) @@ -390,13 +397,13 @@ func TestSetupRoutesIntegration(t *testing.T) { router := mux.NewRouter() setupRoutes(router, oas, envs) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/users/", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -424,7 +431,7 @@ func TestSetupRoutesIntegration(t *testing.T) { context.Background(), config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/users/?foo=bar", nil) @@ -437,7 +444,7 @@ func TestSetupRoutesIntegration(t *testing.T) { w := httptest.NewRecorder() matchedRouted.Handler.ServeHTTP(w, req) - require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode) + require.Equal(t, http.StatusForbidden, w.Result().StatusCode) }) t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { @@ -461,12 +468,12 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas, http.MethodGet, "/foo/route-not-registered-explicitly", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/foo/route-not-registered-explicitly", nil) @@ -489,7 +496,7 @@ func TestSetupRoutesIntegration(t *testing.T) { var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies - foo { true }`, + foo_bar_nested { true }`, } var invoked bool @@ -504,12 +511,12 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas, http.MethodGet, "/foo/bar/nested", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - nil, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/foo/bar/nested", nil) diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 1859230a..041d3ff5 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -37,7 +37,6 @@ func TestRevokeHandler(t *testing.T) { context.Background(), config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, - mockOPAModule, nil, ) @@ -513,7 +512,6 @@ func TestGrantHandler(t *testing.T) { context.Background(), config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, - mockOPAModule, nil, ) diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 75035772..d0f7501d 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -115,7 +115,9 @@ test_policy { true } var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), mongoClient, oas, opa, nil, registry, "") + sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ + MongoClient: mongoClient, + }, registry, "") require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { From d6af8954d8670394a949faaf3c62876c233a7f66 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Fri, 30 Jun 2023 09:40:31 +0200 Subject: [PATCH 108/172] fix(#210): allowed value log in response policy evaluation (#212) * fix(#210): allowed value simplification * refactor: use allowed-inspired func * fix: lax allowed evaluator for logging purposes * refactor: removed useless log * refactor: typo and better comment * refactor: use empty Co-authored-by: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> * refactor: removed lax allow and reworked results processing to fit allowed eval --------- Co-authored-by: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> --- core/opaevaluator.go | 56 +++++++++++++++++++++++++++----------------- core/sdk_test.go | 24 +++++++++++++++++-- go.mod | 2 +- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 8beffd58..d8b8d461 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -344,40 +344,26 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) + allowed, responseBodyOverwriter := processResults(results) logger.WithFields(logrus.Fields{ "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": false, - "allowed": results.Allowed(), + "allowed": allowed, "resultsLength": len(results), "matchedPath": evaluator.routerInfo.MatchedPath, "requestedPath": evaluator.routerInfo.RequestedPath, "method": evaluator.routerInfo.Method, }).Debug("policy evaluation completed") - if results.Allowed() { - logger.WithFields(logrus.Fields{ - "policyName": evaluator.PolicyName, - "allowed": results.Allowed(), - "resultsLength": len(results), - }).Tracef("policy results") - return nil, nil - } - // The results returned by OPA are a list of Results object with fields: - // - Expressions: list of list - // - Bindings: object - // e.g. [{Expressions:[[map["element": true]]] Bindings:map[]}] - // Since we are ALWAYS querying ONE specific policy the result length could not be greater than 1 - if len(results) == 1 { - if exprs := results[0].Expressions; len(exprs) == 1 { - if value, ok := exprs[0].Value.([]interface{}); ok && value != nil && len(value) != 0 { - return value[0], nil - } - } - } logger.WithFields(logrus.Fields{ "policyName": evaluator.PolicyName, - }).Error("policy resulted in not allowed") + "allowed": allowed, + }).Info("policy result") + + if allowed { + return responseBodyOverwriter, nil + } return nil, fmt.Errorf("RBAC policy evaluation failed, user is not allowed") } @@ -444,3 +430,29 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { Content: string(fileContent), }, nil } + +func processResults(results rego.ResultSet) (allowed bool, responseBodyOverwriter any) { + // Use strict allowed check for basic request flow allow policies. + if results.Allowed() { + allowed = true + return + } + + // Here extract first result set to get the response body for the response policy evaluation. + // The results returned by OPA are a list of Results object with fields: + // - Expressions: list of list + // - Bindings: object + // e.g. [{Expressions:[[map["element": true]]] Bindings:map[]}] + // Since we are ALWAYS querying ONE specific policy the result length could not be greater than 1 + if len(results) == 1 { + if exprs := results[0].Expressions; len(exprs) == 1 { + if value, ok := exprs[0].Value.([]interface{}); ok && value != nil && len(value) != 0 { + allowed = true + responseBodyOverwriter = value[0] + return + } + } + } + + return +} diff --git a/core/sdk_test.go b/core/sdk_test.go index 8d7cfcac..d88313fa 100644 --- a/core/sdk_test.go +++ b/core/sdk_test.go @@ -493,6 +493,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { expectedBody string expectedErr bool expectedErrMessage string + notAllowed bool } t.Run("evaluate response", func(t *testing.T) { @@ -527,6 +528,20 @@ func TestEvaluateResponsePolicy(t *testing.T) { expectedBody: `{"f1":"censored","foo":"bar"}`, }, + "with policy failure": { + method: http.MethodGet, + path: "/users/", + opaModuleContent: ` + package policies + responsepolicy [body] { + false + body := input.response.body + }`, + expectedErr: true, + expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", + expectedBody: "", + notAllowed: true, + }, "with mongo query and body changed": { method: http.MethodGet, path: "/users/", @@ -595,7 +610,12 @@ func TestEvaluateResponsePolicy(t *testing.T) { } else { require.NoError(t, err) } - require.JSONEq(t, testCase.expectedBody, string(actual)) + + if testCase.expectedBody == "" { + require.Empty(t, string(actual)) + } else { + require.JSONEq(t, testCase.expectedBody, string(actual)) + } t.Run("logger", func(t *testing.T) { var actual *logrus.Entry @@ -609,7 +629,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { require.NotNil(t, actual) delete(actual.Data, "evaluationTimeMicroseconds") require.Equal(t, logrus.Fields{ - "allowed": false, + "allowed": !testCase.notAllowed, "requestedPath": testCase.path, "matchedPath": evaluatorInfo.evaluatorOptions.RouterInfo.MatchedPath, "method": testCase.method, diff --git a/go.mod b/go.mod index df01a6e2..96681808 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.53.1 github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/common v0.42.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 @@ -57,7 +58,6 @@ require ( github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect From 48ec7ddb5d4e5f203fd5fc678675bfaa468616fc Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:13:26 +0200 Subject: [PATCH 109/172] refactor: reworked error messages (#214) * fix * refactor: core error messages * fix: missing saved file * fix: test * fix: test --- core/errors.go | 38 ++++++++++++++++++++++++++ core/input.go | 10 ++++--- core/opa_transport.go | 6 ++--- core/opaevaluator.go | 63 ++++++++++++++++++++++++++----------------- core/sdk_test.go | 37 +++++++++++-------------- 5 files changed, 101 insertions(+), 53 deletions(-) create mode 100644 core/errors.go diff --git a/core/errors.go b/core/errors.go new file mode 100644 index 00000000..cb2fa4fd --- /dev/null +++ b/core/errors.go @@ -0,0 +1,38 @@ +// Copyright 2023 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 core + +import "fmt" + +var ( + ErrMissingRegoModules = fmt.Errorf("no rego module found in directory") + ErrRegoModuleReadFailed = fmt.Errorf("failed rego file read") + + ErrEvaluatorCreationFailed = fmt.Errorf("error during evaluator creation") + ErrEvaluatorNotFound = fmt.Errorf("evaluator not found") + + ErrPolicyEvalFailed = fmt.Errorf("policy evaluation failed") + ErrPartialPolicyEvalFailed = fmt.Errorf("partial %w", ErrPolicyEvalFailed) + ErrResponsePolicyEvalFailed = fmt.Errorf("response %w", ErrPolicyEvalFailed) + + ErrFailedInputParse = fmt.Errorf("failed input parse") + ErrFailedInputEncode = fmt.Errorf("failed input encode") + ErrFailedInputRequestParse = fmt.Errorf("failed request body parse") + ErrFailedInputRequestDeserialization = fmt.Errorf("failed request body deserialization") + + ErrUnexepectedContentType = fmt.Errorf("unexpected content type") + + ErrOPATransportInvalidResponseBody = fmt.Errorf("response body is not valid") +) diff --git a/core/input.go b/core/input.go index 4a2b9318..0327da6e 100644 --- a/core/input.go +++ b/core/input.go @@ -106,9 +106,11 @@ func CreateRegoQueryInput( inputBytes, err := json.Marshal(input) if err != nil { - return nil, fmt.Errorf("failed input JSON encode: %v", err) + return nil, fmt.Errorf("%w: %v", ErrFailedInputEncode, err) } - logger.Tracef("OPA input rego creation in: %+v", time.Since(opaInputCreationTime)) + logger. + WithField("inputCreationTimeMicroseconds", time.Since(opaInputCreationTime).Microseconds()). + Tracef("input creation time") return inputBytes, nil } @@ -131,10 +133,10 @@ func (req requestInfo) Input(user types.User, responseBody any) (Input, error) { if shouldParseJSONBody { bodyBytes, err := io.ReadAll(req.Body) if err != nil { - return Input{}, fmt.Errorf("failed request body parse: %s", err.Error()) + return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestParse, err.Error()) } if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { - return Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error()) + return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestDeserialization, err.Error()) } req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) } diff --git a/core/opa_transport.go b/core/opa_transport.go index 0620aceb..be3cd27e 100644 --- a/core/opa_transport.go +++ b/core/opa_transport.go @@ -92,13 +92,13 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er if !utils.HasApplicationJSONContentType(resp.Header) { t.logger.WithField("foundContentType", resp.Header.Get(utils.ContentTypeHeaderKey)).Debug("found content type") - t.responseWithError(resp, fmt.Errorf("content-type is not application/json"), http.StatusInternalServerError) + t.responseWithError(resp, fmt.Errorf("%w: response content-type is not application/json", ErrUnexepectedContentType), http.StatusInternalServerError) return resp, nil } var decodedBody interface{} if err := json.Unmarshal(b, &decodedBody); err != nil { - return nil, fmt.Errorf("response body is not valid: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrOPATransportInvalidResponseBody, err.Error()) } userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(t.logger, t.request, t.userHeaders) @@ -121,7 +121,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er } func (t *OPATransport) responseWithError(resp *http.Response, err error, statusCode int) { - t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("error while evaluating column filter query") + t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error(ErrResponsePolicyEvalFailed) message := utils.NO_PERMISSIONS_ERROR_MESSAGE if statusCode != http.StatusForbidden { message = utils.GENERIC_BUSINESS_ERROR_MESSAGE diff --git a/core/opaevaluator.go b/core/opaevaluator.go index d8b8d461..a1ea5b6e 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -24,7 +24,7 @@ import ( "strings" "time" - "github.com/prometheus/client_golang/prometheus" + "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" @@ -32,11 +32,10 @@ import ( "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/rond-authz/rond/custom_builtins" - "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/topdown/print" + "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -65,23 +64,29 @@ type PartialEvaluator struct { } func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { - logger.Infof("precomputing rego query for allow policy: %s", policy) + logger.WithField("policyName", policy).Info("precomputing rego policy") policyEvaluatorTime := time.Now() partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, options) - if err == nil { - logger.Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime)) - return &PartialEvaluator{ - PartialEvaluator: partialResultEvaluator, - }, nil + if err != nil { + return nil, err } - return nil, err + + logger. + WithFields(logrus.Fields{ + "policyName": policy, + "computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds, + }). + Info("precomputation time") + + return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil } func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { if oas == nil { return nil, fmt.Errorf("oas must not be nil") } + policyEvaluators := PartialResultsEvaluators{} for path, OASContent := range oas.Paths { for verb, verbConfig := range OASContent { @@ -92,7 +97,15 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.Ope allowPolicy := verbConfig.PermissionV2.RequestFlow.PolicyName responsePolicy := verbConfig.PermissionV2.ResponseFlow.PolicyName - logger.Infof("precomputing rego queries for API: %s %s. Allow policy: %s. Response policy: %s.", verb, path, allowPolicy, responsePolicy) + logger. + WithFields(logrus.Fields{ + "verb": verb, + "policyName": allowPolicy, + "path": path, + "responsePolicyName": responsePolicy, + }). + Info("precomputing rego queries for API") + if allowPolicy == "" { // allow policy is required, if missing assume the API has no valid x-rond configuration. continue @@ -100,9 +113,8 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.Ope if _, ok := policyEvaluators[allowPolicy]; !ok { evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, oas, opaModuleConfig, options) - if err != nil { - return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) } policyEvaluators[allowPolicy] = *evaluator @@ -111,9 +123,8 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.Ope if responsePolicy != "" { if _, ok := policyEvaluators[responsePolicy]; !ok { evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, oas, opaModuleConfig, options) - if err != nil { - return nil, fmt.Errorf("error during evaluator creation: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) } policyEvaluators[responsePolicy] = *evaluator @@ -182,7 +193,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod } inputTerm, err := ast.ParseTerm(string(input)) if err != nil { - return nil, fmt.Errorf("failed input parse: %v", err) + return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err) } sanitizedPolicy := strings.Replace(policy, ".", "_", -1) @@ -221,10 +232,12 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger opaEvaluatorInstanceTime := time.Now() evaluator, err := NewOPAEvaluator(ctx, policy, config, input, options) if err != nil { - logger.WithError(err).Error("failed RBAC policy creation") + logger.WithError(err).Error(ErrEvaluatorCreationFailed) return nil, err } - logger.Tracef("OPA evaluator instantiated in: %+v", time.Since(opaEvaluatorInstanceTime)) + logger. + WithField("evaluatorCreationTimeMicroseconds", time.Since(opaEvaluatorInstanceTime).Microseconds()). + Trace("evaluator creation time") return evaluator, nil } @@ -266,7 +279,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con if eval, ok := partialEvaluators[policy]; ok { inputTerm, err := ast.ParseTerm(string(input)) if err != nil { - return nil, fmt.Errorf("failed input parse: %v", err) + return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err) } evaluator := eval.PartialEvaluator.Rego( @@ -284,7 +297,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con routerInfo: options.RouterInfo, }, nil } - return nil, fmt.Errorf("policy evaluator not found: %s", policy) + return nil, fmt.Errorf("%w: %s", ErrEvaluatorNotFound, policy) } func (evaluator *OPAEvaluator) metrics() metrics.Metrics { @@ -298,7 +311,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv opaEvaluationTimeStart := time.Now() partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.Context) if err != nil { - return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrPartialPolicyEvalFailed, err.Error()) } opaEvaluationTime := time.Since(opaEvaluationTimeStart) @@ -336,7 +349,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context) if err != nil { - return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrPolicyEvalFailed, err.Error()) } opaEvaluationTime := time.Since(opaEvaluationTimeStart) @@ -364,7 +377,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro if allowed { return responseBodyOverwriter, nil } - return nil, fmt.Errorf("RBAC policy evaluation failed, user is not allowed") + return nil, ErrPolicyEvalFailed } func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *openapi.RondConfig) (interface{}, primitive.M, error) { @@ -418,11 +431,11 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) { }) if regoModulePath == "" { - return nil, fmt.Errorf("no rego module found in directory") + return nil, ErrMissingRegoModules } fileContent, err := utils.ReadFile(regoModulePath) if err != nil { - return nil, fmt.Errorf("failed rego file read: %s", err.Error()) + return nil, fmt.Errorf("%w: %s", ErrRegoModuleReadFailed, err.Error()) } return &OPAModuleConfig{ diff --git a/core/sdk_test.go b/core/sdk_test.go index d88313fa..511f2b64 100644 --- a/core/sdk_test.go +++ b/core/sdk_test.go @@ -161,9 +161,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { reqHeaders map[string]string mongoClient types.IMongoClient - expectedPolicy PolicyResult - expectedErr bool - expectedErrMessage string + expectedPolicy PolicyResult + expectedErr error } t.Run("evaluate request", func(t *testing.T) { @@ -196,9 +195,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { UserID: "my-user", }, - expectedPolicy: PolicyResult{}, - expectedErr: true, - expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", + expectedPolicy: PolicyResult{}, + expectedErr: ErrPolicyEvalFailed, }, "not allowed policy result": { method: http.MethodGet, @@ -208,9 +206,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, opaModuleContent: `package policies todo { false }`, - expectedPolicy: PolicyResult{}, - expectedErr: true, - expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", + expectedPolicy: PolicyResult{}, + expectedErr: ErrPolicyEvalFailed, }, "with empty filter query": { method: http.MethodGet, @@ -396,8 +393,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { rondInput := NewRondInput(req, clientTypeHeaderKey, nil) actual, err := evaluate.EvaluateRequestPolicy(context.Background(), rondInput, testCase.user) - if testCase.expectedErr { - require.EqualError(t, err, testCase.expectedErrMessage) + if testCase.expectedErr != nil { + require.EqualError(t, err, testCase.expectedErr.Error()) } else { require.NoError(t, err) } @@ -490,10 +487,9 @@ func TestEvaluateResponsePolicy(t *testing.T) { decodedBody any - expectedBody string - expectedErr bool - expectedErrMessage string - notAllowed bool + expectedBody string + expectedErr error + notAllowed bool } t.Run("evaluate response", func(t *testing.T) { @@ -537,10 +533,9 @@ func TestEvaluateResponsePolicy(t *testing.T) { false body := input.response.body }`, - expectedErr: true, - expectedErrMessage: "RBAC policy evaluation failed, user is not allowed", - expectedBody: "", - notAllowed: true, + expectedErr: ErrPolicyEvalFailed, + expectedBody: "", + notAllowed: true, }, "with mongo query and body changed": { method: http.MethodGet, @@ -605,8 +600,8 @@ func TestEvaluateResponsePolicy(t *testing.T) { rondInput := NewRondInput(req, clientTypeHeaderKey, nil) actual, err := evaluate.EvaluateResponsePolicy(context.Background(), rondInput, testCase.user, testCase.decodedBody) - if testCase.expectedErr { - require.EqualError(t, err, testCase.expectedErrMessage) + if testCase.expectedErr != nil { + require.EqualError(t, err, testCase.expectedErr.Error()) } else { require.NoError(t, err) } From be54b495dacb3a040014811d148afdaf8d2c77a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:14:08 +0200 Subject: [PATCH 110/172] chore(deps): bump github.com/prometheus/common from 0.42.0 to 0.44.0 (#215) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.42.0 to 0.44.0. - [Release notes](https://github.com/prometheus/common/releases) - [Commits](https://github.com/prometheus/common/compare/v0.42.0...v0.44.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 96681808..dcb95176 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/mia-platform/glogger/v2 v2.1.3 github.com/open-policy-agent/opa v0.53.1 github.com/prometheus/client_golang v1.16.0 - github.com/prometheus/common v0.42.0 + github.com/prometheus/common v0.44.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 @@ -57,7 +57,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect diff --git a/go.sum b/go.sum index 61d7ebc7..b36234ea 100644 --- a/go.sum +++ b/go.sum @@ -418,15 +418,15 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -441,7 +441,7 @@ github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8d github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= From 79fe7549010dbaad08e4b079fbe60149bb1f8b3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:19:10 +0200 Subject: [PATCH 111/172] chore(deps): bump github.com/open-policy-agent/opa from 0.53.1 to 0.54.0 (#216) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.53.1 to 0.54.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.53.1...v0.54.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index dcb95176..e4161f63 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 - github.com/open-policy-agent/opa v0.53.1 + github.com/open-policy-agent/opa v0.54.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.44.0 github.com/samber/lo v1.38.1 @@ -73,16 +73,16 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/yashtewari/glob-intersection v0.1.0 // indirect + github.com/yashtewari/glob-intersection v0.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/sdk v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect - golang.org/x/crypto v0.7.0 // indirect + golang.org/x/crypto v0.10.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index b36234ea..16b28369 100644 --- a/go.sum +++ b/go.sum @@ -384,8 +384,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.53.1 h1:APN8iA7Txgel13kSkc6S8dbUulydiPojXt6iyubmB7Q= -github.com/open-policy-agent/opa v0.53.1/go.mod h1:j3wl8FqSz/+u33Scl72Ms2wxkZx4yZPdqSCrOqBqdsA= +github.com/open-policy-agent/opa v0.54.0 h1:mGEsK+R5ZTMV8fzzbNzmYDGbTmY30wmRCIHmtm2VqWs= +github.com/open-policy-agent/opa v0.54.0/go.mod h1:d8I8jWygKGi4+T4H07qrbeCdH1ITLsEfT0M+bsvxWw0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -514,8 +514,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= -github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= @@ -568,8 +568,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= 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= @@ -648,7 +648,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= 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= @@ -736,8 +736,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -752,8 +752,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -879,7 +879,7 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -901,7 +901,7 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= 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= From 5448ebc194cb08bdde3819731000c96d9413d553 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:33:59 +0200 Subject: [PATCH 112/172] fix: patch bulk during revoke bindings API invocation and refactor to use crud client lib (#217) * feat: use crud client lib * feat: remove mongoclient * update: remove unused function and move helpers to internal --- go.mod | 3 +- go.sum | 3 + helpers/headers_to_proxy.go | 55 --- helpers/headers_to_proxy_test.go | 137 ------- internal/crudclient/crudclient.go | 122 ------ internal/crudclient/crudclient_test.go | 368 ------------------ internal/crudclient/testing.go | 121 ------ .../helpers}/gracefulshutdown.go | 0 .../helpers}/gracefulshutdown_test.go | 0 internal/helpers/headers_to_proxy.go | 30 ++ internal/helpers/headers_to_proxy_test.go | 52 +++ main.go | 2 +- service/router.go | 3 - service/standalone_apis.go | 77 ++-- service/standalone_apis_test.go | 140 +++---- types/rbactypes.go | 4 - 16 files changed, 203 insertions(+), 914 deletions(-) delete mode 100644 helpers/headers_to_proxy.go delete mode 100644 helpers/headers_to_proxy_test.go delete mode 100644 internal/crudclient/crudclient.go delete mode 100644 internal/crudclient/crudclient_test.go delete mode 100644 internal/crudclient/testing.go rename {helpers => internal/helpers}/gracefulshutdown.go (100%) rename {helpers => internal/helpers}/gracefulshutdown_test.go (100%) create mode 100644 internal/helpers/headers_to_proxy.go create mode 100644 internal/helpers/headers_to_proxy_test.go diff --git a/go.mod b/go.mod index e4161f63..9b5e069b 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ 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 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v2 v2.1.3 + github.com/mia-platform/go-crud-service-client v0.10.0 github.com/open-policy-agent/opa v0.54.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.44.0 @@ -27,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/go.sum b/go.sum index 16b28369..da69e2a2 100644 --- a/go.sum +++ b/go.sum @@ -245,6 +245,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= @@ -347,6 +348,8 @@ github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjK github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= github.com/mia-platform/glogger/v2 v2.1.3 h1:Qt/qHETYaFa+Oso9XZauysnHF9CE5816AQJmMo2Uh0Q= github.com/mia-platform/glogger/v2 v2.1.3/go.mod h1:dUYhwmsXVcZe5Eg8V4FIcdv5P4pfk9a/8JExTmi8Uig= +github.com/mia-platform/go-crud-service-client v0.10.0 h1:BUFKQVxfdcnI82cXNIU8Zp2mCI9d7JJS4eUrTUHfxHc= +github.com/mia-platform/go-crud-service-client v0.10.0/go.mod h1:Sj3BEGMu0RTdeye/fPBVwRoojOjKuhWl5j+psY3s40s= github.com/mia-platform/jsonschema v0.1.0 h1:tjQf7TaYROsAqk7SXTL+44TrfKk3bSEvhRGPS51IA5Y= github.com/mia-platform/jsonschema v0.1.0/go.mod h1:r2DJjPA/+6S+WPnXZt1xONMvO2b4hlhfXfUYV0po/Dk= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= diff --git a/helpers/headers_to_proxy.go b/helpers/headers_to_proxy.go deleted file mode 100644 index 46d87048..00000000 --- a/helpers/headers_to_proxy.go +++ /dev/null @@ -1,55 +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 helpers - -import ( - "context" - "net/http" - - "github.com/gorilla/mux" - "github.com/sirupsen/logrus" -) - -type requestHeadersToProxy struct{} - -func SetHeadersToProxy(ctx context.Context, headers http.Header) { - reqHeadersToProxy, ok := ctx.Value(requestHeadersToProxy{}).(http.Header) - if ok && len(reqHeadersToProxy) != 0 { - for name := range reqHeadersToProxy { - headers.Set(name, reqHeadersToProxy.Get(name)) - } - } -} - -func AddHeadersToProxyMiddleware(logger *logrus.Logger, headerNamesToAdd []string) mux.MiddlewareFunc { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - headersToProxy := http.Header{} - for _, headerNameToAdd := range headerNamesToAdd { - headerValue := r.Header.Get(headerNameToAdd) - if len(headerValue) > 0 { - headersToProxy.Set(headerNameToAdd, headerValue) - } - } - ctx := AddHeadersToProxyToContext(r.Context(), headersToProxy) - - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} - -func AddHeadersToProxyToContext(ctx context.Context, value http.Header) context.Context { - return context.WithValue(ctx, requestHeadersToProxy{}, value) -} diff --git a/helpers/headers_to_proxy_test.go b/helpers/headers_to_proxy_test.go deleted file mode 100644 index 65a5a99d..00000000 --- a/helpers/headers_to_proxy_test.go +++ /dev/null @@ -1,137 +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 helpers - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" - - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/require" -) - -func TestAddHeadersToProxyToContext(t *testing.T) { - t.Run("correctly set nil headers in context", func(t *testing.T) { - ctx := context.Background() - ctx = AddHeadersToProxyToContext(ctx, nil) - - requestHeadersToProxy, ok := ctx.Value(requestHeadersToProxy{}).(http.Header) - require.True(t, ok) - require.Len(t, requestHeadersToProxy, 0) - }) - - t.Run("correctly set headers in context", func(t *testing.T) { - ctx := context.Background() - h := http.Header{} - h.Set("foo", "bar") - ctx = AddHeadersToProxyToContext(ctx, h) - - requestHeadersToProxy, ok := ctx.Value(requestHeadersToProxy{}).(http.Header) - require.True(t, ok) - require.Len(t, requestHeadersToProxy, 1) - require.Equal(t, requestHeadersToProxy.Get("foo"), "bar") - }) -} - -func TestAddHeadersToProxyMiddleware(t *testing.T) { - var called = false - t.Run("set empty headers if headers to proxy is nil", func(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - called = true - }) - testMockMiddlewareInvocation(handler, nil) - - require.True(t, called) - }) - - t.Run("set empty headers if headers to proxy is empty", func(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - called = true - }) - testMockMiddlewareInvocation(handler, []string{}) - - require.True(t, called) - }) - - t.Run("set headers proxy in context", func(t *testing.T) { - requestHeaders := http.Header{} - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - called = true - requestHeaders = r.Context().Value(requestHeadersToProxy{}).(http.Header) - }) - h := testMockMiddlewareInvocation(handler, []string{"foo", "x-request-id", "x-forwarded-for", "x-forwarded-host"}) - - require.True(t, called) - require.Equal(t, h, requestHeaders) - }) -} - -func TestSetHeadersToProxy(t *testing.T) { - t.Run("not panic if context not contains headersToProxy key", func(t *testing.T) { - ctx := context.Background() - headers := http.Header{} - SetHeadersToProxy(ctx, headers) - - require.Len(t, headers, 0) - }) - - t.Run("not set header if empty headers to proxy", func(t *testing.T) { - ctx := context.Background() - requestHeaders := http.Header{} - ctx = AddHeadersToProxyToContext(ctx, requestHeaders) - - headers := http.Header{} - SetHeadersToProxy(ctx, headers) - - require.Len(t, headers, 0) - }) - - t.Run("set headers correctly", func(t *testing.T) { - ctx := context.Background() - requestHeaders := http.Header{} - requestHeaders.Set("foo", "bar") - requestHeaders.Set("taz", "ok") - ctx = AddHeadersToProxyToContext(ctx, requestHeaders) - - headers := http.Header{} - SetHeadersToProxy(ctx, headers) - - require.Len(t, headers, 2) - require.Equal(t, headers.Get("foo"), "bar") - require.Equal(t, headers.Get("taz"), "ok") - }) -} - -func testMockMiddlewareInvocation(next http.HandlerFunc, headersToAdd []string) http.Header { - // create a request - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Add("x-request-id", "123") - req.Header.Add("x-forwarded-for", "my-ip") - req.Header.Add("x-forwarded-host", "my-host") - - logger, _ := test.NewNullLogger() - - handler := AddHeadersToProxyMiddleware(logger, headersToAdd) - // invoke the handler - server := handler(next) - // Create a response writer - writer := httptest.NewRecorder() - // Serve HTTP server - server.ServeHTTP(writer, req) - - return req.Header -} 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/helpers/gracefulshutdown.go b/internal/helpers/gracefulshutdown.go similarity index 100% rename from helpers/gracefulshutdown.go rename to internal/helpers/gracefulshutdown.go diff --git a/helpers/gracefulshutdown_test.go b/internal/helpers/gracefulshutdown_test.go similarity index 100% rename from helpers/gracefulshutdown_test.go rename to internal/helpers/gracefulshutdown_test.go diff --git a/internal/helpers/headers_to_proxy.go b/internal/helpers/headers_to_proxy.go new file mode 100644 index 00000000..03ed3169 --- /dev/null +++ b/internal/helpers/headers_to_proxy.go @@ -0,0 +1,30 @@ +// 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 helpers + +import ( + "net/http" +) + +func GetHeadersToProxy(r *http.Request, headerNamesToAdd []string) http.Header { + headersToProxy := http.Header{} + for _, headerNameToAdd := range headerNamesToAdd { + headerValue := r.Header.Get(headerNameToAdd) + if len(headerValue) > 0 { + headersToProxy.Set(headerNameToAdd, headerValue) + } + } + return headersToProxy +} diff --git a/internal/helpers/headers_to_proxy_test.go b/internal/helpers/headers_to_proxy_test.go new file mode 100644 index 00000000..971f845b --- /dev/null +++ b/internal/helpers/headers_to_proxy_test.go @@ -0,0 +1,52 @@ +// 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 helpers + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetHeadersToProxy(t *testing.T) { + t.Run("not set header if empty headers to proxy", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + actual := GetHeadersToProxy(req, nil) + + expected := http.Header{} + + require.Equal(t, actual, expected) + }) + + t.Run("get headers to proxy correctly", func(t *testing.T) { + requestHeaders := http.Header{} + requestHeaders.Set("foo", "bar") + requestHeaders.Set("taz", "ok") + requestHeaders.Set("proxy", "me") + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header = requestHeaders + + actual := GetHeadersToProxy(req, []string{"foo", "proxy"}) + + expected := http.Header{} + expected.Set("foo", "bar") + expected.Set("proxy", "me") + + require.Equal(t, actual, expected) + }) +} diff --git a/main.go b/main.go index 22ace354..870908fe 100644 --- a/main.go +++ b/main.go @@ -25,8 +25,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" + "github.com/rond-authz/rond/internal/helpers" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/service" diff --git a/service/router.go b/service/router.go index 1318c7af..39e3b5d4 100644 --- a/service/router.go +++ b/service/router.go @@ -27,7 +27,6 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" @@ -116,8 +115,6 @@ func SetupRouter( evalRouter := router.NewRoute().Subrouter() if env.Standalone { - router.Use(helpers.AddHeadersToProxyMiddleware(log, env.GetAdditionalHeadersToProxy())) - swaggerRouter, err := swagger.NewRouter(gorilla.NewRouter(router), swagger.Options{ Context: context.Background(), Openapi: &openapi3.T{ diff --git a/service/standalone_apis.go b/service/standalone_apis.go index 3e096e2e..73792b6c 100644 --- a/service/standalone_apis.go +++ b/service/standalone_apis.go @@ -20,13 +20,14 @@ import ( "net/http" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/crudclient" + "github.com/rond-authz/rond/internal/helpers" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" + "github.com/mia-platform/go-crud-service-client" "github.com/sirupsen/logrus" ) @@ -67,23 +68,24 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { return } - bindings := make([]types.Binding, 0) - - 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) return } - query, err := buildQuery(resourceType, reqBody.ResourceIDs, reqBody.Subjects, reqBody.Groups) + query := buildQuery(resourceType, reqBody.ResourceIDs, reqBody.Subjects, reqBody.Groups) + bindings, err := client.List(r.Context(), crud.Options{ + Filter: crud.Filter{ + MongoQuery: query, + Limit: BINDINGS_MAX_PAGE_SIZE, + }, + }) if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed find query crud setup") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed find query crud setup", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return - } - - if err := client.Get(r.Context(), fmt.Sprintf("_q=%s&_l=%d", string(query), BINDINGS_MAX_PAGE_SIZE), &bindings); err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for finding bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return @@ -95,7 +97,7 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { var patchCrudResponse int if len(bindingsToDelete) > 0 { - query, err := buildQueryForBindingsToDelete(bindingsToDelete) + query := buildQueryForBindingsToDelete(bindingsToDelete) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed delete query crud setup") utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed delete query crud setup", utils.GENERIC_BUSINESS_ERROR_MESSAGE) @@ -107,7 +109,8 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { "bindingsToDelete": len(bindingsToDelete), }).Debug("generated query for bindings to delete") - if err := client.Delete(r.Context(), fmt.Sprintf("_q=%s", string(query)), &deleteCrudResponse); err != nil { + deleteCrudResponse, err = client.DeleteMany(r.Context(), crud.Options{Filter: crud.Filter{MongoQuery: query}}) + if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for deleting unused bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return @@ -118,7 +121,8 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { if len(bindingsToPatch) > 0 { body := buildRequestBodyForBindingsToPatch(bindingsToPatch) - if err := client.PatchBulk(r.Context(), body, &patchCrudResponse); err != nil { + patchCrudResponse, err = client.PatchBulk(r.Context(), body, crud.Options{}) + if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") utils.FailResponseWithCode( w, @@ -186,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) @@ -208,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), @@ -238,7 +245,7 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { } } -func buildQuery(resourceType string, resourceIDs []string, subjects []string, groups []string) ([]byte, error) { +func buildQuery(resourceType string, resourceIDs []string, subjects []string, groups []string) map[string]interface{} { queryPartForSubjectOrGroups := map[string]interface{}{ "$or": []map[string]interface{}{}, } @@ -255,7 +262,7 @@ func buildQuery(resourceType string, resourceIDs []string, subjects []string, gr } if resourceType == "" { - return json.Marshal(queryPartForSubjectOrGroups) + return queryPartForSubjectOrGroups } query := map[string]interface{}{ @@ -268,10 +275,10 @@ func buildQuery(resourceType string, resourceIDs []string, subjects []string, gr }, } - return json.Marshal(query) + return query } -func buildQueryForBindingsToDelete(bindingsToDelete []types.Binding) ([]byte, error) { +func buildQueryForBindingsToDelete(bindingsToDelete []types.Binding) map[string]interface{} { bindingsIds := make([]string, len(bindingsToDelete)) for i := 0; i < len(bindingsToDelete); i++ { bindingsIds[i] = bindingsToDelete[i].BindingID @@ -282,25 +289,21 @@ func buildQueryForBindingsToDelete(bindingsToDelete []types.Binding) ([]byte, er "$in": bindingsIds, }, } - return json.Marshal(query) -} - -type UpdateCommand struct { - SetCommand types.BindingUpdate `json:"$set"` -} -type PatchItem struct { - Filter types.BindingFilter `json:"filter"` - Update UpdateCommand `json:"update"` + return query } -func buildRequestBodyForBindingsToPatch(bindingsToPatch []types.Binding) []PatchItem { - patches := make([]PatchItem, len(bindingsToPatch)) +func buildRequestBodyForBindingsToPatch(bindingsToPatch []types.Binding) crud.PatchBulkBody { + patches := make(crud.PatchBulkBody, len(bindingsToPatch)) for i := 0; i < len(bindingsToPatch); i++ { currentBinding := bindingsToPatch[i] - patches[i] = PatchItem{ - Filter: types.BindingFilter{BindingID: currentBinding.BindingID}, - Update: UpdateCommand{ - SetCommand: types.BindingUpdate{ + patches[i] = crud.PatchBulkItem{ + Filter: crud.PatchBulkFilter{ + Fields: map[string]string{ + "bindingId": currentBinding.BindingID, + }, + }, + Update: crud.PatchBody{ + Set: types.BindingUpdate{ Subjects: currentBinding.Subjects, Groups: currentBinding.Groups, }, diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 041d3ff5..5956b5d7 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" @@ -26,8 +27,10 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" + "github.com/mia-platform/go-crud-service-client" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" ) @@ -82,8 +85,7 @@ func TestRevokeHandler(t *testing.T) { responseBody := map[string]interface{}{"answer": float64(42)} gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). Reply(http.StatusBadRequest). JSON(responseBody) @@ -109,13 +111,11 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Delete("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). Reply(http.StatusInternalServerError). JSON(map[string]interface{}{"statusCode": "500", "error": "InternalServerError", "message": "some message"}) @@ -136,7 +136,6 @@ func TestRevokeHandler(t *testing.T) { t.Run("does not invoke delete API if not necessary", func(t *testing.T) { defer gock.Flush() - bindingsFromCrud := []types.Binding{ { BindingID: "bindingToDelete", @@ -149,21 +148,18 @@ func TestRevokeHandler(t *testing.T) { } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") - match := mongoQueryString == `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"project"},{"$or":[{"subjects":{"$in":["piero"]}}]}]}` - limit := req.URL.Query().Get("_l") - matchLimit := limit == "200" + matchLimit := assert.Equal(t, "200", limit) + match := assert.Equal(t, `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"project"},{"$or":[{"subjects":{"$in":["piero"]}}]}]}`, mongoQueryString) return match && matchLimit, nil }). Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Patch("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPatch, "/bindings/bulk"). Reply(http.StatusOK) reqBody := setupRevokeRequestBody(t, RevokeRequestBody{ @@ -195,8 +191,7 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") match := mongoQueryString == `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"myResource"},{"$or":[{"subjects":{"$in":["piero"]}}]}]}` @@ -205,8 +200,7 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Delete("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { mongoQuery := req.URL.Query().Get("_q") match := mongoQuery == `{"bindingId":{"$in":["bindingToDelete"]}}` @@ -244,8 +238,8 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") match := mongoQueryString == `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"some-resource"},{"$or":[{"groups":{"$in":["litfiba"]}}]}]}` @@ -254,8 +248,7 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Delete("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { mongoQuery := req.URL.Query().Get("_q") match := mongoQuery == `{"bindingId":{"$in":["bindingToDelete"]}}` @@ -293,8 +286,7 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") match := mongoQueryString == `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"some-resource"},{"$or":[{"subjects":{"$in":["piero"]}}]}]}` @@ -303,20 +295,23 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Patch("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPatch, "/bindings/bulk"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { - var body []PatchItem + var body crud.PatchBulkBody err := json.NewDecoder(req.Body).Decode(&body) require.NoError(t, err, "unxpected error parsing body in matcher") - require.Equal(t, []PatchItem{ + require.Equal(t, crud.PatchBulkBody{ { - Filter: types.BindingFilter{BindingID: "litfiba"}, - Update: UpdateCommand{ - SetCommand: types.BindingUpdate{ - Subjects: []string{"ghigo"}, - Groups: []string{}, + Filter: crud.PatchBulkFilter{ + Fields: map[string]string{ + "bindingId": "litfiba", + }, + }, + Update: crud.PatchBody{ + Set: map[string]interface{}{ + "subjects": []any{"ghigo"}, + "groups": []any{}, }, }, }, @@ -365,8 +360,7 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") match := mongoQueryString == `{"$and":[{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"resource"},{"$or":[{"subjects":{"$in":["piero","liam","noel"]}},{"groups":{"$in":["brutte_band"]}}]}]}` @@ -375,8 +369,7 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Delete("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { mongoQuery := req.URL.Query().Get("_q") match := mongoQuery == `{"bindingId":{"$in":["oasis"]}}` @@ -385,21 +378,24 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). BodyString("1") - gock.New("http://crud-service"). - Patch("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPatch, "/bindings/bulk"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { - var body []PatchItem + var body crud.PatchBulkBody err := json.NewDecoder(req.Body).Decode(&body) require.NoError(t, err, "unxpected error parsing body in matcher") - require.Equal(t, []PatchItem{ + require.Equal(t, crud.PatchBulkBody{ { - Filter: types.BindingFilter{BindingID: "litfiba"}, - Update: UpdateCommand{ - SetCommand: types.BindingUpdate{ - Subjects: []string{"ghigo"}, - Groups: []string{}, + Filter: crud.PatchBulkFilter{ + Fields: map[string]string{ + "bindingId": "litfiba", + }, + }, + Update: crud.PatchBody{ + Set: map[string]any{ + "subjects": []any{"ghigo"}, + "groups": []any{}, }, }, }, @@ -445,8 +441,8 @@ func TestRevokeHandler(t *testing.T) { }, } gock.DisableNetworking() - gock.New("http://crud-service"). - Get("/bindings/"). + + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { mongoQueryString := req.URL.Query().Get("_q") match := mongoQueryString == `{"$or":[{"subjects":{"$in":["piero","liam","noel"]}},{"groups":{"$in":["brutte_band"]}}]}` @@ -455,8 +451,7 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). JSON(bindingsFromCrud) - gock.New("http://crud-service"). - Delete("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { mongoQuery := req.URL.Query().Get("_q") match := mongoQuery == `{"bindingId":{"$in":["oasis"]}}` @@ -465,21 +460,24 @@ func TestRevokeHandler(t *testing.T) { Reply(http.StatusOK). BodyString("1") - gock.New("http://crud-service"). - Patch("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPatch, "/bindings/bulk"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { - var body []PatchItem + var body crud.PatchBulkBody err := json.NewDecoder(req.Body).Decode(&body) require.NoError(t, err, "unxpected error parsing body in matcher") - require.Equal(t, []PatchItem{ + require.Equal(t, crud.PatchBulkBody{ { - Filter: types.BindingFilter{BindingID: "litfiba"}, - Update: UpdateCommand{ - SetCommand: types.BindingUpdate{ - Subjects: []string{"ghigo"}, - Groups: []string{}, + Filter: crud.PatchBulkFilter{ + Fields: map[string]string{ + "bindingId": "litfiba", + }, + }, + Update: crud.PatchBody{ + Set: map[string]any{ + "subjects": []any{"ghigo"}, + "groups": []any{}, }, }, }, @@ -559,8 +557,7 @@ func TestGrantHandler(t *testing.T) { }) gock.DisableNetworking() - gock.New("http://crud-service"). - Post("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPost, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { var body types.Binding bodyBytes, err := io.ReadAll(req.Body) @@ -616,8 +613,7 @@ func TestGrantHandler(t *testing.T) { }) gock.DisableNetworking() - gock.New("http://crud-service"). - Post("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPost, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { var body types.Binding bodyBytes, err := io.ReadAll(req.Body) @@ -670,8 +666,7 @@ func TestGrantHandler(t *testing.T) { }) gock.DisableNetworking() - gock.New("http://crud-service"). - Post("/bindings/"). + newGockScope(t, "http://crud-service", http.MethodPost, "/bindings/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { var body types.Binding err := json.NewDecoder(req.Body).Decode(&body) @@ -843,3 +838,18 @@ func requestWithParams( } return req } + +func newGockScope(t *testing.T, baseURL, method, path string) *gock.Request { + t.Helper() + + scope := gock.New(baseURL) + scope.Method = strings.ToUpper(method) + scope.Path(fmt.Sprintf("%s$", path)) + + t.Cleanup(func() { + require.True(t, gock.IsDone()) + gock.OffAll() + }) + + return scope +} diff --git a/types/rbactypes.go b/types/rbactypes.go index 6c790755..054a38d7 100644 --- a/types/rbactypes.go +++ b/types/rbactypes.go @@ -49,10 +49,6 @@ type Binding struct { Roles []string `bson:"roles" json:"roles,omitempty"` } -type BindingFilter struct { - BindingID string `bson:"bindingId" json:"bindingId"` -} - type BindingUpdate struct { Groups []string `bson:"groups" json:"groups"` Subjects []string `bson:"subjects" json:"subjects"` From 0482703813b33f3bafaa82fb22092cbe0b056180 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:33:23 +0000 Subject: [PATCH 113/172] chore(deps): bump golang from 1.19.5 to 1.20.6 Bumps golang from 1.19.5 to 1.20.6. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f675f21c..7976196e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.19.5 AS builder +FROM golang:1.20.6 AS builder WORKDIR /app From 53900d77c65366ead363828c9cf241b3d0a1bc4f Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:23:07 +0200 Subject: [PATCH 114/172] Create sdk package (#213) * feat: move core and mux specific function in specific package * rename * feat: create sdk package * move filest in service * remove unused params from sdk * add sdk readme * feat: change sdk interface * feat: change sdk interface with NewFromOAS * remove config structure * Update sdk/README.md Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * update PR * improve docs * test: fix * feat: sdk interface update name * fix: resolve issues with mongo client in context during evaluation in sdk --------- Co-authored-by: Federico Maggi Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- .github/workflows/test.yml | 16 +- Makefile | 5 + README.md | 8 + core/errors.go | 4 - core/input.go | 56 --- core/input_test.go | 81 ----- core/opaevaluator.go | 112 +++--- core/sdk.go | 238 ------------- internal/fake/sdk.go | 9 +- main.go | 13 +- main_test.go | 24 +- sdk/README.md | 4 + sdk/context.go | 39 +++ sdk/context_test.go | 55 +++ sdk/evaluator.go | 154 ++++++++ core/sdk_test.go => sdk/evaluator_test.go | 409 +++++++--------------- sdk/openapi.go | 57 +++ sdk/openapi_test.go | 91 +++++ sdk/rondinput/http/input.go | 80 +++++ sdk/rondinput/http/input_test.go | 104 ++++++ sdk/sdk.go | 87 +++++ sdk/sdk_test.go | 263 ++++++++++++++ service/handler.go | 15 +- {core => service}/opa_transport.go | 18 +- {core => service}/opa_transport_test.go | 58 ++- {core => service}/opamiddleware.go | 12 +- {core => service}/opamiddleware_test.go | 93 +++-- service/router.go | 5 +- service/router_test.go | 23 +- service/statusroutes_test.go | 13 +- 30 files changed, 1308 insertions(+), 838 deletions(-) delete mode 100644 core/sdk.go create mode 100644 sdk/README.md create mode 100644 sdk/context.go create mode 100644 sdk/context_test.go create mode 100644 sdk/evaluator.go rename core/sdk_test.go => sdk/evaluator_test.go (63%) create mode 100644 sdk/openapi.go create mode 100644 sdk/openapi_test.go create mode 100644 sdk/rondinput/http/input.go create mode 100644 sdk/rondinput/http/input_test.go create mode 100644 sdk/sdk.go create mode 100644 sdk/sdk_test.go rename {core => service}/opa_transport.go (89%) rename {core => service}/opa_transport_test.go (91%) rename {core => service}/opamiddleware.go (91%) rename {core => service}/opamiddleware_test.go (85%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef90f7eb..d77f88c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ on: push: jobs: tests: - name: Test with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + name: Test with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: @@ -22,14 +22,14 @@ jobs: - name: Go get dependencies run: go get -v -t -d ./... - name: Run tests - run: make test + run: make coverage - name: Send the coverage output uses: shogo82148/actions-goveralls@v1 with: path-to-profile: coverage.out bench: - name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: @@ -61,11 +61,11 @@ jobs: needs: tests runs-on: ubuntu-latest if: ${{ startsWith(github.ref, 'refs/tags/') || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - + steps: - name: Checkout code uses: actions/checkout@v3 - + - name: Configure docker metadata id: meta uses: docker/metadata-action@v4 @@ -81,7 +81,7 @@ jobs: labels: | org.opencontainers.image.documentation=https://rond-authz.io org.opencontainers.image.vendor=rond authz - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 @@ -97,7 +97,7 @@ jobs: with: username: ${{ secrets.BOT_DOCKER_USERNAME }} password: ${{ secrets.BOT_DOCKER_TOKEN }} - + - name: Prepare build cache uses: actions/cache@v3 with: @@ -105,7 +105,7 @@ jobs: key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- - + - name: Build and push uses: docker/build-push-action@v4 with: diff --git a/Makefile b/Makefile index 30249106..255fb49d 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,11 @@ mongo-start: .PHONY: test test: clean mongo-start + go test ./... -cover + $(MAKE) clean + +.PHONY: coverage +coverage: clean mongo-start go test ./... -coverprofile coverage.out $(MAKE) clean diff --git a/README.md b/README.md index 437bc033..4cba26a7 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,14 @@ make test Please note that in order to run tests you need Docker to be installed; tests need a local instance of MongoDB to be up and running, the `make test` command will take care of it by creating a new `mongodb` container. The container is auomatically removed at the end of tests; if it remains leaked simply run `make clean`. +#### With coverage + +To run test with coverage file in output, run + +```sh +make coverage +``` + ### Contributing Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for further details about the process for submitting pull requests. diff --git a/core/errors.go b/core/errors.go index cb2fa4fd..21c63c3e 100644 --- a/core/errors.go +++ b/core/errors.go @@ -31,8 +31,4 @@ var ( ErrFailedInputEncode = fmt.Errorf("failed input encode") ErrFailedInputRequestParse = fmt.Errorf("failed request body parse") ErrFailedInputRequestDeserialization = fmt.Errorf("failed request body deserialization") - - ErrUnexepectedContentType = fmt.Errorf("unexpected content type") - - ErrOPATransportInvalidResponseBody = fmt.Errorf("response body is not valid") ) diff --git a/core/input.go b/core/input.go index 0327da6e..660d4e80 100644 --- a/core/input.go +++ b/core/input.go @@ -15,15 +15,12 @@ package core import ( - "bytes" "encoding/json" "fmt" - "io" "net/http" "net/url" "time" - "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -117,56 +114,3 @@ func CreateRegoQueryInput( type RondInput interface { Input(user types.User, responseBody any) (Input, error) } - -type requestInfo struct { - *http.Request - clientTypeHeaderKey string - pathParams map[string]string -} - -func (req requestInfo) Input(user types.User, responseBody any) (Input, error) { - shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && - req.ContentLength > 0 && - (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) - - var requestBody any - if shouldParseJSONBody { - bodyBytes, err := io.ReadAll(req.Body) - if err != nil { - return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestParse, err.Error()) - } - if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { - return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestDeserialization, err.Error()) - } - req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - } - - return Input{ - ClientType: req.Header.Get(req.clientTypeHeaderKey), - Request: InputRequest{ - Method: req.Method, - Path: req.URL.Path, - Headers: req.Header, - Query: req.URL.Query(), - PathParams: req.pathParams, - Body: requestBody, - }, - Response: InputResponse{ - Body: responseBody, - }, - User: InputUser{ - Properties: user.Properties, - Groups: user.UserGroups, - Bindings: user.UserBindings, - Roles: user.UserRoles, - }, - }, nil -} - -func NewRondInput(req *http.Request, clientTypeHeaderKey string, pathParams map[string]string) RondInput { - return requestInfo{ - Request: req, - clientTypeHeaderKey: clientTypeHeaderKey, - pathParams: pathParams, - } -} diff --git a/core/input_test.go b/core/input_test.go index 5da56d4f..a20d6cbf 100644 --- a/core/input_test.go +++ b/core/input_test.go @@ -15,14 +15,9 @@ package core import ( - "bytes" - "encoding/json" "fmt" - "net/http" - "net/http/httptest" "testing" - "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -246,79 +241,3 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { b.StopTimer() } } - -func TestRondInput(t *testing.T) { - user := types.User{} - clientTypeHeaderKey := "clienttypeheader" - pathParams := map[string]string{} - - t.Run("request body integration", func(t *testing.T) { - expectedRequestBody := map[string]interface{}{ - "Key": float64(42), - } - reqBody := struct{ Key int }{ - Key: 42, - } - reqBodyBytes, err := json.Marshal(reqBody) - require.Nil(t, err, "Unexpected error") - - t.Run("ignored on method GET", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - - t.Run("ignore nil body on method POST", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", nil) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - - t.Run("added on accepted methods", func(t *testing.T) { - acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} - - for _, method := range acceptedMethods { - req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) - require.NoError(t, err, "Unexpected error") - require.Equal(t, expectedRequestBody, input.Request.Body) - } - }) - - t.Run("added with content-type specifying charset", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) - require.NoError(t, err, "Unexpected error") - require.Equal(t, expectedRequestBody, input.Request.Body) - }) - - t.Run("reject on method POST but with invalid body", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - _, err := rondRequest.Input(user, nil) - require.ErrorContains(t, err, "failed request body deserialization:") - }) - - t.Run("ignore body on method POST but with another content type", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) - req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") - - rondRequest := NewRondInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) - require.NoError(t, err, "Unexpected error") - require.Nil(t, input.Request.Body) - }) - }) -} diff --git a/core/opaevaluator.go b/core/opaevaluator.go index a1ea5b6e..384445e0 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -50,10 +50,9 @@ var Unknowns = []string{"data.resources"} type OPAEvaluator struct { PolicyEvaluator Evaluator PolicyName string - Context context.Context - m *metrics.Metrics - routerInfo openapi.RouterInfo + context context.Context + mongoClient types.IMongoClient } type PartialResultsEvaluatorConfigKey struct{} @@ -63,11 +62,11 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) { +func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (*PartialEvaluator, error) { logger.WithField("policyName", policy).Info("precomputing rego policy") policyEvaluatorTime := time.Now() - partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, options) + partialResultEvaluator, err := newPartialResultEvaluator(ctx, policy, opaModuleConfig, options) if err != nil { return nil, err } @@ -82,7 +81,7 @@ func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy st return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil } -func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (PartialResultsEvaluators, error) { if oas == nil { return nil, fmt.Errorf("oas must not be nil") } @@ -169,27 +168,14 @@ func (h printHook) Print(_ print.Context, message string) error { return err } -type EvaluatorOptions struct { +type OPAEvaluatorOptions struct { EnablePrintStatements bool MongoClient types.IMongoClient - - Metrics *metrics.Metrics - RouterInfo openapi.RouterInfo -} - -func (e *EvaluatorOptions) WithMetrics(metrics metrics.Metrics) *EvaluatorOptions { - e.Metrics = &metrics - return e -} - -func (e *EvaluatorOptions) WithRouterInfo(routerInfo openapi.RouterInfo) *EvaluatorOptions { - e.RouterInfo = routerInfo - return e } -func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { +func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { if options == nil { - options = &EvaluatorOptions{} + options = &OPAEvaluatorOptions{} } inputTerm, err := ast.ParseTerm(string(input)) if err != nil { @@ -211,19 +197,16 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod custom_builtins.MongoFindMany, ) - ctx = mongoclient.WithMongoClient(ctx, options.MongoClient) - return &OPAEvaluator{ PolicyEvaluator: query, PolicyName: policy, - Context: ctx, - m: options.Metrics, - routerInfo: options.RouterInfo, + context: ctx, + mongoClient: options.MongoClient, }, nil } -func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, policy string, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { +func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { // TODO: remove logger and set in sdk logger.WithFields(logrus.Fields{ "policyName": policy, @@ -241,9 +224,9 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger return evaluator, nil } -func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, evaluatorOptions *EvaluatorOptions) (*rego.PartialResult, error) { +func newPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, evaluatorOptions *OPAEvaluatorOptions) (*rego.PartialResult, error) { if evaluatorOptions == nil { - evaluatorOptions = &EvaluatorOptions{} + evaluatorOptions = &OPAEvaluatorOptions{} } if opaModuleConfig == nil { return nil, fmt.Errorf("OPAModuleConfig must not be nil") @@ -271,9 +254,9 @@ func NewPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf return &results, err } -func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, options *EvaluatorOptions) (*OPAEvaluator, error) { +func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { if options == nil { - options = &EvaluatorOptions{} + options = &OPAEvaluatorOptions{} } if eval, ok := partialEvaluators[policy]; ok { @@ -291,32 +274,24 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con return &OPAEvaluator{ PolicyName: policy, PolicyEvaluator: evaluator, - Context: ctx, - m: options.Metrics, - routerInfo: options.RouterInfo, + context: ctx, + mongoClient: options.MongoClient, }, nil } return nil, fmt.Errorf("%w: %s", ErrEvaluatorNotFound, policy) } -func (evaluator *OPAEvaluator) metrics() metrics.Metrics { - if evaluator.m != nil { - return *evaluator.m - } - return metrics.SetupMetrics("rond") -} - -func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitive.M, error) { +func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (primitive.M, error) { opaEvaluationTimeStart := time.Now() - partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.Context) + partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.getContext()) if err != nil { return nil, fmt.Errorf("%w: %s", ErrPartialPolicyEvalFailed, err.Error()) } opaEvaluationTime := time.Since(opaEvaluationTimeStart) - evaluator.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + options.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -325,9 +300,9 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv "policyName": evaluator.PolicyName, "partialEval": true, "allowed": true, - "matchedPath": evaluator.routerInfo.MatchedPath, - "requestedPath": evaluator.routerInfo.RequestedPath, - "method": evaluator.routerInfo.Method, + "matchedPath": options.RouterInfo.MatchedPath, + "requestedPath": options.RouterInfo.RequestedPath, + "method": options.RouterInfo.Method, }).Debug("policy evaluation completed") client := opatranslator.OPAClient{} @@ -344,16 +319,16 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv return q, nil } -func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, error) { +func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (interface{}, error) { opaEvaluationTimeStart := time.Now() - results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context) + results, err := evaluator.PolicyEvaluator.Eval(evaluator.getContext()) if err != nil { return nil, fmt.Errorf("%w: %s", ErrPolicyEvalFailed, err.Error()) } opaEvaluationTime := time.Since(opaEvaluationTimeStart) - evaluator.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + options.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -364,9 +339,9 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro "partialEval": false, "allowed": allowed, "resultsLength": len(results), - "matchedPath": evaluator.routerInfo.MatchedPath, - "requestedPath": evaluator.routerInfo.RequestedPath, - "method": evaluator.routerInfo.Method, + "matchedPath": options.RouterInfo.MatchedPath, + "requestedPath": options.RouterInfo.RequestedPath, + "method": options.RouterInfo.Method, }).Debug("policy evaluation completed") logger.WithFields(logrus.Fields{ @@ -380,12 +355,35 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro return nil, ErrPolicyEvalFailed } -func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *openapi.RondConfig) (interface{}, primitive.M, error) { +func (evaluator *OPAEvaluator) getContext() context.Context { + ctx := evaluator.context + if ctx == nil { + ctx = context.Background() + } + if evaluator.mongoClient != nil { + return mongoclient.WithMongoClient(ctx, evaluator.mongoClient) + } + return ctx +} + +type PolicyEvaluationOptions struct { + Metrics *metrics.Metrics + RouterInfo openapi.RouterInfo +} + +func (evaluator *PolicyEvaluationOptions) metrics() metrics.Metrics { + if evaluator.Metrics != nil { + return *evaluator.Metrics + } + return metrics.SetupMetrics("rond") +} + +func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *openapi.RondConfig, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { if permission.RequestFlow.GenerateQuery { - query, err := evaluator.partiallyEvaluate(logger) + query, err := evaluator.partiallyEvaluate(logger, options) return nil, query, err } - dataFromEvaluation, err := evaluator.Evaluate(logger) + dataFromEvaluation, err := evaluator.Evaluate(logger, options) if err != nil { return nil, nil, err } diff --git a/core/sdk.go b/core/sdk.go deleted file mode 100644 index f8e92714..00000000 --- a/core/sdk.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2023 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 core - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/rond-authz/rond/internal/metrics" - "github.com/rond-authz/rond/openapi" - "github.com/rond-authz/rond/types" - - "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" - "github.com/uptrace/bunrouter" -) - -type PolicyResult struct { - QueryToProxy []byte - Allowed bool -} - -// Warning: This interface is experimental, and it could change with breaking also in rond patches. -// Does not use outside this repository until it is not ready. -type SDK interface { - FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) -} - -// Warning: This interface is experimental, and it could change with breaking also in rond patches. -// Do not use outside this repository until it is not ready. -type SDKEvaluator interface { - Config() openapi.RondConfig - - EvaluateRequestPolicy(ctx context.Context, input RondInput, userInfo types.User) (PolicyResult, error) - EvaluateResponsePolicy(ctx context.Context, input RondInput, userInfo types.User, decodedBody any) ([]byte, error) -} - -type evaluator struct { - logger *logrus.Entry - rondConfig openapi.RondConfig - opaModuleConfig *OPAModuleConfig - partialResultEvaluators PartialResultsEvaluators - - evaluatorOptions *EvaluatorOptions -} - -func (e evaluator) Config() openapi.RondConfig { - return e.rondConfig -} - -func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req RondInput, userInfo types.User) (PolicyResult, error) { - if req == nil { - return PolicyResult{}, fmt.Errorf("RondInput cannot be empty") - } - - rondConfig := e.Config() - - input, err := req.Input(userInfo, nil) - if err != nil { - return PolicyResult{}, err - } - - regoInput, err := CreateRegoQueryInput(e.logger, input, RegoInputOptions{ - EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, - }) - if err != nil { - return PolicyResult{}, nil - } - - var evaluatorAllowPolicy *OPAEvaluator - if !rondConfig.RequestFlow.GenerateQuery { - evaluatorAllowPolicy, err = e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, rondConfig.RequestFlow.PolicyName, regoInput, e.evaluatorOptions) - if err != nil { - return PolicyResult{}, err - } - } else { - evaluatorAllowPolicy, err = e.opaModuleConfig.CreateQueryEvaluator(ctx, e.logger, rondConfig.RequestFlow.PolicyName, regoInput, e.evaluatorOptions) - if err != nil { - return PolicyResult{}, err - } - } - - _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, &rondConfig) - - if err != nil { - e.logger.WithField("error", logrus.Fields{ - "policyName": rondConfig.RequestFlow.PolicyName, - "message": err.Error(), - }).Error("RBAC policy evaluation failed") - return PolicyResult{}, err - } - - var queryToProxy = []byte{} - if query != nil { - queryToProxy, err = json.Marshal(query) - if err != nil { - return PolicyResult{}, err - } - } - - return PolicyResult{ - Allowed: true, - QueryToProxy: queryToProxy, - }, nil -} - -func (e evaluator) EvaluateResponsePolicy(ctx context.Context, rondInput RondInput, userInfo types.User, decodedBody any) ([]byte, error) { - if rondInput == nil { - return nil, fmt.Errorf("RondInput cannot be empty") - } - - rondConfig := e.Config() - - input, err := rondInput.Input(userInfo, decodedBody) - if err != nil { - return nil, err - } - - regoInput, err := CreateRegoQueryInput(e.logger, input, RegoInputOptions{ - EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, - }) - if err != nil { - return nil, err - } - - evaluator, err := e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, e.rondConfig.ResponseFlow.PolicyName, regoInput, e.evaluatorOptions) - if err != nil { - return nil, err - } - - bodyToProxy, err := evaluator.Evaluate(e.logger) - if err != nil { - return nil, err - } - - marshalledBody, err := json.Marshal(bodyToProxy) - if err != nil { - return nil, err - } - - return marshalledBody, nil -} - -type rondImpl struct { - partialResultEvaluators PartialResultsEvaluators - evaluatorOptions *EvaluatorOptions - oasRouter *bunrouter.CompatRouter - oas *openapi.OpenAPISpec - opaModuleConfig *OPAModuleConfig - - clientTypeHeaderKey string -} - -func (r rondImpl) FindEvaluator(logger *logrus.Entry, method, path string) (SDKEvaluator, error) { - permission, routerInfo, err := r.oas.FindPermission(r.oasRouter, path, method) - if err != nil { - return nil, err - } - return evaluator{ - rondConfig: permission, - logger: logger, - opaModuleConfig: r.opaModuleConfig, - partialResultEvaluators: r.partialResultEvaluators, - evaluatorOptions: r.evaluatorOptions.WithRouterInfo(routerInfo), - }, err -} - -// The SDK is now into core because there are coupled function here which should use the SDK itself -// (which uses core, so it will result in a cyclic dependency). In the future, sdk should be in a -// specific package. -func NewSDK( - ctx context.Context, - logger *logrus.Entry, - oas *openapi.OpenAPISpec, - opaModuleConfig *OPAModuleConfig, - evaluatorOptions *EvaluatorOptions, - registry *prometheus.Registry, - clientTypeHeaderKey string, -) (SDK, error) { - evaluator, err := SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) - if err != nil { - return nil, err - } - - logger.WithField("policiesLength", len(evaluator)).Debug("policies evaluators partial results computed") - - oasRouter, err := oas.PrepareOASRouter() - if err != nil { - return nil, fmt.Errorf("invalid OAS configuration: %s", err) - } - - m := metrics.SetupMetrics("rond") - if registry != nil { - m.MustRegister(registry) - } - if evaluatorOptions == nil { - evaluatorOptions = &EvaluatorOptions{} - } - evaluatorOptions.WithMetrics(m) - - return rondImpl{ - partialResultEvaluators: evaluator, - oasRouter: oasRouter, - evaluatorOptions: evaluatorOptions, - oas: oas, - opaModuleConfig: opaModuleConfig, - - clientTypeHeaderKey: clientTypeHeaderKey, - }, nil -} - -type sdkKey struct{} - -func WithEvaluatorSDK(ctx context.Context, evaluator SDKEvaluator) context.Context { - return context.WithValue(ctx, sdkKey{}, evaluator) -} - -func GetEvaluatorSKD(ctx context.Context) (SDKEvaluator, error) { - sdk, ok := ctx.Value(sdkKey{}).(SDKEvaluator) - if !ok { - return nil, fmt.Errorf("no SDKEvaluator found in request context") - } - - return sdk, nil -} diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go index 3d4eb8a6..37230955 100644 --- a/internal/fake/sdk.go +++ b/internal/fake/sdk.go @@ -19,6 +19,7 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" ) @@ -37,7 +38,7 @@ func NewSDKEvaluator( partialEvaluator core.PartialResultsEvaluators, permission openapi.RondConfig, requestPolicyEvaluatorResult *RequestPolicyEvaluatorResult, -) core.SDKEvaluator { +) sdk.Evaluator { return SDKEvaluator{ partialEvaluator: partialEvaluator, permission: permission, @@ -46,11 +47,11 @@ func NewSDKEvaluator( } } -func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (core.PolicyResult, error) { +func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (sdk.PolicyResult, error) { if s.requestPolicyEvaluatorResult == nil { - return core.PolicyResult{}, nil + return sdk.PolicyResult{}, nil } - return core.PolicyResult{}, s.requestPolicyEvaluatorResult.Err + return sdk.PolicyResult{}, s.requestPolicyEvaluatorResult.Err } func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) { diff --git a/main.go b/main.go index 870908fe..91f7f9e4 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ import ( "github.com/rond-authz/rond/internal/helpers" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" "github.com/mia-platform/glogger/v2" @@ -99,10 +100,14 @@ func entrypoint(shutdown chan os.Signal) { ) registry := prometheus.NewRegistry() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(log), oas, opaModuleConfig, &core.EvaluatorOptions{ - EnablePrintStatements: env.IsTraceLogLevel(), - MongoClient: mongoClient, - }, registry, env.ClientTypeHeader) + sdk, err := sdk.NewFromOAS(ctx, opaModuleConfig, oas, &sdk.FromOASOptions{ + Registry: registry, + EvaluatorOptions: &core.OPAEvaluatorOptions{ + EnablePrintStatements: env.IsTraceLogLevel(), + MongoClient: mongoClient, + }, + Logger: logrus.NewEntry(log), + }) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, diff --git a/main_test.go b/main_test.go index 78040950..391479e2 100644 --- a/main_test.go +++ b/main_test.go @@ -28,7 +28,6 @@ import ( "testing" "time" - "github.com/mia-platform/glogger/v2" "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" @@ -36,6 +35,7 @@ import ( "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" "github.com/rond-authz/rond/types" @@ -1748,7 +1748,6 @@ func TestSetupRouterStandaloneMode(t *testing.T) { defer gock.Flush() log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) env := config.EnvironmentVariables{ Standalone: true, @@ -1795,9 +1794,13 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ - MongoClient: mongoClient, - }, registry, "") + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: mongoClient, + }, + Registry: registry, + Logger: logrus.NewEntry(logger), + }) require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) @@ -1906,7 +1909,6 @@ func TestSetupRouterMetrics(t *testing.T) { defer gock.Flush() log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) env := config.EnvironmentVariables{ Standalone: true, @@ -1954,9 +1956,13 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ - MongoClient: mongoClient, - }, registry, "") + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: mongoClient, + }, + Registry: registry, + Logger: logrus.NewEntry(logger), + }) require.NoError(t, err, "unexpected error") router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 00000000..6bbac0a2 --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,4 @@ +# SDK + +The Rönd SDK API is experimental and currently under heavy development; for this reason it could undergo breaking changes regardless of the rönd package version. + diff --git a/sdk/context.go b/sdk/context.go new file mode 100644 index 00000000..b9d90d1b --- /dev/null +++ b/sdk/context.go @@ -0,0 +1,39 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "errors" +) + +var ( + ErrGetEvaluator = errors.New("no Evaluator found in request context") +) + +type sdkKey struct{} + +func WithEvaluator(ctx context.Context, evaluator Evaluator) context.Context { + return context.WithValue(ctx, sdkKey{}, evaluator) +} + +func GetEvaluator(ctx context.Context) (Evaluator, error) { + sdk, ok := ctx.Value(sdkKey{}).(Evaluator) + if !ok { + return nil, ErrGetEvaluator + } + + return sdk, nil +} diff --git a/sdk/context_test.go b/sdk/context_test.go new file mode 100644 index 00000000..d2454971 --- /dev/null +++ b/sdk/context_test.go @@ -0,0 +1,55 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "testing" + + "github.com/rond-authz/rond/openapi" + + "github.com/stretchr/testify/require" +) + +func TestContext(t *testing.T) { + t.Run("ok", func(t *testing.T) { + ctx := context.Background() + rondConfig := openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + GenerateQuery: true, + }, + ResponseFlow: openapi.ResponseFlow{ + PolicyName: "other", + }, + } + + expectedEvaluator := evaluator{ + rondConfig: rondConfig, + } + + ctx = WithEvaluator(ctx, expectedEvaluator) + + actualEvaluator, err := GetEvaluator(ctx) + require.NoError(t, err) + require.Equal(t, expectedEvaluator, actualEvaluator) + }) + + t.Run("throws if not in context", func(t *testing.T) { + actualEvaluator, err := GetEvaluator(context.Background()) + require.EqualError(t, err, ErrGetEvaluator.Error()) + require.Nil(t, actualEvaluator) + }) +} diff --git a/sdk/evaluator.go b/sdk/evaluator.go new file mode 100644 index 00000000..5abf4bdd --- /dev/null +++ b/sdk/evaluator.go @@ -0,0 +1,154 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" + + "github.com/sirupsen/logrus" +) + +type PolicyResult struct { + QueryToProxy []byte + Allowed bool +} + +// Warning: This interface is experimental, and it could change with breaking also in rond patches. +// Do not use outside this repository until it is ready. +type Evaluator interface { + // retrieve the RondConfig used to generate the evaluator + Config() openapi.RondConfig + + // EvaluateResponsePolicy evaluate request policy. In the response, it is specified if the + // request is allowed and the request query (if filter generation is requested) + EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (PolicyResult, error) + // EvaluateResponsePolicy evaluate response policy, take as input the decodedBody body from response + // (unmarshalled) and it is usable as `input.response.body` in the policy. The response is the response + // value returned by the policy. + EvaluateResponsePolicy(ctx context.Context, input core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) +} + +type evaluator struct { + logger *logrus.Entry + rondConfig openapi.RondConfig + opaModuleConfig *core.OPAModuleConfig + partialResultEvaluators core.PartialResultsEvaluators + + opaEvaluatorOptions *core.OPAEvaluatorOptions + policyEvaluationOptions *core.PolicyEvaluationOptions +} + +func (e evaluator) Config() openapi.RondConfig { + return e.rondConfig +} + +func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req core.RondInput, userInfo types.User) (PolicyResult, error) { + if req == nil { + return PolicyResult{}, fmt.Errorf("RondInput cannot be empty") + } + + rondConfig := e.Config() + + input, err := req.Input(userInfo, nil) + if err != nil { + return PolicyResult{}, err + } + + regoInput, err := core.CreateRegoQueryInput(e.logger, input, core.RegoInputOptions{ + EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, + }) + if err != nil { + return PolicyResult{}, nil + } + + var evaluatorAllowPolicy *core.OPAEvaluator + if !rondConfig.RequestFlow.GenerateQuery { + evaluatorAllowPolicy, err = e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, rondConfig.RequestFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + if err != nil { + return PolicyResult{}, err + } + } else { + evaluatorAllowPolicy, err = e.opaModuleConfig.CreateQueryEvaluator(ctx, e.logger, rondConfig.RequestFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + if err != nil { + return PolicyResult{}, err + } + } + + _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, &rondConfig, e.policyEvaluationOptions) + + if err != nil { + e.logger.WithField("error", logrus.Fields{ + "policyName": rondConfig.RequestFlow.PolicyName, + "message": err.Error(), + }).Error("RBAC policy evaluation failed") + return PolicyResult{}, err + } + + var queryToProxy = []byte{} + if query != nil { + queryToProxy, err = json.Marshal(query) + if err != nil { + return PolicyResult{}, err + } + } + + return PolicyResult{ + Allowed: true, + QueryToProxy: queryToProxy, + }, nil +} + +func (e evaluator) EvaluateResponsePolicy(ctx context.Context, rondInput core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) { + if rondInput == nil { + return nil, fmt.Errorf("RondInput cannot be empty") + } + + rondConfig := e.Config() + + input, err := rondInput.Input(userInfo, decodedBody) + if err != nil { + return nil, err + } + + regoInput, err := core.CreateRegoQueryInput(e.logger, input, core.RegoInputOptions{ + EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, + }) + if err != nil { + return nil, err + } + + evaluator, err := e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, e.rondConfig.ResponseFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + if err != nil { + return nil, err + } + + bodyToProxy, err := evaluator.Evaluate(e.logger, e.policyEvaluationOptions) + if err != nil { + return nil, err + } + + marshalledBody, err := json.Marshal(bodyToProxy) + if err != nil { + return nil, err + } + + return marshalledBody, nil +} diff --git a/core/sdk_test.go b/sdk/evaluator_test.go similarity index 63% rename from core/sdk_test.go rename to sdk/evaluator_test.go index 511f2b64..9e594ef6 100644 --- a/core/sdk_test.go +++ b/sdk/evaluator_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package sdk import ( "bytes" @@ -22,6 +22,7 @@ import ( "net/http/httptest" "testing" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" @@ -33,118 +34,11 @@ import ( "github.com/stretchr/testify/require" ) -func TestNewSDK(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - - openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") - require.Nil(t, err) - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies - very_very_composed_permission { true }`, - } - - t.Run("fails if oas is nil", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, nil, nil, nil, nil, "") - require.ErrorContains(t, err, "oas must not be nil") - require.Nil(t, sdk) - }) - - t.Run("fails if opaModuleConfig is nil", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, openAPISpec, nil, nil, nil, "") - require.ErrorContains(t, err, "OPAModuleConfig must not be nil") - require.Nil(t, sdk) - }) - - t.Run("fails if oas is invalid", func(t *testing.T) { - oas, err := openapi.LoadOASFile("../mocks/invalidOASConfiguration.json") - require.NoError(t, err) - sdk, err := NewSDK(context.Background(), logger, oas, opaModule, nil, nil, "") - require.ErrorContains(t, err, "invalid OAS configuration:") - require.Nil(t, sdk) - }) - - t.Run("creates sdk correctly", func(t *testing.T) { - sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, nil, "") - require.NoError(t, err) - require.NotEmpty(t, sdk) - }) - - t.Run("if registry is passed, setup metrics", func(t *testing.T) { - registry := prometheus.NewRegistry() - sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, registry, "") - require.NoError(t, err) - require.NotEmpty(t, sdk) - }) -} - -func TestSDK(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - - openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") - require.Nil(t, err) - opaModule := &OPAModuleConfig{ - Name: "example.rego", - Content: `package policies - very_very_composed_permission { true }`, - } - registry := prometheus.NewRegistry() - sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, nil, registry, "") - require.NoError(t, err) - - rond, ok := sdk.(rondImpl) - require.True(t, ok, "rondImpl is not sdk") - - t.Run("FindEvaluator", func(t *testing.T) { - t.Run("throws if path and method not found", func(t *testing.T) { - actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/not-existent/path") - require.ErrorContains(t, err, "not found oas definition: GET /not-existent/path") - require.Nil(t, actual) - }) - - t.Run("returns correct evaluator", func(t *testing.T) { - actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") - require.NoError(t, err) - evaluatorOptions := &EvaluatorOptions{ - Metrics: rond.evaluatorOptions.Metrics, - RouterInfo: openapi.RouterInfo{ - MatchedPath: "/users/", - RequestedPath: "/users/", - Method: http.MethodGet, - }, - } - require.Equal(t, evaluator{ - rondConfig: openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ - PolicyName: "todo", - }, - }, - opaModuleConfig: opaModule, - partialResultEvaluators: rond.partialResultEvaluators, - logger: logger, - evaluatorOptions: evaluatorOptions, - }, actual) - - t.Run("get permissions", func(t *testing.T) { - require.Equal(t, openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ - PolicyName: "todo", - }, - }, actual.Config()) - }) - }) - }) -} - func TestEvaluateRequestPolicy(t *testing.T) { logger := logrus.NewEntry(logrus.New()) - clientTypeHeaderKey := "client-header-key" - t.Run("throws without RondInput", func(t *testing.T) { - sdk := getSdk(t, nil) + sdk := getOASSdk(t, nil) evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") require.NoError(t, err) @@ -196,7 +90,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, expectedPolicy: PolicyResult{}, - expectedErr: ErrPolicyEvalFailed, + expectedErr: core.ErrPolicyEvalFailed, }, "not allowed policy result": { method: http.MethodGet, @@ -207,7 +101,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { opaModuleContent: `package policies todo { false }`, expectedPolicy: PolicyResult{}, - expectedErr: ErrPolicyEvalFailed, + expectedErr: core.ErrPolicyEvalFailed, }, "with empty filter query": { method: http.MethodGet, @@ -292,6 +186,33 @@ func TestEvaluateRequestPolicy(t *testing.T) { user: types.User{ UserID: "my-user", }, + opaModuleContent: `package policies + todo { + project := find_one("my-collection", {"myField": "1234"}) + project.myField == "1234" + } + `, + mongoClient: &mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte{}, + }, + }, + "with mongo client and find_one with dynamic find_one query": { + method: http.MethodGet, + path: "/users/", + user: types.User{ + UserID: "my-user", + Properties: map[string]any{ + "field": "1234", + }, + }, mongoClient: &mocks.MongoClientMock{ FindOneResult: map[string]string{"myField": "1234"}, FindOneExpectation: func(collectionName string, query interface{}) { @@ -300,10 +221,12 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, }, opaModuleContent: `package policies - todo { - project := find_one("my-collection", {"myField": "1234"}) - project.myField == "1234" - } + todo { + field := input.user.properties.field + field == "1234" + project := find_one("my-collection", {"myField": field}) + project.myField == "1234" + } `, expectedPolicy: PolicyResult{ Allowed: true, @@ -343,9 +266,6 @@ func TestEvaluateRequestPolicy(t *testing.T) { user: types.User{ UserGroups: []string{"my-group"}, }, - reqHeaders: map[string]string{ - "my-header-key": "ok", - }, mongoClient: &mocks.MongoClientMock{ FindOneResult: map[string]string{"myField": "1234"}, FindOneExpectation: func(collectionName string, query interface{}) { @@ -366,12 +286,44 @@ func TestEvaluateRequestPolicy(t *testing.T) { QueryToProxy: []byte(`{"$or":[{"$and":[{"filterField":{"$eq":"1234"}}]}]}`), }, }, + "with query and mongo client with dynamic find_one query": { + method: http.MethodGet, + path: "/users/", + oasFilePath: "../mocks/rondOasConfig.json", + user: types.User{ + UserID: "my-user", + Properties: map[string]any{ + "field": "1234", + }, + }, + mongoClient: &mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + opaModuleContent: ` + package policies + generate_filter { + field := input.user.properties.field + field == "1234" + project := find_one("my-collection", {"myField": field}) + + query := data.resources[_] + query.filterField == "1234" + }`, + expectedPolicy: PolicyResult{ + Allowed: true, + QueryToProxy: []byte(`{"$or":[{"$and":[{"filterField":{"$eq":"1234"}}]}]}`), + }, + }, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { registry := prometheus.NewPedanticRegistry() - sdk := getSdk(t, &sdkOptions{ + sdk := getOASSdk(t, &sdkOptions{ opaModuleContent: testCase.opaModuleContent, oasFilePath: testCase.oasFilePath, mongoClient: testCase.mongoClient, @@ -384,13 +336,17 @@ func TestEvaluateRequestPolicy(t *testing.T) { evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) require.NoError(t, err) - req := httptest.NewRequest(testCase.method, testCase.path, nil) + headers := http.Header{} if testCase.reqHeaders != nil { for k, v := range testCase.reqHeaders { - req.Header.Set(k, v) + headers.Set(k, v) } } - rondInput := NewRondInput(req, clientTypeHeaderKey, nil) + rondInput := getFakeInput(t, core.InputRequest{ + Headers: headers, + Path: testCase.path, + Method: testCase.method, + }, "") actual, err := evaluate.EvaluateRequestPolicy(context.Background(), rondInput, testCase.user) if testCase.expectedErr != nil { @@ -420,7 +376,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { fields := logrus.Fields{ "allowed": actual.Allowed, "requestedPath": testCase.path, - "matchedPath": evaluatorInfo.evaluatorOptions.RouterInfo.MatchedPath, + "matchedPath": evaluatorInfo.policyEvaluationOptions.RouterInfo.MatchedPath, "method": testCase.method, "partialEval": evaluate.Config().RequestFlow.GenerateQuery, "policyName": evaluate.Config().RequestFlow.PolicyName, @@ -466,10 +422,8 @@ func assertCorrectMetrics(t *testing.T, registry *prometheus.Registry, expected func TestEvaluateResponsePolicy(t *testing.T) { logger := logrus.NewEntry(logrus.New()) - clientTypeHeaderKey := "client-header-key" - t.Run("throws without RondInput", func(t *testing.T) { - sdk := getSdk(t, nil) + sdk := getOASSdk(t, nil) evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") require.NoError(t, err) @@ -533,7 +487,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { false body := input.response.body }`, - expectedErr: ErrPolicyEvalFailed, + expectedErr: core.ErrPolicyEvalFailed, expectedBody: "", notAllowed: true, }, @@ -581,7 +535,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { log.Level = logrus.DebugLevel logger := logrus.NewEntry(log) registry := prometheus.NewPedanticRegistry() - sdk := getSdk(t, &sdkOptions{ + sdk := getOASSdk(t, &sdkOptions{ opaModuleContent: opaModuleContent, oasFilePath: "../mocks/rondOasConfig.json", mongoClient: testCase.mongoClient, @@ -597,7 +551,17 @@ func TestEvaluateResponsePolicy(t *testing.T) { req.Header.Set(k, v) } } - rondInput := NewRondInput(req, clientTypeHeaderKey, nil) + headers := http.Header{} + if testCase.reqHeaders != nil { + for k, v := range testCase.reqHeaders { + headers.Set(k, v) + } + } + rondInput := getFakeInput(t, core.InputRequest{ + Headers: headers, + Path: testCase.path, + Method: testCase.method, + }, "") actual, err := evaluate.EvaluateResponsePolicy(context.Background(), rondInput, testCase.user, testCase.decodedBody) if testCase.expectedErr != nil { @@ -626,7 +590,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { require.Equal(t, logrus.Fields{ "allowed": !testCase.notAllowed, "requestedPath": testCase.path, - "matchedPath": evaluatorInfo.evaluatorOptions.RouterInfo.MatchedPath, + "matchedPath": evaluatorInfo.policyEvaluationOptions.RouterInfo.MatchedPath, "method": testCase.method, "partialEval": false, "policyName": evaluate.Config().ResponseFlow.PolicyName, @@ -643,39 +607,8 @@ func TestEvaluateResponsePolicy(t *testing.T) { }) } -func TestContext(t *testing.T) { - t.Run("ok", func(t *testing.T) { - ctx := context.Background() - rondConfig := openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ - PolicyName: "todo", - GenerateQuery: true, - }, - ResponseFlow: openapi.ResponseFlow{ - PolicyName: "other", - }, - } - - expectedEvaluator := evaluator{ - rondConfig: rondConfig, - } - - ctx = WithEvaluatorSDK(ctx, expectedEvaluator) - - actualEvaluator, err := GetEvaluatorSKD(ctx) - require.NoError(t, err) - require.Equal(t, expectedEvaluator, actualEvaluator) - }) - - t.Run("throws if not in context", func(t *testing.T) { - actualEvaluator, err := GetEvaluatorSKD(context.Background()) - require.EqualError(t, err, "no SDKEvaluator found in request context") - require.Nil(t, actualEvaluator) - }) -} - func BenchmarkEvaluateRequest(b *testing.B) { - moduleConfig, err := LoadRegoModule("../mocks/bench-policies") + moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") require.NoError(b, err, "Unexpected error") openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") @@ -683,21 +616,29 @@ func BenchmarkEvaluateRequest(b *testing.B) { log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) - sdk, err := NewSDK(context.Background(), logger, openAPISpec, moduleConfig, &EvaluatorOptions{ - MongoClient: testmongoMock, - }, nil, "") + sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: testmongoMock, + }, + }) require.NoError(b, err) b.ResetTimer() for n := 0; n < b.N; n++ { b.StopTimer() - req := httptest.NewRequest(http.MethodGet, "/projects/project123", nil) - req.Header.Set("my-header", "value") + headers := http.Header{} + headers.Set("my-header", "value") recorder := httptest.NewRecorder() - rondInput := NewRondInput(req, "", map[string]string{ - "projectId": "project123", - }) + + rondInput := getFakeInput(b, core.InputRequest{ + Path: "/projects/project123", + Headers: headers, + Method: http.MethodGet, + PathParams: map[string]string{ + "projectId": "project123", + }, + }, "") b.StartTimer() evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/project123") require.NoError(b, err) @@ -707,19 +648,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { } } -type sdkOptions struct { - opaModuleContent string - oasFilePath string - - mongoClient types.IMongoClient - registry *prometheus.Registry -} - -type tHelper interface { - Helper() -} - -func getSdk(t require.TestingT, options *sdkOptions) SDK { +func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if h, ok := t.(tHelper); ok { h.Helper() } @@ -736,7 +665,7 @@ func getSdk(t require.TestingT, options *sdkOptions) SDK { openAPISpec, err := openapi.LoadOASFile(oasFilePath) require.NoError(t, err) - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { true }`, @@ -744,111 +673,15 @@ func getSdk(t require.TestingT, options *sdkOptions) SDK { if options.opaModuleContent != "" { opaModule.Content = options.opaModuleContent } - sdk, err := NewSDK(context.Background(), logger, openAPISpec, opaModule, &EvaluatorOptions{ - EnablePrintStatements: true, - MongoClient: options.mongoClient, - }, options.registry, "") + sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &FromOASOptions{ + Registry: options.registry, + EvaluatorOptions: &core.OPAEvaluatorOptions{ + EnablePrintStatements: true, + MongoClient: options.mongoClient, + }, + Logger: logger, + }) require.NoError(t, err) return sdk } - -var testmongoMock = &mocks.MongoClientMock{ - UserBindings: []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"admin"}, - Groups: []string{"area_rocket"}, - Permissions: []string{"permission4"}, - Resource: &types.Resource{ - ResourceType: "project", - ResourceID: "project123", - }, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding3", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission10", "permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding4", - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission11"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFiltering", - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFilteringFromSubject", - Subjects: []string{"filter_test"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role6"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PRIVATE", - }, - }, - UserRoles: []types.Role{ - { - RoleID: "admin", - Permissions: []string{"console.project.view", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role6", - Permissions: []string{"permission3", "permission5"}, - CRUDDocumentState: "PRIVATE", - }, - { - RoleID: "notUsedByAnyone", - Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, - CRUDDocumentState: "PUBLIC", - }, - }, -} diff --git a/sdk/openapi.go b/sdk/openapi.go new file mode 100644 index 00000000..46a6d857 --- /dev/null +++ b/sdk/openapi.go @@ -0,0 +1,57 @@ +// Copyright 2023 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 sdk + +import ( + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/openapi" + + "github.com/sirupsen/logrus" + "github.com/uptrace/bunrouter" +) + +type oasImpl struct { + oas *openapi.OpenAPISpec + oasRouter *bunrouter.CompatRouter + + opaModuleConfig *core.OPAModuleConfig + partialResultEvaluators core.PartialResultsEvaluators + opaEvaluatorOptions *core.OPAEvaluatorOptions + metrics *metrics.Metrics +} + +func (r oasImpl) FindEvaluator(logger *logrus.Entry, method, path string) (Evaluator, error) { + permission, routerInfo, err := r.oas.FindPermission(r.oasRouter, path, method) + if err != nil { + return nil, err + } + return evaluator{ + rondConfig: permission, + logger: logger, + opaModuleConfig: r.opaModuleConfig, + partialResultEvaluators: r.partialResultEvaluators, + + opaEvaluatorOptions: r.opaEvaluatorOptions, + policyEvaluationOptions: &core.PolicyEvaluationOptions{ + Metrics: r.metrics, + RouterInfo: routerInfo, + }, + }, err +} + +type OASEvaluatorFinder interface { + FindEvaluator(logger *logrus.Entry, method, path string) (Evaluator, error) +} diff --git a/sdk/openapi_test.go b/sdk/openapi_test.go new file mode 100644 index 00000000..1bdb169d --- /dev/null +++ b/sdk/openapi_test.go @@ -0,0 +1,91 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "net/http" + "testing" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/openapi" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestOasSDK(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.Nil(t, err) + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + registry := prometheus.NewRegistry() + sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &FromOASOptions{ + Registry: registry, + Logger: logger, + }) + require.NoError(t, err) + + oas, ok := sdk.(oasImpl) + require.True(t, ok, "oasImpl is not sdk") + + t.Run("FindEvaluator", func(t *testing.T) { + t.Run("throws if path and method not found", func(t *testing.T) { + actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/not-existent/path") + require.ErrorContains(t, err, "not found oas definition: GET /not-existent/path") + require.Nil(t, actual) + }) + + t.Run("returns correct evaluator", func(t *testing.T) { + actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + evaluatorOptions := &core.PolicyEvaluationOptions{ + Metrics: oas.metrics, + RouterInfo: openapi.RouterInfo{ + MatchedPath: "/users/", + RequestedPath: "/users/", + Method: http.MethodGet, + }, + } + require.Equal(t, evaluator{ + rondConfig: openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + }, + }, + opaModuleConfig: opaModule, + partialResultEvaluators: oas.partialResultEvaluators, + logger: logger, + policyEvaluationOptions: evaluatorOptions, + }, actual) + + t.Run("get permissions", func(t *testing.T) { + require.Equal(t, openapi.RondConfig{ + RequestFlow: openapi.RequestFlow{ + PolicyName: "todo", + }, + }, actual.Config()) + }) + }) + }) +} diff --git a/sdk/rondinput/http/input.go b/sdk/rondinput/http/input.go new file mode 100644 index 00000000..79ae226b --- /dev/null +++ b/sdk/rondinput/http/input.go @@ -0,0 +1,80 @@ +// Copyright 2023 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 rondhttp + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/types" +) + +type requestInfo struct { + *http.Request + clientTypeHeaderKey string + pathParams map[string]string +} + +func (req requestInfo) Input(user types.User, responseBody any) (core.Input, error) { + shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && + req.ContentLength > 0 && + (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) + + var requestBody any + if shouldParseJSONBody { + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + return core.Input{}, fmt.Errorf("failed request body parse: %s", err.Error()) + } + if err := json.Unmarshal(bodyBytes, &requestBody); err != nil { + return core.Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error()) + } + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + } + + return core.Input{ + ClientType: req.Header.Get(req.clientTypeHeaderKey), + Request: core.InputRequest{ + Method: req.Method, + Path: req.URL.Path, + Headers: req.Header, + Query: req.URL.Query(), + PathParams: req.pathParams, + Body: requestBody, + }, + Response: core.InputResponse{ + Body: responseBody, + }, + User: core.InputUser{ + Properties: user.Properties, + Groups: user.UserGroups, + Bindings: user.UserBindings, + Roles: user.UserRoles, + }, + }, nil +} + +func NewInput(req *http.Request, clientTypeHeaderKey string, pathParams map[string]string) core.RondInput { + return requestInfo{ + Request: req, + clientTypeHeaderKey: clientTypeHeaderKey, + pathParams: pathParams, + } +} diff --git a/sdk/rondinput/http/input_test.go b/sdk/rondinput/http/input_test.go new file mode 100644 index 00000000..e8635d12 --- /dev/null +++ b/sdk/rondinput/http/input_test.go @@ -0,0 +1,104 @@ +// Copyright 2023 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 rondhttp + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/types" + + "github.com/stretchr/testify/require" +) + +func TestRondInput(t *testing.T) { + user := types.User{} + clientTypeHeaderKey := "clienttypeheader" + pathParams := map[string]string{} + + t.Run("request body integration", func(t *testing.T) { + expectedRequestBody := map[string]interface{}{ + "Key": float64(42), + } + reqBody := struct{ Key int }{ + Key: 42, + } + reqBodyBytes, err := json.Marshal(reqBody) + require.Nil(t, err, "Unexpected error") + + t.Run("ignored on method GET", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) + + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("ignore nil body on method POST", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", nil) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + + t.Run("added on accepted methods", func(t *testing.T) { + acceptedMethods := []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} + + for _, method := range acceptedMethods { + req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + } + }) + + t.Run("added with content-type specifying charset", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + require.NoError(t, err, "Unexpected error") + require.Equal(t, expectedRequestBody, input.Request.Body) + }) + + t.Run("reject on method POST but with invalid body", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "application/json") + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + _, err := rondRequest.Input(user, nil) + require.ErrorContains(t, err, "failed request body deserialization:") + }) + + t.Run("ignore body on method POST but with another content type", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) + req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") + + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + require.NoError(t, err, "Unexpected error") + require.Nil(t, input.Request.Body) + }) + }) +} diff --git a/sdk/sdk.go b/sdk/sdk.go new file mode 100644 index 00000000..62ee962b --- /dev/null +++ b/sdk/sdk.go @@ -0,0 +1,87 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "fmt" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/openapi" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" +) + +type FromOASOptions struct { + Registry *prometheus.Registry + EvaluatorOptions *core.OPAEvaluatorOptions + Logger *logrus.Entry +} + +// The SDK is now into core because there are coupled function here which should use the SDK itself +// (which uses core, so it will result in a cyclic dependency). In the future, sdk should be in a +// specific package. +func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, options *FromOASOptions) (OASEvaluatorFinder, error) { + if opaModuleConfig == nil { + return nil, fmt.Errorf("OPAModuleConfig must not be nil") + } + + if options == nil { + options = &FromOASOptions{} + } + if options.Logger == nil { + // TODO: default to a fake silent logger instead of return error + return nil, fmt.Errorf("logger is required inside options") + } + + var evaluatorOptions *core.OPAEvaluatorOptions + if options.EvaluatorOptions != nil { + evaluatorOptions = options.EvaluatorOptions + } + metrics := setupMetrics(options.Registry) + + logger := options.Logger + evaluator, err := core.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) + if err != nil { + return nil, err + } + + logger.WithField("policiesLength", len(evaluator)).Debug("policies evaluators partial results computed") + + oasRouter, err := oas.PrepareOASRouter() + if err != nil { + return nil, fmt.Errorf("invalid OAS configuration: %s", err) + } + + return oasImpl{ + oas: oas, + oasRouter: oasRouter, + + opaModuleConfig: opaModuleConfig, + partialResultEvaluators: evaluator, + opaEvaluatorOptions: evaluatorOptions, + metrics: metrics, + }, nil +} + +func setupMetrics(registry *prometheus.Registry) *metrics.Metrics { + m := metrics.SetupMetrics("rond") + if registry != nil { + m.MustRegister(registry) + } + return &m +} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go new file mode 100644 index 00000000..d45a21f3 --- /dev/null +++ b/sdk/sdk_test.go @@ -0,0 +1,263 @@ +// Copyright 2023 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 sdk + +import ( + "context" + "net/http" + "testing" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/types" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestNewFromOas(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + very_very_composed_permission { true }`, + } + ctx := context.Background() + + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + options := &FromOASOptions{ + Logger: logger, + } + + t.Run("throws if options is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, nil) + require.EqualError(t, err, "logger is required inside options") + require.Nil(t, sdk) + }) + + t.Run("throws if logger is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{}) + require.EqualError(t, err, "logger is required inside options") + require.Nil(t, sdk) + }) + + t.Run("throws if opaModuleConfig is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, nil, nil, options) + require.EqualError(t, err, "OPAModuleConfig must not be nil") + require.Nil(t, sdk) + }) + + t.Run("throws if oas is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, nil, options) + require.EqualError(t, err, "oas must not be nil") + require.Nil(t, sdk) + }) + + t.Run("throws if oas is invalid", func(t *testing.T) { + oas, err := openapi.LoadOASFile("../mocks/invalidOASConfiguration.json") + require.NoError(t, err) + sdk, err := NewFromOAS(ctx, opaModule, oas, options) + require.ErrorContains(t, err, "invalid OAS configuration:") + require.Nil(t, sdk) + }) + + t.Run("if registry is passed, setup metrics", func(t *testing.T) { + registry := prometheus.NewRegistry() + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{ + Registry: registry, + Logger: logger, + }) + require.NoError(t, err) + require.NotEmpty(t, sdk) + }) + + t.Run("passes EvaluatorOptions and set metrics correctly", func(t *testing.T) { + evalOpts := &core.OPAEvaluatorOptions{ + EnablePrintStatements: true, + } + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{ + EvaluatorOptions: evalOpts, + Logger: logger, + Registry: prometheus.NewRegistry(), + }) + require.NoError(t, err) + require.NotEmpty(t, sdk) + r, ok := sdk.(oasImpl) + require.True(t, ok) + require.Equal(t, evalOpts, r.opaEvaluatorOptions) + }) + + t.Run("creates OAS sdk correctly", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, options) + require.NoError(t, err) + + t.Run("and find evaluators", func(t *testing.T) { + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + require.NotNil(t, evaluator) + }) + }) +} + +type sdkOptions struct { + opaModuleContent string + oasFilePath string + + mongoClient types.IMongoClient + registry *prometheus.Registry +} + +type tHelper interface { + Helper() +} + +var testmongoMock = &mocks.MongoClientMock{ + UserBindings: []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"admin"}, + Groups: []string{"area_rocket"}, + Permissions: []string{"permission4"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "project123", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding3", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission10", "permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding4", + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission11"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFiltering", + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFilteringFromSubject", + Subjects: []string{"filter_test"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "notUsedByAnyone", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permissionNotUsed"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "notUsedByAnyone2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role6"}, + Permissions: []string{"permissionNotUsed"}, + CRUDDocumentState: "PRIVATE", + }, + }, + UserRoles: []types.Role{ + { + RoleID: "admin", + Permissions: []string{"console.project.view", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role6", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PRIVATE", + }, + { + RoleID: "notUsedByAnyone", + Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, + CRUDDocumentState: "PUBLIC", + }, + }, +} + +type FakeInput struct { + request core.InputRequest + clientType string +} + +func (i FakeInput) Input(user types.User, responseBody any) (core.Input, error) { + return core.Input{ + User: core.InputUser{ + Properties: user.Properties, + Groups: user.UserGroups, + Bindings: user.UserBindings, + Roles: user.UserRoles, + }, + Request: i.request, + Response: core.InputResponse{ + Body: responseBody, + }, + ClientType: i.clientType, + }, nil +} + +func getFakeInput(t require.TestingT, request core.InputRequest, clientType string) core.RondInput { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + return FakeInput{ + request: request, + clientType: clientType, + } +} diff --git a/service/handler.go b/service/handler.go index 01b3b5f7..ac31b21d 100644 --- a/service/handler.go +++ b/service/handler.go @@ -19,12 +19,13 @@ import ( "net/http" "net/http/httputil" - "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" + rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" @@ -40,7 +41,7 @@ func ReverseProxyOrResponse( env config.EnvironmentVariables, w http.ResponseWriter, req *http.Request, - evaluatorSdk core.SDKEvaluator, + evaluatorSdk sdk.Evaluator, ) { var permission openapi.RondConfig if evaluatorSdk != nil { @@ -76,7 +77,7 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { return } - evaluatorSdk, err := core.GetEvaluatorSKD(requestContext) + evaluatorSdk, err := sdk.GetEvaluator(requestContext) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("no evaluatorSdk found in context") utils.FailResponse(w, "no evaluators sdk found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) @@ -93,7 +94,7 @@ func EvaluateRequest( req *http.Request, env config.EnvironmentVariables, w http.ResponseWriter, - evaluatorSdk core.SDKEvaluator, + evaluatorSdk sdk.Evaluator, ) error { requestContext := req.Context() logger := glogger.Get(requestContext) @@ -111,7 +112,7 @@ func EvaluateRequest( return err } - rondInput := core.NewRondInput(req, env.ClientTypeHeader, mux.Vars(req)) + rondInput := rondhttp.NewInput(req, env.ClientTypeHeader, mux.Vars(req)) result, err := evaluatorSdk.EvaluateRequestPolicy(req.Context(), rondInput, userInfo) if err != nil { if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { @@ -147,7 +148,7 @@ func ReverseProxy( w http.ResponseWriter, req *http.Request, permission *openapi.RondConfig, - evaluatorSdk core.SDKEvaluator, + evaluatorSdk sdk.Evaluator, ) { targetHostFromEnv := env.TargetServiceHost proxy := httputil.ReverseProxy{ @@ -167,7 +168,7 @@ func ReverseProxy( proxy.ServeHTTP(w, req) return } - proxy.Transport = core.NewOPATransport( + proxy.Transport = NewOPATransport( http.DefaultTransport, req.Context(), logger, diff --git a/core/opa_transport.go b/service/opa_transport.go similarity index 89% rename from core/opa_transport.go rename to service/opa_transport.go index be3cd27e..ea079632 100644 --- a/core/opa_transport.go +++ b/service/opa_transport.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package service import ( "bytes" @@ -23,14 +23,22 @@ import ( "net/http" "strconv" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/sdk" + rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) +var ( + ErrUnexepectedContentType = fmt.Errorf("unexpected content type") + ErrOPATransportInvalidResponseBody = fmt.Errorf("response body is not valid") +) + type OPATransport struct { http.RoundTripper // FIXME: this overlaps with the req.Context used during RoundTrip. @@ -40,7 +48,7 @@ type OPATransport struct { clientHeaderKey string userHeaders types.UserHeadersKeys - evaluatorSDK SDKEvaluator + evaluatorSDK sdk.Evaluator } func NewOPATransport( @@ -50,7 +58,7 @@ func NewOPATransport( req *http.Request, clientHeaderKey string, userHeadersKeys types.UserHeadersKeys, - evaluatorSDK SDKEvaluator, + evaluatorSDK sdk.Evaluator, ) *OPATransport { return &OPATransport{ RoundTripper: transport, @@ -108,7 +116,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er } pathParams := mux.Vars(t.request) - input := NewRondInput(t.request, t.clientHeaderKey, pathParams) + input := rondhttp.NewInput(t.request, t.clientHeaderKey, pathParams) responseBody, err := t.evaluatorSDK.EvaluateResponsePolicy(t.context, input, userInfo, decodedBody) if err != nil { @@ -121,7 +129,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er } func (t *OPATransport) responseWithError(resp *http.Response, err error, statusCode int) { - t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error(ErrResponsePolicyEvalFailed) + t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error(core.ErrResponsePolicyEvalFailed) message := utils.NO_PERMISSIONS_ERROR_MESSAGE if statusCode != http.StatusForbidden { message = utils.GENERIC_BUSINESS_ERROR_MESSAGE diff --git a/core/opa_transport_test.go b/service/opa_transport_test.go similarity index 91% rename from core/opa_transport_test.go rename to service/opa_transport_test.go index faa1ee87..730e5d1d 100644 --- a/core/opa_transport_test.go +++ b/service/opa_transport_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package service import ( "bytes" @@ -25,10 +25,15 @@ import ( "strconv" "testing" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" + + "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -465,3 +470,54 @@ func (m *MockReader) Read(p []byte) (n int, err error) { func (m *MockReader) Close() error { return m.CloseError } + +type sdkOptions struct { + opaModuleContent string + oasFilePath string + + mongoClient types.IMongoClient + registry *prometheus.Registry +} + +type tHelper interface { + Helper() +} + +func getSdk(t require.TestingT, options *sdkOptions) sdk.OASEvaluatorFinder { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + logger := logrus.NewEntry(logrus.New()) + if options == nil { + options = &sdkOptions{} + } + + var oasFilePath = "../mocks/simplifiedMock.json" + if options.oasFilePath != "" { + oasFilePath = options.oasFilePath + } + + openAPISpec, err := openapi.LoadOASFile(oasFilePath) + require.NoError(t, err) + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + todo { true }`, + } + if options.opaModuleContent != "" { + opaModule.Content = options.opaModuleContent + } + + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: options.mongoClient, + EnablePrintStatements: true, + }, + Registry: options.registry, + Logger: logger, + }) + require.NoError(t, err) + + return sdk +} diff --git a/core/opamiddleware.go b/service/opamiddleware.go similarity index 91% rename from core/opamiddleware.go rename to service/opamiddleware.go index 62adc022..cce9dd96 100644 --- a/core/opamiddleware.go +++ b/service/opamiddleware.go @@ -12,15 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package service import ( "errors" "net/http" "strings" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/gorilla/mux" "github.com/mia-platform/glogger/v2" @@ -33,8 +35,8 @@ type OPAMiddlewareOptions struct { } func OPAMiddleware( - opaModuleConfig *OPAModuleConfig, - sdk SDK, + opaModuleConfig *core.OPAModuleConfig, + rondSDK sdk.OASEvaluatorFinder, routesToNotProxy []string, targetServiceOASPath string, options *OPAMiddlewareOptions, @@ -53,7 +55,7 @@ func OPAMiddleware( logger := glogger.Get(r.Context()) - evaluator, err := sdk.FindEvaluator(logger, r.Method, path) + evaluator, err := rondSDK.FindEvaluator(logger, r.Method, path) rondConfig := openapi.RondConfig{} if err == nil { rondConfig = evaluator.Config() @@ -91,7 +93,7 @@ func OPAMiddleware( return } - ctx := WithEvaluatorSDK(r.Context(), evaluator) + ctx := sdk.WithEvaluator(r.Context(), evaluator) next.ServeHTTP(w, r.WithContext(ctx)) }) diff --git a/core/opamiddleware_test.go b/service/opamiddleware_test.go similarity index 85% rename from core/opamiddleware_test.go rename to service/opamiddleware_test.go index c61806e3..910134c6 100644 --- a/core/opamiddleware_test.go +++ b/service/opamiddleware_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package service import ( "context" @@ -23,8 +23,10 @@ import ( "os" "testing" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -33,27 +35,21 @@ import ( ) func TestOPAMiddleware(t *testing.T) { - getSDK := func(t *testing.T, oas *openapi.OpenAPISpec, opaModule *OPAModuleConfig) SDK { + getSDK := func(t *testing.T, oas *openapi.OpenAPISpec, opaModule *core.OPAModuleConfig) sdk.OASEvaluatorFinder { t.Helper() logger, _ := test.NewNullLogger() - sdk, err := NewSDK( - context.Background(), - logrus.NewEntry(logger), - oas, - opaModule, - nil, - nil, - "", - ) - require.NoError(t, err) + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.FromOASOptions{ + Logger: logrus.NewEntry(logger), + }) + require.NoError(t, err, "unexpected error") return sdk } routesNotToProxy := make([]string, 0) t.Run(`strict mode failure`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { true }`, @@ -117,7 +113,7 @@ todo { true }`, }) t.Run(`documentation request`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies foobar { true }`, @@ -183,15 +179,15 @@ foobar { true }`, require.NoError(t, err) t.Run(`rego package doesn't contain expected policy`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies another { true }`, } - sdk := getSDK(t, openAPISpec, opaModule) + rondSDK := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err, "Unexpected error") require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) @@ -205,15 +201,15 @@ foobar { true }`, }) t.Run(`rego package contains expected permission`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies todo { true }`, } - sdk := getSDK(t, openAPISpec, opaModule) + rondSDK := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) @@ -227,16 +223,16 @@ foobar { true }`, }) t.Run(`rego package contains composed permission`, func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies very_very_composed_permission { true }`, } - sdk := getSDK(t, openAPISpec, opaModule) + rondSDK := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", nil) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) @@ -250,7 +246,7 @@ very_very_composed_permission { true }`, }) t.Run("injects correct permission", func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies very_very_composed_permission_with_eval { true }`, @@ -260,11 +256,11 @@ very_very_composed_permission_with_eval { true }`, IsStandalone: false, PathPrefixStandalone: "/eval", } - sdk := getSDK(t, openAPISpec, opaModule) + rondSDK := getSDK(t, openAPISpec, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) @@ -282,8 +278,8 @@ very_very_composed_permission_with_eval { true }`, routesNotToProxy := []string{"/not/proxy"} middleware := OPAMiddleware(nil, nil, routesNotToProxy, "", nil) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := GetEvaluatorSKD(r.Context()) - require.EqualError(t, err, "no SDKEvaluator found in request context") + _, err := sdk.GetEvaluator(r.Context()) + require.EqualError(t, err, sdk.ErrGetEvaluator.Error()) })) w := httptest.NewRecorder() @@ -303,35 +299,30 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { IsStandalone: true, PathPrefixStandalone: "/eval", } - getSdk := func(t *testing.T, opaModule *OPAModuleConfig) SDK { + getSdk := func(t *testing.T, opaModule *core.OPAModuleConfig) sdk.OASEvaluatorFinder { t.Helper() log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) - sdk, err := NewSDK( - context.Background(), - logger, - openAPISpec, - opaModule, - nil, - nil, - "", - ) - require.NoError(t, err) + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.FromOASOptions{ + Logger: logger, + }) + require.NoError(t, err, "unexpected error") + return sdk } t.Run("injects correct path removing prefix", func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies very_very_composed_permission { true }`, } - sdk := getSdk(t, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) + rondSDK := getSdk(t, opaModule) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) @@ -345,16 +336,16 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { }) t.Run("injects correct path removing only one prefix", func(t *testing.T) { - opaModule := &OPAModuleConfig{ + opaModule := &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies very_very_composed_permission_with_eval { true }`, } - sdk := getSdk(t, opaModule) - middleware := OPAMiddleware(opaModule, sdk, routesNotToProxy, "", options) + rondSDK := getSdk(t, opaModule) + middleware := OPAMiddleware(opaModule, rondSDK, routesNotToProxy, "", options) builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - actual, err := GetEvaluatorSKD(r.Context()) + actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) diff --git a/service/router.go b/service/router.go index 39e3b5d4..88488054 100644 --- a/service/router.go +++ b/service/router.go @@ -32,6 +32,7 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" @@ -100,7 +101,7 @@ func SetupRouter( env config.EnvironmentVariables, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, - sdk core.SDK, + sdk sdk.OASEvaluatorFinder, mongoClient *mongoclient.MongoClient, registry *prometheus.Registry, ) (*mux.Router, error) { @@ -149,7 +150,7 @@ func SetupRouter( } } - evalRouter.Use(core.OPAMiddleware(opaModuleConfig, sdk, routesToNotProxy, env.TargetServiceOASPath, &core.OPAMiddlewareOptions{ + evalRouter.Use(OPAMiddleware(opaModuleConfig, sdk, routesToNotProxy, env.TargetServiceOASPath, &OPAMiddlewareOptions{ IsStandalone: env.Standalone, PathPrefixStandalone: env.PathPrefixStandalone, })) diff --git a/service/router_test.go b/service/router_test.go index e0561d1e..9445b909 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -30,6 +30,7 @@ import ( "github.com/rond-authz/rond/internal/fake" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" @@ -231,7 +232,7 @@ func createContext( t *testing.T, originalCtx context.Context, env config.EnvironmentVariables, - evaluator core.SDKEvaluator, + evaluator sdk.Evaluator, mongoClient *mocks.MongoClientMock, ) context.Context { t.Helper() @@ -239,7 +240,7 @@ func createContext( var partialContext context.Context partialContext = context.WithValue(originalCtx, config.EnvKey{}, env) - partialContext = core.WithEvaluatorSDK(partialContext, evaluator) + partialContext = sdk.WithEvaluator(partialContext, evaluator) if mongoClient != nil { partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) @@ -271,7 +272,7 @@ func getEvaluator( oas *openapi.OpenAPISpec, method, path string, options *evaluatorParams, -) core.SDKEvaluator { +) sdk.Evaluator { t.Helper() if options == nil { @@ -284,18 +285,14 @@ func getEvaluator( logger = logrus.NewEntry(log) } - sdk, err := core.NewSDK( - ctx, - logger, - oas, - opaModule, - &core.EvaluatorOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, - options.registry, - "", - ) - require.NoError(t, err) + Registry: options.registry, + Logger: logger, + }) + require.NoError(t, err, "unexpected error") evaluator, err := sdk.FindEvaluator(logger, method, path) require.NoError(t, err) diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index d0f7501d..1e9a983f 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -22,12 +22,12 @@ import ( "net/http/httptest" "testing" - "github.com/mia-platform/glogger/v2" "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" "github.com/gorilla/mux" "github.com/sirupsen/logrus" @@ -92,7 +92,6 @@ func TestStatusRoutes(testCase *testing.T) { func TestStatusRoutesIntegration(t *testing.T) { log, _ := test.NewNullLogger() - ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) opa := &core.OPAModuleConfig{ Name: "policies", @@ -115,9 +114,13 @@ test_policy { true } var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := core.NewSDK(ctx, logrus.NewEntry(logger), oas, opa, &core.EvaluatorOptions{ - MongoClient: mongoClient, - }, registry, "") + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: mongoClient, + }, + Registry: registry, + Logger: logrus.NewEntry(logger), + }) require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { From 8ce2333ba3ece78c3cb3fbfbde3bf641f28acb87 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Fri, 14 Jul 2023 17:03:44 +0200 Subject: [PATCH 115/172] feat: new FromConfig SDK (#219) * feat: remove openapi package from core * feat: remove rondConfig params from evaluation policy function * test: add more openapi tests * feat: add NewWithConfig function in sdk * Update core/errors.go * Update openapi/openapi_utils.go --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/errors.go | 2 + core/opaevaluator.go | 241 +++---------- core/opaevaluator_test.go | 116 +++---- core/partialevaluators.go | 151 ++++++++ core/partialevaluators_test.go | 325 ++++++++++++++++++ core/print.go | 58 ++++ core/print_test.go | 35 ++ core/utils.go | 23 ++ core/utils_test.go | 39 +++ internal/fake/sdk.go | 7 +- main.go | 2 +- main_test.go | 24 +- .../example.rego | 2 +- openapi/evaluators.go | 38 ++ openapi/evaluators_test.go | 98 ++++++ openapi/openapi_utils.go | 75 ++-- openapi/openapi_utils_test.go | 205 +++++------ sdk/context_test.go | 8 +- sdk/evaluator.go | 9 +- sdk/evaluator_test.go | 10 +- sdk/openapi.go | 8 +- sdk/openapi_test.go | 18 +- sdk/sdk.go | 39 ++- sdk/sdk_test.go | 119 ++++++- service/handler.go | 6 +- service/handler_test.go | 46 +-- service/opa_transport_test.go | 2 +- service/opamiddleware.go | 2 +- service/opamiddleware_test.go | 16 +- service/router_test.go | 18 +- service/statusroutes_test.go | 2 +- 31 files changed, 1236 insertions(+), 508 deletions(-) create mode 100644 core/partialevaluators.go create mode 100644 core/partialevaluators_test.go create mode 100644 core/print.go create mode 100644 core/print_test.go create mode 100644 core/utils.go create mode 100644 core/utils_test.go create mode 100644 openapi/evaluators.go create mode 100644 openapi/evaluators_test.go diff --git a/core/errors.go b/core/errors.go index 21c63c3e..403af248 100644 --- a/core/errors.go +++ b/core/errors.go @@ -19,6 +19,7 @@ import "fmt" var ( ErrMissingRegoModules = fmt.Errorf("no rego module found in directory") ErrRegoModuleReadFailed = fmt.Errorf("failed rego file read") + ErrInvalidConfig = fmt.Errorf("invalid rond configuration") ErrEvaluatorCreationFailed = fmt.Errorf("error during evaluator creation") ErrEvaluatorNotFound = fmt.Errorf("evaluator not found") @@ -31,4 +32,5 @@ var ( ErrFailedInputEncode = fmt.Errorf("failed input encode") ErrFailedInputRequestParse = fmt.Errorf("failed request body parse") ErrFailedInputRequestDeserialization = fmt.Errorf("failed request body deserialization") + ErrRondConfigNotExists = fmt.Errorf("rond config does not exist") ) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 384445e0..463befff 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -16,9 +16,7 @@ package core import ( "context" - "encoding/json" "fmt" - "io" "os" "path/filepath" "strings" @@ -29,143 +27,54 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" - "github.com/open-policy-agent/opa/topdown/print" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson/primitive" ) -type Evaluator interface { - Eval(ctx context.Context) (rego.ResultSet, error) - Partial(ctx context.Context) (*rego.PartialQueries, error) +type RondConfig struct { + RequestFlow RequestFlow `json:"requestFlow"` + ResponseFlow ResponseFlow `json:"responseFlow"` + Options PermissionOptions `json:"options"` } -var Unknowns = []string{"data.resources"} - -type OPAEvaluator struct { - PolicyEvaluator Evaluator - PolicyName string - - context context.Context - mongoClient types.IMongoClient +type QueryOptions struct { + HeaderName string `json:"headerName"` } -type PartialResultsEvaluatorConfigKey struct{} -type PartialResultsEvaluators map[string]PartialEvaluator - -type PartialEvaluator struct { - PartialEvaluator *rego.PartialResult +type RequestFlow struct { + PolicyName string `json:"policyName"` + GenerateQuery bool `json:"generateQuery"` + QueryOptions QueryOptions `json:"queryOptions"` } -func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (*PartialEvaluator, error) { - logger.WithField("policyName", policy).Info("precomputing rego policy") - - policyEvaluatorTime := time.Now() - partialResultEvaluator, err := newPartialResultEvaluator(ctx, policy, opaModuleConfig, options) - if err != nil { - return nil, err - } - - logger. - WithFields(logrus.Fields{ - "policyName": policy, - "computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds, - }). - Info("precomputation time") - - return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil +type ResponseFlow struct { + PolicyName string `json:"policyName"` } -func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (PartialResultsEvaluators, error) { - if oas == nil { - return nil, fmt.Errorf("oas must not be nil") - } - - policyEvaluators := PartialResultsEvaluators{} - for path, OASContent := range oas.Paths { - for verb, verbConfig := range OASContent { - if verbConfig.PermissionV2 == nil { - continue - } - - allowPolicy := verbConfig.PermissionV2.RequestFlow.PolicyName - responsePolicy := verbConfig.PermissionV2.ResponseFlow.PolicyName - - logger. - WithFields(logrus.Fields{ - "verb": verb, - "policyName": allowPolicy, - "path": path, - "responsePolicyName": responsePolicy, - }). - Info("precomputing rego queries for API") - - if allowPolicy == "" { - // allow policy is required, if missing assume the API has no valid x-rond configuration. - continue - } - - if _, ok := policyEvaluators[allowPolicy]; !ok { - evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, oas, opaModuleConfig, options) - if err != nil { - return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) - } - - policyEvaluators[allowPolicy] = *evaluator - } - - if responsePolicy != "" { - if _, ok := policyEvaluators[responsePolicy]; !ok { - evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, oas, opaModuleConfig, options) - if err != nil { - return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) - } - - policyEvaluators[responsePolicy] = *evaluator - } - } - } - } - return policyEvaluators, nil +type PermissionOptions struct { + EnableResourcePermissionsMapOptimization bool `json:"enableResourcePermissionsMapOptimization"` + IgnoreTrailingSlash bool `json:"ignoreTrailingSlash,omitempty"` } -func NewPrintHook(w io.Writer, policy string) print.Hook { - return printHook{ - w: w, - policyName: policy, - } +type Evaluator interface { + Eval(ctx context.Context) (rego.ResultSet, error) + Partial(ctx context.Context) (*rego.PartialQueries, error) } -type printHook struct { - w io.Writer - policyName string -} +var Unknowns = []string{"data.resources"} -type LogPrinter struct { - Level int `json:"level"` - Message string `json:"msg"` - Time int64 `json:"time"` - PolicyName string `json:"policyName"` -} +type OPAEvaluator struct { + PolicyEvaluator Evaluator + PolicyName string -func (h printHook) Print(_ print.Context, message string) error { - structMessage := LogPrinter{ - Level: 10, - Message: message, - Time: time.Now().UnixNano() / 1000, - PolicyName: h.policyName, - } - msg, err := json.Marshal(structMessage) - if err != nil { - return err - } - _, err = fmt.Fprintln(h.w, string(msg)) - return err + context context.Context + mongoClient types.IMongoClient + generateQuery bool } type OPAEvaluatorOptions struct { @@ -173,7 +82,7 @@ type OPAEvaluatorOptions struct { MongoClient types.IMongoClient } -func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { +func newQueryOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { if options == nil { options = &OPAEvaluatorOptions{} } @@ -201,8 +110,9 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod PolicyEvaluator: query, PolicyName: policy, - context: ctx, - mongoClient: options.MongoClient, + context: ctx, + mongoClient: options.MongoClient, + generateQuery: true, }, nil } @@ -213,7 +123,7 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger }).Info("Policy to be evaluated") opaEvaluatorInstanceTime := time.Now() - evaluator, err := NewOPAEvaluator(ctx, policy, config, input, options) + evaluator, err := newQueryOPAEvaluator(ctx, policy, config, input, options) if err != nil { logger.WithError(err).Error(ErrEvaluatorCreationFailed) return nil, err @@ -224,65 +134,10 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger return evaluator, nil } -func newPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, evaluatorOptions *OPAEvaluatorOptions) (*rego.PartialResult, error) { - if evaluatorOptions == nil { - evaluatorOptions = &OPAEvaluatorOptions{} - } - if opaModuleConfig == nil { - return nil, fmt.Errorf("OPAModuleConfig must not be nil") - } - - sanitizedPolicy := strings.Replace(policy, ".", "_", -1) - queryString := fmt.Sprintf("data.policies.%s", sanitizedPolicy) - - options := []func(*rego.Rego){ - rego.Query(queryString), - rego.Module(opaModuleConfig.Name, opaModuleConfig.Content), - rego.Unknowns(Unknowns), - rego.EnablePrintStatements(evaluatorOptions.EnablePrintStatements), - rego.PrintHook(NewPrintHook(os.Stdout, policy)), - rego.Capabilities(ast.CapabilitiesForThisVersion()), - custom_builtins.GetHeaderFunction, - } - if evaluatorOptions.MongoClient != nil { - ctx = mongoclient.WithMongoClient(ctx, evaluatorOptions.MongoClient) - options = append(options, custom_builtins.MongoFindOne, custom_builtins.MongoFindMany) - } - regoInstance := rego.New(options...) - - results, err := regoInstance.PartialResult(ctx) - return &results, err -} - -func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { +func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (primitive.M, error) { if options == nil { - options = &OPAEvaluatorOptions{} - } - - if eval, ok := partialEvaluators[policy]; ok { - inputTerm, err := ast.ParseTerm(string(input)) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err) - } - - evaluator := eval.PartialEvaluator.Rego( - rego.ParsedInput(inputTerm.Value), - rego.EnablePrintStatements(options.EnablePrintStatements), - rego.PrintHook(NewPrintHook(os.Stdout, policy)), - ) - - return &OPAEvaluator{ - PolicyName: policy, - PolicyEvaluator: evaluator, - - context: ctx, - mongoClient: options.MongoClient, - }, nil + options = &PolicyEvaluationOptions{} } - return nil, fmt.Errorf("%w: %s", ErrEvaluatorNotFound, policy) -} - -func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (primitive.M, error) { opaEvaluationTimeStart := time.Now() partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.getContext()) if err != nil { @@ -295,15 +150,15 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options * "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) - logger.WithFields(logrus.Fields{ + fields := logrus.Fields{ "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": true, "allowed": true, - "matchedPath": options.RouterInfo.MatchedPath, - "requestedPath": options.RouterInfo.RequestedPath, - "method": options.RouterInfo.Method, - }).Debug("policy evaluation completed") + } + addDataToLogFields(fields, options.AdditionalLogFields) + + logger.WithFields(fields).Debug("policy evaluation completed") client := opatranslator.OPAClient{} q, err := client.ProcessQuery(partialResults) @@ -320,6 +175,10 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options * } func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (interface{}, error) { + if options == nil { + options = &PolicyEvaluationOptions{} + } + opaEvaluationTimeStart := time.Now() results, err := evaluator.PolicyEvaluator.Eval(evaluator.getContext()) @@ -333,16 +192,16 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEva }).Observe(float64(opaEvaluationTime.Milliseconds())) allowed, responseBodyOverwriter := processResults(results) - logger.WithFields(logrus.Fields{ + fields := logrus.Fields{ "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": false, "allowed": allowed, "resultsLength": len(results), - "matchedPath": options.RouterInfo.MatchedPath, - "requestedPath": options.RouterInfo.RequestedPath, - "method": options.RouterInfo.Method, - }).Debug("policy evaluation completed") + } + addDataToLogFields(fields, options.AdditionalLogFields) + + logger.WithFields(fields).Debug("policy evaluation completed") logger.WithFields(logrus.Fields{ "policyName": evaluator.PolicyName, @@ -367,8 +226,8 @@ func (evaluator *OPAEvaluator) getContext() context.Context { } type PolicyEvaluationOptions struct { - Metrics *metrics.Metrics - RouterInfo openapi.RouterInfo + Metrics *metrics.Metrics + AdditionalLogFields map[string]string } func (evaluator *PolicyEvaluationOptions) metrics() metrics.Metrics { @@ -378,8 +237,8 @@ func (evaluator *PolicyEvaluationOptions) metrics() metrics.Metrics { return metrics.SetupMetrics("rond") } -func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, permission *openapi.RondConfig, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { - if permission.RequestFlow.GenerateQuery { +func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { + if evaluator.generateQuery { query, err := evaluator.partiallyEvaluate(logger, options) return nil, query, err } diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 5bed8b78..56fa6dca 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -15,17 +15,15 @@ package core import ( - "bytes" "context" "encoding/json" "net/http" - "regexp" "testing" - "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/types" - "github.com/open-policy-agent/opa/topdown/print" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -35,7 +33,7 @@ func TestNewOPAEvaluator(t *testing.T) { input := map[string]interface{}{} inputBytes, _ := json.Marshal(input) t.Run("policy sanitization", func(t *testing.T) { - evaluator, _ := NewOPAEvaluator(context.Background(), "very.composed.policy", &OPAModuleConfig{Content: "package policies very_composed_policy {true}"}, inputBytes, nil) + evaluator, _ := newQueryOPAEvaluator(context.Background(), "very.composed.policy", &OPAModuleConfig{Content: "package policies very_composed_policy {true}"}, inputBytes, nil) result, err := evaluator.PolicyEvaluator.Eval(context.TODO()) require.Nil(t, err, "unexpected error") @@ -47,46 +45,45 @@ func TestNewOPAEvaluator(t *testing.T) { }) } -func TestCreatePolicyEvaluators(t *testing.T) { - t.Run("with simplified mock", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - ctx := context.Background() - - opaModuleDirectory := "../mocks/rego-policies" - loadOptions := openapi.LoadOptions{ - APIPermissionsFilePath: "../mocks/simplifiedMock.json", - } - openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, loadOptions) - require.NoError(t, err, "unexpected error") - - opaModuleConfig, err := LoadRegoModule(opaModuleDirectory) - require.NoError(t, err, "unexpected error") - - policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) - require.NoError(t, err, "unexpected error creating evaluators") - require.Len(t, policyEvals, 4, "unexpected length") - }) - - t.Run("with complete oas mock", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - ctx := context.Background() - - opaModulesDirectory := "../mocks/rego-policies" - - loadOptions := openapi.LoadOptions{ - APIPermissionsFilePath: "../mocks/pathsConfigAllInclusive.json", - } - openApiSpec, err := openapi.LoadOASFromFileOrNetwork(log, loadOptions) - require.NoError(t, err, "unexpected error") - - opaModuleConfig, err := LoadRegoModule(opaModulesDirectory) - require.NoError(t, err, "unexpected error") - - policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) - require.NoError(t, err, "unexpected error creating evaluators") - require.Len(t, policyEvals, 4, "unexpected length") +func TestOPAEvaluator(t *testing.T) { + t.Run("get context", func(t *testing.T) { + t.Run("no context and no mongo client", func(t *testing.T) { + opaEval := OPAEvaluator{} + ctx := opaEval.getContext() + + require.NotNil(t, ctx) + client, err := mongoclient.GetMongoClientFromContext(ctx) + require.NoError(t, err) + require.Nil(t, client) + }) + + t.Run("passed context with mongo client", func(t *testing.T) { + mongoClient := mocks.MongoClientMock{} + originalContext := mongoclient.WithMongoClient(context.Background(), mongoClient) + opaEval := OPAEvaluator{ + context: originalContext, + } + ctx := opaEval.getContext() + + require.NotNil(t, ctx) + client, err := mongoclient.GetMongoClientFromContext(ctx) + require.NoError(t, err) + require.Equal(t, mongoClient, client) + }) + + t.Run("passed mongo client", func(t *testing.T) { + mongoClient := mocks.MongoClientMock{} + opaEval := OPAEvaluator{ + context: context.Background(), + mongoClient: mongoClient, + } + ctx := opaEval.getContext() + + require.NotNil(t, ctx) + client, err := mongoclient.GetMongoClientFromContext(ctx) + require.NoError(t, err) + require.Equal(t, mongoClient, client) + }) }) } @@ -118,10 +115,12 @@ column_policy{ false } ` - permission := openapi.XPermission{ - AllowPermission: "allow", - ResponseFilter: openapi.ResponseFilterConfiguration{ - Policy: "column_policy", + permission := RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "allow", + }, + ResponseFlow: ResponseFlow{ + PolicyName: "column_policy", }, } @@ -134,29 +133,18 @@ column_policy{ inputBytes, _ := json.Marshal(input) t.Run("create evaluator with allowPolicy", func(t *testing.T) { - evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.AllowPermission, inputBytes, nil) + evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.RequestFlow.PolicyName, inputBytes, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) t.Run("create evaluator with policy for column filtering", func(t *testing.T) { - evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.ResponseFilter.Policy, inputBytes, nil) + evaluator, err := opaModuleConfig.CreateQueryEvaluator(context.Background(), logger, permission.ResponseFlow.PolicyName, inputBytes, nil) require.True(t, evaluator != nil) require.NoError(t, err, "Unexpected status code.") }) } -func TestPrint(t *testing.T) { - var buf bytes.Buffer - h := NewPrintHook(&buf, "policy-name") - - err := h.Print(print.Context{}, "the print message") - require.NoError(t, err) - - var re = regexp.MustCompile(`"time":\d+`) - require.JSONEq(t, `{"level":10,"msg":"the print message","time":123,"policyName":"policy-name"}`, string(re.ReplaceAll(buf.Bytes(), []byte("\"time\":123")))) -} - func TestGetHeaderFunction(t *testing.T) { headerKeyMocked := "exampleKey" headerValueMocked := "value" @@ -176,7 +164,7 @@ func TestGetHeaderFunction(t *testing.T) { } inputBytes, _ := json.Marshal(input) - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) + opaEvaluator, err := newQueryOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) @@ -195,7 +183,7 @@ func TestGetHeaderFunction(t *testing.T) { } inputBytes, _ := json.Marshal(input) - opaEvaluator, err := NewOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) + opaEvaluator, err := newQueryOPAEvaluator(context.Background(), queryString, opaModule, inputBytes, nil) require.NoError(t, err, "Unexpected error during creation of opaEvaluator") results, err := opaEvaluator.PolicyEvaluator.Eval(context.TODO()) diff --git a/core/partialevaluators.go b/core/partialevaluators.go new file mode 100644 index 00000000..3390dd9e --- /dev/null +++ b/core/partialevaluators.go @@ -0,0 +1,151 @@ +// Copyright 2023 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 core + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + "github.com/rond-authz/rond/custom_builtins" + "github.com/rond-authz/rond/internal/mongoclient" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/rego" + "github.com/sirupsen/logrus" +) + +type PartialResultsEvaluators map[string]PartialEvaluator + +type PartialEvaluator struct { + PartialEvaluator *rego.PartialResult +} + +func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (*PartialEvaluator, error) { + logger.WithField("policyName", policy).Info("precomputing rego policy") + + policyEvaluatorTime := time.Now() + partialResultEvaluator, err := newPartialResultEvaluator(ctx, policy, opaModuleConfig, options) + if err != nil { + return nil, err + } + + logger. + WithFields(logrus.Fields{ + "policyName": policy, + "computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds, + }). + Info("precomputation time") + + return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil +} + +func (policyEvaluators PartialResultsEvaluators) AddFromConfig(ctx context.Context, logger *logrus.Entry, opaModuleConfig *OPAModuleConfig, rondConfig *RondConfig, options *OPAEvaluatorOptions) error { + allowPolicy := rondConfig.RequestFlow.PolicyName + responsePolicy := rondConfig.ResponseFlow.PolicyName + + logger. + WithFields(logrus.Fields{ + "policyName": allowPolicy, + "responsePolicyName": responsePolicy, + }). + Info("precomputing rego queries") + + if allowPolicy == "" { + return fmt.Errorf("%w: allow policy is required", ErrInvalidConfig) + } + + if _, ok := policyEvaluators[allowPolicy]; !ok { + evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, opaModuleConfig, options) + if err != nil { + return fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) + } + + policyEvaluators[allowPolicy] = *evaluator + } + + if responsePolicy != "" { + if _, ok := policyEvaluators[responsePolicy]; !ok { + evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, opaModuleConfig, options) + if err != nil { + return fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error()) + } + + policyEvaluators[responsePolicy] = *evaluator + } + } + + return nil +} + +func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx context.Context, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { + if options == nil { + options = &OPAEvaluatorOptions{} + } + + if eval, ok := partialEvaluators[policy]; ok { + inputTerm, err := ast.ParseTerm(string(input)) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err) + } + + evaluator := eval.PartialEvaluator.Rego( + rego.ParsedInput(inputTerm.Value), + rego.EnablePrintStatements(options.EnablePrintStatements), + rego.PrintHook(NewPrintHook(os.Stdout, policy)), + ) + + return &OPAEvaluator{ + PolicyName: policy, + PolicyEvaluator: evaluator, + + context: ctx, + mongoClient: options.MongoClient, + }, nil + } + return nil, fmt.Errorf("%w: %s", ErrEvaluatorNotFound, policy) +} + +func newPartialResultEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, evaluatorOptions *OPAEvaluatorOptions) (*rego.PartialResult, error) { + if evaluatorOptions == nil { + evaluatorOptions = &OPAEvaluatorOptions{} + } + if opaModuleConfig == nil { + return nil, fmt.Errorf("OPAModuleConfig must not be nil") + } + + sanitizedPolicy := strings.Replace(policy, ".", "_", -1) + queryString := fmt.Sprintf("data.policies.%s", sanitizedPolicy) + + options := []func(*rego.Rego){ + rego.Query(queryString), + rego.Module(opaModuleConfig.Name, opaModuleConfig.Content), + rego.Unknowns(Unknowns), + rego.EnablePrintStatements(evaluatorOptions.EnablePrintStatements), + rego.PrintHook(NewPrintHook(os.Stdout, policy)), + rego.Capabilities(ast.CapabilitiesForThisVersion()), + custom_builtins.GetHeaderFunction, + } + if evaluatorOptions.MongoClient != nil { + ctx = mongoclient.WithMongoClient(ctx, evaluatorOptions.MongoClient) + options = append(options, custom_builtins.MongoFindOne, custom_builtins.MongoFindMany) + } + regoInstance := rego.New(options...) + + results, err := regoInstance.PartialResult(ctx) + return &results, err +} diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go new file mode 100644 index 00000000..e955fd78 --- /dev/null +++ b/core/partialevaluators_test.go @@ -0,0 +1,325 @@ +// Copyright 2023 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 core + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/internal/mocks" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestPartialResultEvaluators(t *testing.T) { + logger := logrus.NewEntry(logrus.New()) + + opaModule := &OPAModuleConfig{ + Content: `package policies + allow { + true + } + deny { + false + } + column_policy{ + false + } + `, + Name: "policies", + } + rondInput := Input{ + Request: InputRequest{}, + Response: InputResponse{}, + User: InputUser{}, + ClientType: "client-type", + } + + t.Run("throws if request policy is empty", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "", + }, + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, nil) + require.EqualError(t, err, fmt.Sprintf("%s: allow policy is required", ErrInvalidConfig)) + }) + + t.Run("throws if OpaModuleConfig is nil", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "not_exist", + }, + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, nil, rondConfig, nil) + require.EqualError(t, err, fmt.Sprintf("%s: OPAModuleConfig must not be nil", ErrEvaluatorCreationFailed)) + }) + + t.Run("correctly create partial evaluator", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "allow", + }, + ResponseFlow: ResponseFlow{ + PolicyName: "column_policy", + }, + } + ctx := context.Background() + + err := partialEvaluators.AddFromConfig(ctx, logger, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["allow"]) + require.NotNil(t, partialEvaluators["column_policy"]) + + t.Run("find and evaluate policy - request", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(ctx, "allow", input, nil) + require.NoError(t, err) + res, err := evaluator.Evaluate(logger, nil) + require.NoError(t, err) + require.Nil(t, res) + }) + + t.Run("find and evaluate policy - response fails", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(ctx, "column_policy", input, nil) + require.NoError(t, err) + _, err = evaluator.Evaluate(logger, nil) + require.EqualError(t, err, ErrPolicyEvalFailed.Error()) + }) + }) + + t.Run("correctly create with mongo client", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "allow_with_find_one", + }, + } + + opaModule, err := LoadRegoModule("../mocks/rego-policies-with-mongo-builtins") + require.NoError(t, err) + + evalOpts := OPAEvaluatorOptions{ + MongoClient: mocks.MongoClientMock{ + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "projects", collectionName) + require.Equal(t, map[string]interface{}{"projectId": "1234"}, query) + }, + FindOneResult: map[string]string{ + "tenantId": "some-tenant", + }, + }, + } + + err = partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["allow_with_find_one"]) + + rondInput := Input{ + Request: InputRequest{ + PathParams: map[string]string{ + "projectId": "1234", + }, + }, + Response: InputResponse{}, + User: InputUser{}, + ClientType: "client-type", + } + + t.Run("find and evaluate policy", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(context.Background(), "allow_with_find_one", input, &evalOpts) + require.NoError(t, err) + res, query, err := evaluator.PolicyEvaluation(logger, nil) + require.NoError(t, err) + require.Empty(t, query) + require.Empty(t, res) + }) + }) + + t.Run("correctly create with mongo client with query generation", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "filter_projects", + GenerateQuery: true, + }, + } + + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: ` + package policies + filter_projects { + field := input.user.properties.field + field == "1234" + myCollDoc := find_one("my-collection", {"myField": field}) + myCollDoc + + query := data.resources[_] + query.filterField == myCollDoc.filterField + } + `, + } + + evalOpts := OPAEvaluatorOptions{ + MongoClient: mocks.MongoClientMock{ + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + FindOneResult: map[string]string{ + "filterField": "something", + }, + }, + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["filter_projects"]) + + rondInput := Input{ + Request: InputRequest{}, + Response: InputResponse{}, + User: InputUser{ + Properties: map[string]interface{}{ + "field": "1234", + }, + }, + ClientType: "client-type", + } + + t.Run("find and evaluate policy", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := opaModule.CreateQueryEvaluator(context.Background(), logger, "filter_projects", input, &evalOpts) + require.NoError(t, err) + res, query, err := evaluator.PolicyEvaluation(logger, nil) + require.NoError(t, err) + require.Empty(t, res) + + var actualQuery = []byte{} + actualQuery, err = json.Marshal(query) + require.NoError(t, err) + require.JSONEq(t, `{"$or":[{"$and":[{"filterField":{"$eq":"something"}}]}]}`, string(actualQuery)) + }) + }) + + t.Run("with passed metrics", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "filter_projects", + GenerateQuery: true, + }, + } + + opaModule := &OPAModuleConfig{ + Name: "example.rego", + Content: ` + package policies + filter_projects { + field := input.user.properties.field + field == "1234" + myCollDoc := find_one("my-collection", {"myField": field}) + myCollDoc + + query := data.resources[_] + query.filterField == myCollDoc.filterField + } + `, + } + + evalOpts := OPAEvaluatorOptions{ + MongoClient: mocks.MongoClientMock{ + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + FindOneResult: map[string]string{ + "filterField": "something", + }, + }, + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["filter_projects"]) + + rondInput := Input{ + Request: InputRequest{}, + Response: InputResponse{}, + User: InputUser{ + Properties: map[string]interface{}{ + "field": "1234", + }, + }, + ClientType: "client-type", + } + + t.Run("find and evaluate policy", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := opaModule.CreateQueryEvaluator(context.Background(), logger, "filter_projects", input, &evalOpts) + require.NoError(t, err) + metrics := metrics.SetupMetrics("rond") + opts := PolicyEvaluationOptions{ + Metrics: &metrics, + } + res, query, err := evaluator.PolicyEvaluation(logger, &opts) + require.NoError(t, err) + require.Empty(t, res) + + var actualQuery = []byte{} + actualQuery, err = json.Marshal(query) + require.NoError(t, err) + require.JSONEq(t, `{"$or":[{"$and":[{"filterField":{"$eq":"something"}}]}]}`, string(actualQuery)) + }) + }) + + t.Run("evaluation fails", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "deny", + }, + } + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["deny"]) + + t.Run("find and evaluate policy - request", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(context.Background(), "deny", input, nil) + require.NoError(t, err) + _, _, err = evaluator.PolicyEvaluation(logger, nil) + require.EqualError(t, err, ErrPolicyEvalFailed.Error()) + }) + }) +} diff --git a/core/print.go b/core/print.go new file mode 100644 index 00000000..8a9dbb93 --- /dev/null +++ b/core/print.go @@ -0,0 +1,58 @@ +// Copyright 2023 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 core + +import ( + "encoding/json" + "fmt" + "io" + "time" + + "github.com/open-policy-agent/opa/topdown/print" +) + +func NewPrintHook(w io.Writer, policy string) print.Hook { + return printHook{ + w: w, + policyName: policy, + } +} + +type printHook struct { + w io.Writer + policyName string +} + +type LogPrinter struct { + Level int `json:"level"` + Message string `json:"msg"` + Time int64 `json:"time"` + PolicyName string `json:"policyName"` +} + +func (h printHook) Print(_ print.Context, message string) error { + structMessage := LogPrinter{ + Level: 10, + Message: message, + Time: time.Now().UnixNano() / 1000, + PolicyName: h.policyName, + } + msg, err := json.Marshal(structMessage) + if err != nil { + return err + } + _, err = fmt.Fprintln(h.w, string(msg)) + return err +} diff --git a/core/print_test.go b/core/print_test.go new file mode 100644 index 00000000..9cc90a08 --- /dev/null +++ b/core/print_test.go @@ -0,0 +1,35 @@ +// Copyright 2023 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 core + +import ( + "bytes" + "regexp" + "testing" + + "github.com/open-policy-agent/opa/topdown/print" + "github.com/stretchr/testify/require" +) + +func TestPrint(t *testing.T) { + var buf bytes.Buffer + h := NewPrintHook(&buf, "policy-name") + + err := h.Print(print.Context{}, "the print message") + require.NoError(t, err) + + var re = regexp.MustCompile(`"time":\d+`) + require.JSONEq(t, `{"level":10,"msg":"the print message","time":123,"policyName":"policy-name"}`, string(re.ReplaceAll(buf.Bytes(), []byte("\"time\":123")))) +} diff --git a/core/utils.go b/core/utils.go new file mode 100644 index 00000000..6164d6b9 --- /dev/null +++ b/core/utils.go @@ -0,0 +1,23 @@ +// Copyright 2023 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 core + +import "github.com/sirupsen/logrus" + +func addDataToLogFields(fields logrus.Fields, m map[string]string) { + for k, v := range m { + fields[k] = v + } +} diff --git a/core/utils_test.go b/core/utils_test.go new file mode 100644 index 00000000..c1b75c2c --- /dev/null +++ b/core/utils_test.go @@ -0,0 +1,39 @@ +// Copyright 2023 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 core + +import ( + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestAdditionalLogFields(t *testing.T) { + logFieldsToAdd := map[string]string{ + "foo": "bar", + } + + actual := logrus.Fields{ + "taz": "ok", + } + addDataToLogFields(actual, logFieldsToAdd) + + expected := logrus.Fields{ + "foo": "bar", + "taz": "ok", + } + require.Equal(t, expected, actual) +} diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go index 37230955..a48a09ba 100644 --- a/internal/fake/sdk.go +++ b/internal/fake/sdk.go @@ -18,7 +18,6 @@ import ( "context" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" ) @@ -29,14 +28,14 @@ type RequestPolicyEvaluatorResult struct { type SDKEvaluator struct { partialEvaluator core.PartialResultsEvaluators - permission openapi.RondConfig + permission core.RondConfig requestPolicyEvaluatorResult *RequestPolicyEvaluatorResult } func NewSDKEvaluator( partialEvaluator core.PartialResultsEvaluators, - permission openapi.RondConfig, + permission core.RondConfig, requestPolicyEvaluatorResult *RequestPolicyEvaluatorResult, ) sdk.Evaluator { return SDKEvaluator{ @@ -58,6 +57,6 @@ func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.Ron return nil, nil } -func (s SDKEvaluator) Config() openapi.RondConfig { +func (s SDKEvaluator) Config() core.RondConfig { return s.permission } diff --git a/main.go b/main.go index 91f7f9e4..8ea0a196 100644 --- a/main.go +++ b/main.go @@ -100,7 +100,7 @@ func entrypoint(shutdown chan os.Signal) { ) registry := prometheus.NewRegistry() - sdk, err := sdk.NewFromOAS(ctx, opaModuleConfig, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(ctx, opaModuleConfig, oas, &sdk.Options{ Registry: registry, EvaluatorOptions: &core.OPAEvaluatorOptions{ EnablePrintStatements: env.IsTraceLogLevel(), diff --git a/main_test.go b/main_test.go index 391479e2..b5cd3bef 100644 --- a/main_test.go +++ b/main_test.go @@ -1772,18 +1772,18 @@ filter_policy { Paths: openapi.OpenAPIPaths{ "/evalapi": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "test_policy"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "test_policy"}, }, }, }, "/evalfilter": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "filter_policy", GenerateQuery: true, - QueryOptions: openapi.QueryOptions{HeaderName: "my-query"}, + QueryOptions: core.QueryOptions{HeaderName: "my-query"}, }, }, }, @@ -1794,7 +1794,7 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, @@ -1934,18 +1934,18 @@ filter_policy { Paths: openapi.OpenAPIPaths{ "/evalapi": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "test_policy"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "test_policy"}, }, }, }, "/evalfilter": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "filter_policy", GenerateQuery: true, - QueryOptions: openapi.QueryOptions{HeaderName: "my-query"}, + QueryOptions: core.QueryOptions{HeaderName: "my-query"}, }, }, }, @@ -1956,7 +1956,7 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, diff --git a/mocks/rego-policies-with-mongo-builtins/example.rego b/mocks/rego-policies-with-mongo-builtins/example.rego index 2ff3c589..61c0b326 100644 --- a/mocks/rego-policies-with-mongo-builtins/example.rego +++ b/mocks/rego-policies-with-mongo-builtins/example.rego @@ -29,7 +29,7 @@ filter_projects { } projection_feature_toggle[res] { - ft_not_allowed := {x | + ft_not_allowed := {x | some key, val in input.response.body ft_checker with input.ft as key x = key diff --git a/openapi/evaluators.go b/openapi/evaluators.go new file mode 100644 index 00000000..8d559608 --- /dev/null +++ b/openapi/evaluators.go @@ -0,0 +1,38 @@ +package openapi + +import ( + "context" + "fmt" + + "github.com/rond-authz/rond/core" + + "github.com/sirupsen/logrus" +) + +func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *OpenAPISpec, opaModuleConfig *core.OPAModuleConfig, options *core.OPAEvaluatorOptions) (core.PartialResultsEvaluators, error) { + if oas == nil { + return nil, fmt.Errorf("oas must not be nil") + } + + policyEvaluators := core.PartialResultsEvaluators{} + for path, OASContent := range oas.Paths { + for verb, verbConfig := range OASContent { + if verbConfig.PermissionV2 == nil { + continue + } + + logger. + WithFields(logrus.Fields{ + "verb": verb, + "path": path, + }). + Info("precomputing rego evaluators for API") + + if err := policyEvaluators.AddFromConfig(ctx, logger, opaModuleConfig, verbConfig.PermissionV2, options); err != nil { + // allow policy is required, if missing assume the API has no valid x-rond configuration. + continue + } + } + } + return policyEvaluators, nil +} diff --git a/openapi/evaluators_test.go b/openapi/evaluators_test.go new file mode 100644 index 00000000..5e92a0e0 --- /dev/null +++ b/openapi/evaluators_test.go @@ -0,0 +1,98 @@ +package openapi + +import ( + "context" + "testing" + + "github.com/rond-authz/rond/core" + + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestCreatePolicyEvaluators(t *testing.T) { + t.Run("with simplified mock", func(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + ctx := context.Background() + + opaModuleDirectory := "../mocks/rego-policies" + loadOptions := LoadOptions{ + APIPermissionsFilePath: "../mocks/simplifiedMock.json", + } + openApiSpec, err := LoadOASFromFileOrNetwork(log, loadOptions) + require.NoError(t, err, "unexpected error") + + opaModuleConfig, err := core.LoadRegoModule(opaModuleDirectory) + require.NoError(t, err, "unexpected error") + + policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) + require.NoError(t, err, "unexpected error creating evaluators") + require.Len(t, policyEvals, 4, "unexpected length") + }) + + t.Run("with complete oas mock", func(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + ctx := context.Background() + + opaModulesDirectory := "../mocks/rego-policies" + + loadOptions := LoadOptions{ + APIPermissionsFilePath: "../mocks/pathsConfigAllInclusive.json", + } + openApiSpec, err := LoadOASFromFileOrNetwork(log, loadOptions) + require.NoError(t, err, "unexpected error") + + opaModuleConfig, err := core.LoadRegoModule(opaModulesDirectory) + require.NoError(t, err, "unexpected error") + + policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) + require.NoError(t, err, "unexpected error creating evaluators") + require.Len(t, policyEvals, 4, "unexpected length") + }) + + t.Run("with oas nil", func(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + ctx := context.Background() + + _, err := SetupEvaluators(ctx, logger, nil, nil, nil) + require.EqualError(t, err, "oas must not be nil") + }) + + t.Run("with complete oas mock", func(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + ctx := context.Background() + + opaModulesDirectory := "../mocks/rego-policies" + + openApiSpec := &OpenAPISpec{ + Paths: OpenAPIPaths{ + "/invalid-path": PathVerbs{ + "GET": VerbConfig{ + PermissionV2: &core.RondConfig{}, + }, + }, + "/path": PathVerbs{ + "GET": VerbConfig{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "allow", + }, + }, + }, + }, + }, + } + + opaModuleConfig, err := core.LoadRegoModule(opaModulesDirectory) + require.NoError(t, err, "unexpected error") + + policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) + require.NoError(t, err, "unexpected error creating evaluators") + require.Len(t, policyEvals, 1, "unexpected length") + }) +} diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index 689c1c29..c96ccb6e 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -27,6 +27,7 @@ import ( "strings" "time" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" "github.com/sirupsen/logrus" @@ -59,11 +60,6 @@ type RouterInfo struct { type XPermissionKey struct{} -type PermissionOptions struct { - EnableResourcePermissionsMapOptimization bool `json:"enableResourcePermissionsMapOptimization"` - IgnoreTrailingSlash bool `json:"ignoreTrailingSlash,omitempty"` -} - // Config v1 // type ResourceFilter struct { RowFilter RowFilterConfiguration `json:"rowFilter"` @@ -82,37 +78,14 @@ type XPermission struct { AllowPermission string `json:"allow"` ResponseFilter ResponseFilterConfiguration `json:"responseFilter"` ResourceFilter ResourceFilter `json:"resourceFilter"` - Options PermissionOptions `json:"options"` + Options core.PermissionOptions `json:"options"` } // END Config v1 // -// Config v2 // -type QueryOptions struct { - HeaderName string `json:"headerName"` -} - -type RequestFlow struct { - PolicyName string `json:"policyName"` - GenerateQuery bool `json:"generateQuery"` - QueryOptions QueryOptions `json:"queryOptions"` -} - -type ResponseFlow struct { - PolicyName string `json:"policyName"` -} - -type RondConfig struct { - RequestFlow RequestFlow `json:"requestFlow"` - ResponseFlow ResponseFlow `json:"responseFlow"` - Options PermissionOptions `json:"options"` -} - -// END Config v2 // - type VerbConfig struct { - PermissionV1 *XPermission `json:"x-permission"` - PermissionV2 *RondConfig `json:"x-rond"` + PermissionV1 *XPermission `json:"x-permission"` + PermissionV2 *core.RondConfig `json:"x-rond"` } type PathVerbs map[string]VerbConfig @@ -211,7 +184,7 @@ func registerLaxPath(OASRouter *bunrouter.CompatRouter, method string, methodCon } // FIXME: This is not a logic method of OAS, but could be a method of OASRouter -func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path string, method string) (RondConfig, RouterInfo, error) { +func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path string, method string) (core.RondConfig, RouterInfo, error) { routerInfo := RouterInfo{ Method: method, RequestedPath: path, @@ -221,12 +194,12 @@ func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path s responseReader := strings.NewReader("request-permissions") request, err := http.NewRequest(method, path, responseReader) if err != nil { - return RondConfig{}, routerInfo, err + return core.RondConfig{}, routerInfo, err } OASRouter.ServeHTTP(recorder, request) if recorder.Code != http.StatusOK { - return RondConfig{}, routerInfo, fmt.Errorf("%w: %s %s", ErrNotFoundOASDefinition, utils.SanitizeString(method), utils.SanitizeString(path)) + return core.RondConfig{}, routerInfo, fmt.Errorf("%w: %s %s", ErrNotFoundOASDefinition, utils.SanitizeString(method), utils.SanitizeString(path)) } recorderResult := recorder.Result() @@ -234,47 +207,47 @@ func (oas *OpenAPISpec) FindPermission(OASRouter *bunrouter.CompatRouter, path s routerInfo.MatchedPath = pathTemplate rowFilterEnabled, err := strconv.ParseBool(recorderResult.Header.Get("resourceFilter.rowFilter.enabled")) if err != nil { - return RondConfig{}, routerInfo, fmt.Errorf("error while parsing rowFilter.enabled: %s", err) + return core.RondConfig{}, routerInfo, fmt.Errorf("error while parsing rowFilter.enabled: %s", err) } enableResourcePermissionsMapOptimization, err := strconv.ParseBool(recorderResult.Header.Get("options.enableResourcePermissionsMapOptimization")) if err != nil { - return RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.enableResourcePermissionsMapOptimization: %s", err) + return core.RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.enableResourcePermissionsMapOptimization: %s", err) } ignoreTrailingSlash, err := strconv.ParseBool(recorderResult.Header.Get("options.ignoreTrailingSlash")) if err != nil { - return RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.ignoreTrailingSlash: %s", err) + return core.RondConfig{}, routerInfo, fmt.Errorf("error while parsing options.ignoreTrailingSlash: %s", err) } - return RondConfig{ - RequestFlow: RequestFlow{ + return core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: recorderResult.Header.Get("allow"), GenerateQuery: rowFilterEnabled, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: recorderResult.Header.Get("resourceFilter.rowFilter.headerKey"), }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: recorderResult.Header.Get("responseFilter.policy"), }, - Options: PermissionOptions{ + Options: core.PermissionOptions{ EnableResourcePermissionsMapOptimization: enableResourcePermissionsMapOptimization, IgnoreTrailingSlash: ignoreTrailingSlash, }, }, routerInfo, nil } -func newRondConfigFromPermissionV1(v1Permission *XPermission) *RondConfig { - return &RondConfig{ - RequestFlow: RequestFlow{ +func newRondConfigFromPermissionV1(v1Permission *XPermission) *core.RondConfig { + return &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: v1Permission.AllowPermission, GenerateQuery: v1Permission.ResourceFilter.RowFilter.Enabled, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: v1Permission.ResourceFilter.RowFilter.HeaderKey, }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: v1Permission.ResponseFilter.Policy, }, - Options: PermissionOptions{ + Options: core.PermissionOptions{ EnableResourcePermissionsMapOptimization: v1Permission.Options.EnableResourcePermissionsMapOptimization, IgnoreTrailingSlash: v1Permission.Options.IgnoreTrailingSlash, }, @@ -385,13 +358,13 @@ func LoadOASFromFileOrNetwork(log *logrus.Logger, config LoadOptions) (*OpenAPIS return nil, fmt.Errorf("missing openapi config: one of TargetServiceOASPath or APIPermissionsFilePath is required") } -func WithXPermission(requestContext context.Context, permission *RondConfig) context.Context { +func WithXPermission(requestContext context.Context, permission *core.RondConfig) context.Context { return context.WithValue(requestContext, XPermissionKey{}, permission) } // GetXPermission can be used by a request handler to get XPermission instance from its context. -func GetXPermission(requestContext context.Context) (*RondConfig, error) { - permission, ok := requestContext.Value(XPermissionKey{}).(*RondConfig) +func GetXPermission(requestContext context.Context) (*core.RondConfig, error) { + permission, ok := requestContext.Value(XPermissionKey{}).(*core.RondConfig) if !ok { return nil, fmt.Errorf("no permission configuration found in request context") } diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index f5276cf1..090a7c61 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -20,6 +20,7 @@ import ( "fmt" "testing" + "github.com/rond-authz/rond/core" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" @@ -46,25 +47,25 @@ func TestFetchOpenAPI(t *testing.T) { require.Equal(t, OpenAPIPaths{ "/users/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, "head": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, "post": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "notexistingpermission"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "notexistingpermission"}, }, }, }, "/composed/permission/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission"}, }, }, }, @@ -74,8 +75,8 @@ func TestFetchOpenAPI(t *testing.T) { }, "/eval/composed/permission/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}, }, }, }, @@ -140,17 +141,17 @@ func TestLoadOASFile(t *testing.T) { require.Equal(t, OpenAPIPaths{ "/users-from-static-file/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foobar", GenerateQuery: true, - QueryOptions: QueryOptions{HeaderName: "customHeaderKey"}, + QueryOptions: core.QueryOptions{HeaderName: "customHeaderKey"}, }, }, }, "post": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "notexistingpermission", }, }, @@ -185,17 +186,17 @@ func TestLoadOAS(t *testing.T) { require.Equal(t, OpenAPIPaths{ "/users-from-static-file/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foobar", GenerateQuery: true, - QueryOptions: QueryOptions{HeaderName: "customHeaderKey"}, + QueryOptions: core.QueryOptions{HeaderName: "customHeaderKey"}, }, }, }, "post": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "notexistingpermission", }, }, @@ -226,25 +227,25 @@ func TestLoadOAS(t *testing.T) { require.Equal(t, OpenAPIPaths{ "/users/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, "head": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, "post": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "notexistingpermission"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "notexistingpermission"}, }, }, }, "/composed/permission/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission"}, }, }, }, @@ -254,8 +255,8 @@ func TestLoadOAS(t *testing.T) { }, "/eval/composed/permission/": PathVerbs{ "get": VerbConfig{ - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}, }, }, }, @@ -292,7 +293,7 @@ func TestFindPermission(t *testing.T) { OASRouter, _ := oas.PrepareOASRouter() found, matchedPath, err := oas.FindPermission(OASRouter, "/not/existing/route", "/invalid-method") - require.Empty(t, RondConfig{}, found) + require.Empty(t, core.RondConfig{}, found) require.EqualError(t, err, "net/http: invalid method \"/invalid-method\"") require.Equal(t, RouterInfo{ Method: "/invalid-method", @@ -300,7 +301,7 @@ func TestFindPermission(t *testing.T) { }, matchedPath) found, matchedPath, err = oas.FindPermission(OASRouter, "/not/existing/route", "GET") - require.Empty(t, RondConfig{}, found) + require.Empty(t, core.RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: GET /not/existing/route", ErrNotFoundOASDefinition)) require.Equal(t, RouterInfo{ Method: "GET", @@ -308,7 +309,7 @@ func TestFindPermission(t *testing.T) { }, matchedPath) found, matchedPath, err = oas.FindPermission(OASRouter, "/no/method", "PUT") - require.Equal(t, RondConfig{}, found) + require.Equal(t, core.RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: PUT /no/method", ErrNotFoundOASDefinition)) require.Equal(t, RouterInfo{ Method: "PUT", @@ -316,7 +317,7 @@ func TestFindPermission(t *testing.T) { }, matchedPath) found, matchedPath, err = oas.FindPermission(OASRouter, "/use/method/that/not/existing/put", "PUT") - require.Equal(t, RondConfig{}, found) + require.Equal(t, core.RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: PUT /use/method/that/not/existing/put", ErrNotFoundOASDefinition)) require.Equal(t, RouterInfo{ Method: "PUT", @@ -325,11 +326,11 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/barId", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{ + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foo_bar_params", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "customHeaderKey", }, }, @@ -342,11 +343,11 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/barId/another-params-not-configured", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{ + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foo_bar", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "customHeaderKey", }, }, @@ -359,7 +360,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/nested/case/really/nested", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo_bar_nested_case"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/foo/bar/nested/case/*", RequestedPath: "/foo/bar/nested/case/really/nested", @@ -368,11 +369,11 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/bar/nested", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{ + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foo_bar_nested", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "customHeaderKey", }, }, @@ -385,11 +386,11 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/foo/simple", "PATCH") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{ + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "foo", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "customHeaderKey", }, }, @@ -401,7 +402,7 @@ func TestFindPermission(t *testing.T) { }, matchedPath) found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all", "GET") - require.Equal(t, RondConfig{}, found) + require.Equal(t, core.RondConfig{}, found) require.EqualError(t, err, fmt.Sprintf("%s: GET /test/all", ErrNotFoundOASDefinition)) require.Equal(t, RouterInfo{ Method: "GET", @@ -410,7 +411,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_get"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/", @@ -419,7 +420,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_get"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_get"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -428,7 +429,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "POST") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_post"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_post"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -437,7 +438,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "PUT") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_all"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -446,7 +447,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "PATCH") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_all"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -455,7 +456,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "DELETE") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_all"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -464,7 +465,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/test/all/verb", "HEAD") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "permission_for_all"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "permission_for_all"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/test/all/*", RequestedPath: "/test/all/verb", @@ -473,7 +474,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/projects/", "POST") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_all"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "project_all"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/projects/", RequestedPath: "/projects/", @@ -482,7 +483,7 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/projects/", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "project_get"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "project_get"}}, found) require.Equal(t, RouterInfo{ MatchedPath: "/projects/", RequestedPath: "/projects/", @@ -491,10 +492,10 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/with/trailing/slash/", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{PolicyName: "foo_bar"}, - ResponseFlow: ResponseFlow{PolicyName: "original_path"}, - Options: PermissionOptions{IgnoreTrailingSlash: true}, + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "foo_bar"}, + ResponseFlow: core.ResponseFlow{PolicyName: "original_path"}, + Options: core.PermissionOptions{IgnoreTrailingSlash: true}, }, found) require.Equal(t, RouterInfo{ MatchedPath: "/with/trailing/slash/", @@ -504,10 +505,10 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/with/trailing/slash", "GET") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{PolicyName: "foo_bar"}, - ResponseFlow: ResponseFlow{PolicyName: "original_path"}, - Options: PermissionOptions{IgnoreTrailingSlash: true}, + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "foo_bar"}, + ResponseFlow: core.ResponseFlow{PolicyName: "original_path"}, + Options: core.PermissionOptions{IgnoreTrailingSlash: true}, }, found) require.Equal(t, RouterInfo{ MatchedPath: "/with/trailing/slash/", @@ -517,9 +518,9 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/without/trailing/slash", "POST") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{PolicyName: "foo_bar"}, - Options: PermissionOptions{IgnoreTrailingSlash: true}, + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "foo_bar"}, + Options: core.PermissionOptions{IgnoreTrailingSlash: true}, }, found) require.Equal(t, RouterInfo{ MatchedPath: "/without/trailing/slash", @@ -529,9 +530,9 @@ func TestFindPermission(t *testing.T) { found, matchedPath, err = oas.FindPermission(OASRouter, "/without/trailing/slash/", "POST") require.NoError(t, err) - require.Equal(t, RondConfig{ - RequestFlow: RequestFlow{PolicyName: "foo_bar"}, - Options: PermissionOptions{IgnoreTrailingSlash: true}, + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "foo_bar"}, + Options: core.PermissionOptions{IgnoreTrailingSlash: true}, }, found) require.Equal(t, RouterInfo{ MatchedPath: "/without/trailing/slash", @@ -545,7 +546,7 @@ func TestFindPermission(t *testing.T) { OASRouter, _ := oas.PrepareOASRouter() found, matchedPath, err := oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%252Fcms-backend%252FcmsProperties.json", "POST") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "allow_commit"}}, found) require.NoError(t, err) require.Equal(t, RouterInfo{ MatchedPath: "/api/backend/projects/:projectId/branches/:branchName/files/:filePath", @@ -554,7 +555,7 @@ func TestFindPermission(t *testing.T) { }, matchedPath) found, matchedPath, err = oas.FindPermission(OASRouter, "/api/backend/projects/5df2260277baff0011fde823/branches/team-james/files/config-extension%2Fcms-backend%2FcmsProperties.json", "POST") - require.Equal(t, RondConfig{RequestFlow: RequestFlow{PolicyName: "allow_commit"}}, found) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "allow_commit"}}, found) require.NoError(t, err) require.Equal(t, RouterInfo{ MatchedPath: "/api/backend/projects/:projectId/branches/:branchName/files/:filePath", @@ -573,7 +574,7 @@ func TestGetXPermission(t *testing.T) { }) t.Run(`GetXPermission returns OPAEvaluator from context`, func(t *testing.T) { - ctx := context.WithValue(context.Background(), XPermissionKey{}, &RondConfig{RequestFlow: RequestFlow{PolicyName: "foo"}}) + ctx := context.WithValue(context.Background(), XPermissionKey{}, &core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo"}}) permission, err := GetXPermission(ctx) require.True(t, err == nil, "Unexpected error.") require.True(t, permission != nil, "XPermission not found.") @@ -613,15 +614,15 @@ func TestAdaptOASSpec(t *testing.T) { "/path-with-old-perm": PathVerbs{ "get": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "header", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res", }, }, @@ -677,30 +678,30 @@ func TestAdaptOASSpec(t *testing.T) { "/path-with-old-perm": PathVerbs{ "get": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "header", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res", }, }, }, "post": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req_post", GenerateQuery: false, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res_post", }, }, @@ -709,8 +710,8 @@ func TestAdaptOASSpec(t *testing.T) { "/path-with-old-perm-2": PathVerbs{ "patch": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req_patch", }, }, @@ -750,15 +751,15 @@ func TestAdaptOASSpec(t *testing.T) { Policy: "allow_res_post_OLD_CONF", }, }, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req_post", GenerateQuery: false, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res_post", }, }, @@ -778,30 +779,30 @@ func TestAdaptOASSpec(t *testing.T) { "/path-with-old-perm": PathVerbs{ "get": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req", GenerateQuery: true, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "header", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res", }, }, }, "post": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req_post", GenerateQuery: false, - QueryOptions: QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "", }, }, - ResponseFlow: ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "allow_res_post", }, }, @@ -810,8 +811,8 @@ func TestAdaptOASSpec(t *testing.T) { "/path-with-old-perm-2": PathVerbs{ "patch": VerbConfig{ PermissionV1: nil, - PermissionV2: &RondConfig{ - RequestFlow: RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow_req_patch", }, }, diff --git a/sdk/context_test.go b/sdk/context_test.go index d2454971..ca0f7368 100644 --- a/sdk/context_test.go +++ b/sdk/context_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/core" "github.com/stretchr/testify/require" ) @@ -26,12 +26,12 @@ import ( func TestContext(t *testing.T) { t.Run("ok", func(t *testing.T) { ctx := context.Background() - rondConfig := openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + rondConfig := core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "todo", GenerateQuery: true, }, - ResponseFlow: openapi.ResponseFlow{ + ResponseFlow: core.ResponseFlow{ PolicyName: "other", }, } diff --git a/sdk/evaluator.go b/sdk/evaluator.go index 5abf4bdd..f15bd32c 100644 --- a/sdk/evaluator.go +++ b/sdk/evaluator.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/sirupsen/logrus" @@ -35,7 +34,7 @@ type PolicyResult struct { // Do not use outside this repository until it is ready. type Evaluator interface { // retrieve the RondConfig used to generate the evaluator - Config() openapi.RondConfig + Config() core.RondConfig // EvaluateResponsePolicy evaluate request policy. In the response, it is specified if the // request is allowed and the request query (if filter generation is requested) @@ -48,7 +47,7 @@ type Evaluator interface { type evaluator struct { logger *logrus.Entry - rondConfig openapi.RondConfig + rondConfig core.RondConfig opaModuleConfig *core.OPAModuleConfig partialResultEvaluators core.PartialResultsEvaluators @@ -56,7 +55,7 @@ type evaluator struct { policyEvaluationOptions *core.PolicyEvaluationOptions } -func (e evaluator) Config() openapi.RondConfig { +func (e evaluator) Config() core.RondConfig { return e.rondConfig } @@ -92,7 +91,7 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req core.RondInput } } - _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, &rondConfig, e.policyEvaluationOptions) + _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, e.policyEvaluationOptions) if err != nil { e.logger.WithField("error", logrus.Fields{ diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 9e594ef6..8f6bd81d 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -311,7 +311,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { project := find_one("my-collection", {"myField": field}) query := data.resources[_] - query.filterField == "1234" + query.filterField == project.myField }`, expectedPolicy: PolicyResult{ Allowed: true, @@ -376,7 +376,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { fields := logrus.Fields{ "allowed": actual.Allowed, "requestedPath": testCase.path, - "matchedPath": evaluatorInfo.policyEvaluationOptions.RouterInfo.MatchedPath, + "matchedPath": evaluatorInfo.policyEvaluationOptions.AdditionalLogFields["matchedPath"], "method": testCase.method, "partialEval": evaluate.Config().RequestFlow.GenerateQuery, "policyName": evaluate.Config().RequestFlow.PolicyName, @@ -590,7 +590,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { require.Equal(t, logrus.Fields{ "allowed": !testCase.notAllowed, "requestedPath": testCase.path, - "matchedPath": evaluatorInfo.policyEvaluationOptions.RouterInfo.MatchedPath, + "matchedPath": evaluatorInfo.policyEvaluationOptions.AdditionalLogFields["matchedPath"], "method": testCase.method, "partialEval": false, "policyName": evaluate.Config().ResponseFlow.PolicyName, @@ -616,7 +616,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) - sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &FromOASOptions{ + sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: testmongoMock, }, @@ -673,7 +673,7 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if options.opaModuleContent != "" { opaModule.Content = options.opaModuleContent } - sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &FromOASOptions{ + sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{ Registry: options.registry, EvaluatorOptions: &core.OPAEvaluatorOptions{ EnablePrintStatements: true, diff --git a/sdk/openapi.go b/sdk/openapi.go index 46a6d857..39cd311e 100644 --- a/sdk/openapi.go +++ b/sdk/openapi.go @@ -46,8 +46,12 @@ func (r oasImpl) FindEvaluator(logger *logrus.Entry, method, path string) (Evalu opaEvaluatorOptions: r.opaEvaluatorOptions, policyEvaluationOptions: &core.PolicyEvaluationOptions{ - Metrics: r.metrics, - RouterInfo: routerInfo, + Metrics: r.metrics, + AdditionalLogFields: map[string]string{ + "matchedPath": routerInfo.MatchedPath, + "requestedPath": routerInfo.RequestedPath, + "method": routerInfo.Method, + }, }, }, err } diff --git a/sdk/openapi_test.go b/sdk/openapi_test.go index 1bdb169d..6d85472d 100644 --- a/sdk/openapi_test.go +++ b/sdk/openapi_test.go @@ -40,7 +40,7 @@ func TestOasSDK(t *testing.T) { very_very_composed_permission { true }`, } registry := prometheus.NewRegistry() - sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &FromOASOptions{ + sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{ Registry: registry, Logger: logger, }) @@ -61,15 +61,15 @@ func TestOasSDK(t *testing.T) { require.NoError(t, err) evaluatorOptions := &core.PolicyEvaluationOptions{ Metrics: oas.metrics, - RouterInfo: openapi.RouterInfo{ - MatchedPath: "/users/", - RequestedPath: "/users/", - Method: http.MethodGet, + AdditionalLogFields: map[string]string{ + "matchedPath": "/users/", + "requestedPath": "/users/", + "method": http.MethodGet, }, } require.Equal(t, evaluator{ - rondConfig: openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + rondConfig: core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "todo", }, }, @@ -80,8 +80,8 @@ func TestOasSDK(t *testing.T) { }, actual) t.Run("get permissions", func(t *testing.T) { - require.Equal(t, openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + require.Equal(t, core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "todo", }, }, actual.Config()) diff --git a/sdk/sdk.go b/sdk/sdk.go index 62ee962b..93b83aa6 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -26,22 +26,19 @@ import ( "github.com/sirupsen/logrus" ) -type FromOASOptions struct { +type Options struct { Registry *prometheus.Registry EvaluatorOptions *core.OPAEvaluatorOptions Logger *logrus.Entry } -// The SDK is now into core because there are coupled function here which should use the SDK itself -// (which uses core, so it will result in a cyclic dependency). In the future, sdk should be in a -// specific package. -func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, options *FromOASOptions) (OASEvaluatorFinder, error) { +func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, options *Options) (OASEvaluatorFinder, error) { if opaModuleConfig == nil { return nil, fmt.Errorf("OPAModuleConfig must not be nil") } if options == nil { - options = &FromOASOptions{} + options = &Options{} } if options.Logger == nil { // TODO: default to a fake silent logger instead of return error @@ -55,7 +52,7 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas metrics := setupMetrics(options.Registry) logger := options.Logger - evaluator, err := core.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) + evaluator, err := openapi.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) if err != nil { return nil, err } @@ -78,6 +75,34 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas }, nil } +func NewWithConfig(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, rondConfig core.RondConfig, options *Options) (Evaluator, error) { + if options == nil { + options = &Options{} + } + if options.Logger == nil { + // TODO: default to a fake silent logger instead of return error + return nil, fmt.Errorf("logger must be set in config options") + } + + policyEvaluators := core.PartialResultsEvaluators{} + if err := policyEvaluators.AddFromConfig(ctx, options.Logger, opaModuleConfig, &rondConfig, options.EvaluatorOptions); err != nil { + return nil, err + } + metrics := setupMetrics(options.Registry) + + return evaluator{ + rondConfig: rondConfig, + logger: options.Logger, + opaModuleConfig: opaModuleConfig, + partialResultEvaluators: policyEvaluators, + + opaEvaluatorOptions: options.EvaluatorOptions, + policyEvaluationOptions: &core.PolicyEvaluationOptions{ + Metrics: metrics, + }, + }, nil +} + func setupMetrics(registry *prometheus.Registry) *metrics.Metrics { m := metrics.SetupMetrics("rond") if registry != nil { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index d45a21f3..56026e95 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -16,6 +16,7 @@ package sdk import ( "context" + "fmt" "net/http" "testing" @@ -44,7 +45,7 @@ func TestNewFromOas(t *testing.T) { log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) - options := &FromOASOptions{ + options := &Options{ Logger: logger, } @@ -55,7 +56,7 @@ func TestNewFromOas(t *testing.T) { }) t.Run("throws if logger is nil", func(t *testing.T) { - sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{}) + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{}) require.EqualError(t, err, "logger is required inside options") require.Nil(t, sdk) }) @@ -82,7 +83,7 @@ func TestNewFromOas(t *testing.T) { t.Run("if registry is passed, setup metrics", func(t *testing.T) { registry := prometheus.NewRegistry() - sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{ + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{ Registry: registry, Logger: logger, }) @@ -94,7 +95,7 @@ func TestNewFromOas(t *testing.T) { evalOpts := &core.OPAEvaluatorOptions{ EnablePrintStatements: true, } - sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &FromOASOptions{ + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{ EvaluatorOptions: evalOpts, Logger: logger, Registry: prometheus.NewRegistry(), @@ -118,6 +119,116 @@ func TestNewFromOas(t *testing.T) { }) } +func TestNewWithConfig(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + allow { true } + projection_field { true } + `, + } + ctx := context.Background() + + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + options := &Options{ + Logger: logger, + } + + rondConfig := core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "allow"}, + ResponseFlow: core.ResponseFlow{PolicyName: "projection_field"}, + } + + t.Run("throws if logger not passed", func(t *testing.T) { + evaluator, err := NewWithConfig(ctx, opaModule, core.RondConfig{}, nil) + require.EqualError(t, err, "logger must be set in config options") + require.Nil(t, evaluator) + }) + + t.Run("throws if empty config", func(t *testing.T) { + evaluator, err := NewWithConfig(ctx, opaModule, core.RondConfig{}, options) + require.ErrorContains(t, err, core.ErrInvalidConfig.Error()) + require.Nil(t, evaluator) + }) + + t.Run("throws if opaModuleConfig is nil", func(t *testing.T) { + sdk, err := NewWithConfig(ctx, nil, rondConfig, options) + require.EqualError(t, err, fmt.Sprintf("%s: OPAModuleConfig must not be nil", core.ErrEvaluatorCreationFailed)) + require.Nil(t, sdk) + }) + + t.Run("passes EvaluatorOptions and set metrics correctly", func(t *testing.T) { + evalOpts := &core.OPAEvaluatorOptions{ + EnablePrintStatements: true, + } + eval, err := NewWithConfig(ctx, opaModule, rondConfig, &Options{ + EvaluatorOptions: evalOpts, + Logger: logger, + Registry: prometheus.NewRegistry(), + }) + require.NoError(t, err) + require.NotEmpty(t, eval) + r, ok := eval.(evaluator) + require.True(t, ok) + require.Equal(t, evalOpts, r.opaEvaluatorOptions) + }) + + t.Run("creates config sdk correctly", func(t *testing.T) { + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, options) + require.NoError(t, err) + require.NotNil(t, evaluator) + + t.Run("run evaluator correctly", func(t *testing.T) { + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, result) + }) + }) + + t.Run("creates config sdk correctly - using mongo functions", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + allow { + project := find_one("my-collection", {"myField": "1234"}) + project.myField == "1234" + } + projection_field { true } + `, + } + options := &Options{ + Logger: logger, + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: mocks.MongoClientMock{ + FindOneResult: map[string]string{"myField": "1234"}, + FindOneExpectation: func(collectionName string, query interface{}) { + require.Equal(t, "my-collection", collectionName) + require.Equal(t, map[string]interface{}{"myField": "1234"}, query) + }, + }, + }, + } + + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, options) + require.NoError(t, err) + require.NotNil(t, evaluator) + + t.Run("run evaluator correctly", func(t *testing.T) { + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, result) + }) + }) +} + type sdkOptions struct { opaModuleContent string oasFilePath string diff --git a/service/handler.go b/service/handler.go index ac31b21d..7cdb6f0b 100644 --- a/service/handler.go +++ b/service/handler.go @@ -19,11 +19,11 @@ import ( "net/http" "net/http/httputil" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" @@ -43,7 +43,7 @@ func ReverseProxyOrResponse( req *http.Request, evaluatorSdk sdk.Evaluator, ) { - var permission openapi.RondConfig + var permission core.RondConfig if evaluatorSdk != nil { permission = evaluatorSdk.Config() } @@ -147,7 +147,7 @@ func ReverseProxy( env config.EnvironmentVariables, w http.ResponseWriter, req *http.Request, - permission *openapi.RondConfig, + permission *core.RondConfig, evaluatorSdk sdk.Evaluator, ) { targetHostFromEnv := env.TargetServiceHost diff --git a/service/handler_test.go b/service/handler_test.go index 63fa2dc6..38cac1a5 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -45,11 +45,11 @@ import ( "github.com/stretchr/testify/require" ) -var mockRondConfigWithQueryGen = openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ +var mockRondConfigWithQueryGen = core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: openapi.QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "rowfilterquery", }, }, @@ -60,8 +60,8 @@ func TestDirectProxyHandler(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, }, @@ -72,11 +72,11 @@ func TestDirectProxyHandler(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: openapi.QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "rowfilterquery", }, }, @@ -217,7 +217,7 @@ func TestDirectProxyHandler(t *testing.T) { serverURL, _ := url.Parse(server.URL) - rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, @@ -849,8 +849,8 @@ func TestStandaloneMode(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, }, @@ -861,11 +861,11 @@ func TestStandaloneMode(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "allow", GenerateQuery: true, - QueryOptions: openapi.QueryOptions{ + QueryOptions: core.QueryOptions{ HeaderName: "rowfilterquery", }, }, @@ -1179,8 +1179,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, }, @@ -1215,7 +1215,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { count(input.request.headers["%s"]) != 0 }`, mockHeader), } - rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} evaluator := getEvaluator(t, ctx, opaModule, nil, rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), @@ -1386,15 +1386,15 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, }, }, } - rondConfig := &openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + rondConfig := &core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} evaluator := getEvaluator(t, ctx, opaModule, nil, *rondConfig, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), @@ -1945,13 +1945,13 @@ project := find_one("projects", {"projectId": "1234"}) project.tenantId == "1234" }`, } - var mockXPermission = openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} + var mockXPermission = core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{PolicyName: "todo"}, + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, }, }, }, diff --git a/service/opa_transport_test.go b/service/opa_transport_test.go index 730e5d1d..c22283ee 100644 --- a/service/opa_transport_test.go +++ b/service/opa_transport_test.go @@ -509,7 +509,7 @@ func getSdk(t require.TestingT, options *sdkOptions) sdk.OASEvaluatorFinder { opaModule.Content = options.opaModuleContent } - sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: options.mongoClient, EnablePrintStatements: true, diff --git a/service/opamiddleware.go b/service/opamiddleware.go index cce9dd96..6c9b66d0 100644 --- a/service/opamiddleware.go +++ b/service/opamiddleware.go @@ -56,7 +56,7 @@ func OPAMiddleware( logger := glogger.Get(r.Context()) evaluator, err := rondSDK.FindEvaluator(logger, r.Method, path) - rondConfig := openapi.RondConfig{} + rondConfig := core.RondConfig{} if err == nil { rondConfig = evaluator.Config() } diff --git a/service/opamiddleware_test.go b/service/opamiddleware_test.go index 910134c6..ecb1f02b 100644 --- a/service/opamiddleware_test.go +++ b/service/opamiddleware_test.go @@ -39,7 +39,7 @@ func TestOPAMiddleware(t *testing.T) { t.Helper() logger, _ := test.NewNullLogger() - sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ Logger: logrus.NewEntry(logger), }) require.NoError(t, err, "unexpected error") @@ -189,7 +189,7 @@ foobar { true }`, builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err, "Unexpected error") - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -211,7 +211,7 @@ foobar { true }`, builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -234,7 +234,7 @@ very_very_composed_permission { true }`, builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -262,7 +262,7 @@ very_very_composed_permission_with_eval { true }`, builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -304,7 +304,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { log, _ := test.NewNullLogger() logger := logrus.NewEntry(log) - sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.Options{ Logger: logger, }) require.NoError(t, err, "unexpected error") @@ -324,7 +324,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) @@ -347,7 +347,7 @@ very_very_composed_permission_with_eval { true }`, builtHandler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actual, err := sdk.GetEvaluator(r.Context()) require.NoError(t, err) - require.Equal(t, openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) + require.Equal(t, core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "very.very.composed.permission.with.eval"}}, actual.Config()) w.WriteHeader(http.StatusOK) })) diff --git a/service/router_test.go b/service/router_test.go index 9445b909..4c8bffbf 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -60,11 +60,11 @@ func TestSetupRoutes(t *testing.T) { "/-/rbac-check-up": openapi.PathVerbs{"get": openapi.VerbConfig{}}, "/with/trailing/slash": openapi.PathVerbs{ "get": openapi.VerbConfig{ - PermissionV2: &openapi.RondConfig{ - RequestFlow: openapi.RequestFlow{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{ PolicyName: "filter_policy", }, - Options: openapi.PermissionOptions{IgnoreTrailingSlash: true}, + Options: core.PermissionOptions{IgnoreTrailingSlash: true}, }, }, }, @@ -256,7 +256,7 @@ var mockOPAModule = &core.OPAModuleConfig{ Content: `package policies todo { true }`, } -var mockXPermission = openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "todo"}} +var mockXPermission = core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} type evaluatorParams struct { logger *logrus.Entry @@ -268,7 +268,7 @@ func getEvaluator( ctx context.Context, opaModule *core.OPAModuleConfig, mongoClient *mocks.MongoClientMock, - rondConfig openapi.RondConfig, + rondConfig core.RondConfig, oas *openapi.OpenAPISpec, method, path string, options *evaluatorParams, @@ -285,7 +285,7 @@ func getEvaluator( logger = logrus.NewEntry(log) } - sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, @@ -360,7 +360,7 @@ func TestSetupRoutesIntegration(t *testing.T) { router := mux.NewRouter() setupRoutes(router, oas, envs) - eval, err := core.SetupEvaluators(ctx, logger, oas, mockOPAModule, nil) + eval, err := openapi.SetupEvaluators(ctx, logger, oas, mockOPAModule, nil) require.NoError(t, err) evaluator := fake.NewSDKEvaluator(eval, mockXPermission, nil) @@ -446,7 +446,7 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") - rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "foo"}} + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo"}} var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies @@ -489,7 +489,7 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes a specific API within a nested path", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") - rondConfig := openapi.RondConfig{RequestFlow: openapi.RequestFlow{PolicyName: "foo"}} + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo"}} var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 1e9a983f..3626063c 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -114,7 +114,7 @@ test_policy { true } var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() - sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.FromOASOptions{ + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, From daa8ad16f10c6f6b829cf9923e607a7ad29a2fae Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Tue, 18 Jul 2023 12:49:00 +0200 Subject: [PATCH 116/172] feat: use rond logger instead of logrus (#220) * feat: use rond logger instead of logrus * feat: upgrade to use glogger v4 * remove logrus from sdk and core * rename logger in logging * now logger is no more required in sdk * remove old TODO * refactor: change import sorting * Update logging/logrus/logger_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * Update logging/test/logger_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * Update sdk/sdk_test.go Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> * update --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/input.go | 11 +- core/input_test.go | 19 +- core/opaevaluator.go | 33 +- core/opaevaluator_test.go | 24 +- core/partialevaluators.go | 13 +- core/partialevaluators_test.go | 43 ++- core/utils.go | 4 +- core/utils_test.go | 5 +- go.mod | 6 +- go.sum | 14 +- internal/mongoclient/mongoclient.go | 41 +-- internal/mongoclient/mongoclient_test.go | 39 ++- logging/logger.go | 62 ++++ logging/logrus/logger.go | 62 ++++ logging/logrus/logger_test.go | 137 +++++++++ logging/test/logger.go | 173 +++++++++++ logging/test/logger_test.go | 369 +++++++++++++++++++++++ main.go | 13 +- main_test.go | 6 +- openapi/evaluators.go | 21 +- openapi/evaluators_test.go | 33 +- openapi/openapi_utils.go | 16 +- openapi/openapi_utils_test.go | 7 +- sdk/evaluator.go | 7 +- sdk/evaluator_test.go | 45 ++- sdk/openapi.go | 6 +- sdk/openapi_test.go | 6 +- sdk/sdk.go | 22 +- sdk/sdk_test.go | 83 +++-- service/handler.go | 14 +- service/handler_test.go | 13 +- service/opa_transport.go | 3 +- service/opa_transport_test.go | 7 +- service/opamiddleware.go | 7 +- service/opamiddleware_test.go | 6 +- service/router.go | 13 +- service/router_test.go | 17 +- service/standalone_apis.go | 6 +- service/statusroutes.go | 7 +- service/statusroutes_test.go | 4 +- types/rbactypes.go | 2 + 41 files changed, 1171 insertions(+), 248 deletions(-) create mode 100644 logging/logger.go create mode 100644 logging/logrus/logger.go create mode 100644 logging/logrus/logger_test.go create mode 100644 logging/test/logger.go create mode 100644 logging/test/logger_test.go diff --git a/core/input.go b/core/input.go index 660d4e80..a2384fbf 100644 --- a/core/input.go +++ b/core/input.go @@ -21,9 +21,8 @@ import ( "net/url" "time" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - - "github.com/sirupsen/logrus" ) type Input struct { @@ -54,7 +53,7 @@ type InputUser struct { ResourcePermissionsMap PermissionsOnResourceMap `json:"resourcePermissionsMap,omitempty"` } -func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, enableResourcePermissionsMapOptimization bool) { +func (input *Input) buildOptimizedResourcePermissionsMap(logger logging.Logger, enableResourcePermissionsMapOptimization bool) { if !enableResourcePermissionsMapOptimization { return } @@ -85,7 +84,7 @@ func (input *Input) buildOptimizedResourcePermissionsMap(logger *logrus.Entry, e } } input.User.ResourcePermissionsMap = permissionsOnResourceMap - logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Tracef("resource permission map creation") + logger.WithField("resourcePermissionMapCreationTime", fmt.Sprintf("%+v", time.Since(opaPermissionsMapTime))).Trace("resource permission map creation") } type RegoInputOptions struct { @@ -93,7 +92,7 @@ type RegoInputOptions struct { } func CreateRegoQueryInput( - logger *logrus.Entry, + logger logging.Logger, input Input, options RegoInputOptions, ) ([]byte, error) { @@ -107,7 +106,7 @@ func CreateRegoQueryInput( } logger. WithField("inputCreationTimeMicroseconds", time.Since(opaInputCreationTime).Microseconds()). - Tracef("input creation time") + Trace("input creation time") return inputBytes, nil } diff --git a/core/input_test.go b/core/input_test.go index a20d6cbf..94968334 100644 --- a/core/input_test.go +++ b/core/input_test.go @@ -18,19 +18,17 @@ import ( "fmt" "testing" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) func TestCreateRegoInput(t *testing.T) { - logrusLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(logrusLogger) + log := logging.NewNoOpLogger() t.Run("returns correctly", func(t *testing.T) { - actual, err := CreateRegoQueryInput(logger, Input{}, RegoInputOptions{}) + actual, err := CreateRegoQueryInput(log, Input{}, RegoInputOptions{}) require.NoError(t, err) require.Equal(t, "{\"request\":{\"method\":\"\",\"path\":\"\"},\"response\":{},\"user\":{}}", string(actual)) }) @@ -79,7 +77,7 @@ func TestCreateRegoInput(t *testing.T) { User: user, } - input.buildOptimizedResourcePermissionsMap(logger, true) + input.buildOptimizedResourcePermissionsMap(log, true) expected := PermissionsOnResourceMap{ "permission1:type1:resource1": true, "permission2:type1:resource1": true, @@ -101,7 +99,7 @@ func TestCreateRegoInput(t *testing.T) { User: user, } - input.buildOptimizedResourcePermissionsMap(logger, false) + input.buildOptimizedResourcePermissionsMap(log, false) require.Nil(t, input.User.ResourcePermissionsMap) }) @@ -136,7 +134,7 @@ func TestCreateRegoInput(t *testing.T) { }, } - input.buildOptimizedResourcePermissionsMap(logger, true) + input.buildOptimizedResourcePermissionsMap(log, true) expected := PermissionsOnResourceMap{ "permission1:type1:resource1": true, "permission2:type1:resource1": true, @@ -185,7 +183,7 @@ func TestCreateRegoInput(t *testing.T) { }, } - input.buildOptimizedResourcePermissionsMap(logger, true) + input.buildOptimizedResourcePermissionsMap(log, true) expected := PermissionsOnResourceMap{ "permission3:type2:resource2": true, "permission3:type3:resource3": true, @@ -228,8 +226,7 @@ func BenchmarkBuildOptimizedResourcePermissionsMap(b *testing.B) { Bindings: bindings, } - logrusLogger, _ := test.NewNullLogger() - logger := logrus.NewEntry(logrusLogger) + logger := logging.NewNoOpLogger() input := Input{ User: user, } diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 463befff..b47de925 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -27,12 +27,12 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -75,11 +75,13 @@ type OPAEvaluator struct { context context.Context mongoClient types.IMongoClient generateQuery bool + logger logging.Logger } type OPAEvaluatorOptions struct { EnablePrintStatements bool MongoClient types.IMongoClient + Logger logging.Logger } func newQueryOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAModuleConfig, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { @@ -113,19 +115,19 @@ func newQueryOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *O context: ctx, mongoClient: options.MongoClient, generateQuery: true, + logger: options.Logger, }, nil } -func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger *logrus.Entry, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { - // TODO: remove logger and set in sdk - logger.WithFields(logrus.Fields{ +func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger logging.Logger, policy string, input []byte, options *OPAEvaluatorOptions) (*OPAEvaluator, error) { + logger.WithFields(map[string]any{ "policyName": policy, }).Info("Policy to be evaluated") opaEvaluatorInstanceTime := time.Now() evaluator, err := newQueryOPAEvaluator(ctx, policy, config, input, options) if err != nil { - logger.WithError(err).Error(ErrEvaluatorCreationFailed) + logger.WithField("error", err).Error(ErrEvaluatorCreationFailed) return nil, err } logger. @@ -134,7 +136,7 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger return evaluator, nil } -func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (primitive.M, error) { +func (evaluator *OPAEvaluator) partiallyEvaluate(logger logging.Logger, options *PolicyEvaluationOptions) (primitive.M, error) { if options == nil { options = &PolicyEvaluationOptions{} } @@ -150,7 +152,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options * "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) - fields := logrus.Fields{ + fields := map[string]any{ "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": true, @@ -166,15 +168,15 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry, options * return nil, err } - logger.WithFields(logrus.Fields{ + logger.WithFields(map[string]any{ "allowed": true, "query": q, - }).Tracef("policy results and query") + }).Trace("policy results and query") return q, nil } -func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEvaluationOptions) (interface{}, error) { +func (evaluator *OPAEvaluator) Evaluate(logger logging.Logger, options *PolicyEvaluationOptions) (interface{}, error) { if options == nil { options = &PolicyEvaluationOptions{} } @@ -192,7 +194,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEva }).Observe(float64(opaEvaluationTime.Milliseconds())) allowed, responseBodyOverwriter := processResults(results) - fields := logrus.Fields{ + fields := map[string]any{ "evaluationTimeMicroseconds": opaEvaluationTime.Microseconds(), "policyName": evaluator.PolicyName, "partialEval": false, @@ -203,7 +205,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry, options *PolicyEva logger.WithFields(fields).Debug("policy evaluation completed") - logger.WithFields(logrus.Fields{ + logger.WithFields(map[string]any{ "policyName": evaluator.PolicyName, "allowed": allowed, }).Info("policy result") @@ -220,7 +222,10 @@ func (evaluator *OPAEvaluator) getContext() context.Context { ctx = context.Background() } if evaluator.mongoClient != nil { - return mongoclient.WithMongoClient(ctx, evaluator.mongoClient) + ctx = mongoclient.WithMongoClient(ctx, evaluator.mongoClient) + } + if evaluator.logger != nil { + ctx = logging.WithContext(ctx, evaluator.logger) } return ctx } @@ -237,7 +242,7 @@ func (evaluator *PolicyEvaluationOptions) metrics() metrics.Metrics { return metrics.SetupMetrics("rond") } -func (evaluator *OPAEvaluator) PolicyEvaluation(logger *logrus.Entry, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { +func (evaluator *OPAEvaluator) PolicyEvaluation(logger logging.Logger, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { if evaluator.generateQuery { query, err := evaluator.partiallyEvaluate(logger, options) return nil, query, err diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 56fa6dca..39f9c965 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -22,10 +22,9 @@ import ( "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) @@ -47,7 +46,7 @@ func TestNewOPAEvaluator(t *testing.T) { func TestOPAEvaluator(t *testing.T) { t.Run("get context", func(t *testing.T) { - t.Run("no context and no mongo client", func(t *testing.T) { + t.Run("no context", func(t *testing.T) { opaEval := OPAEvaluator{} ctx := opaEval.getContext() @@ -55,6 +54,9 @@ func TestOPAEvaluator(t *testing.T) { client, err := mongoclient.GetMongoClientFromContext(ctx) require.NoError(t, err) require.Nil(t, client) + + logger := logging.FromContext(ctx) + require.NotNil(t, logger) }) t.Run("passed context with mongo client", func(t *testing.T) { @@ -84,6 +86,19 @@ func TestOPAEvaluator(t *testing.T) { require.NoError(t, err) require.Equal(t, mongoClient, client) }) + + t.Run("passed logger", func(t *testing.T) { + log := logging.NewNoOpLogger() + opaEval := OPAEvaluator{ + context: context.Background(), + logger: log, + } + ctx := opaEval.getContext() + + require.NotNil(t, ctx) + actualLog := logging.FromContext(ctx) + require.Equal(t, log, actualLog) + }) }) } @@ -126,8 +141,7 @@ column_policy{ opaModuleConfig := &OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() input := Input{Request: InputRequest{}, Response: InputResponse{}} inputBytes, _ := json.Marshal(input) diff --git a/core/partialevaluators.go b/core/partialevaluators.go index 3390dd9e..6b79ad34 100644 --- a/core/partialevaluators.go +++ b/core/partialevaluators.go @@ -23,10 +23,10 @@ import ( "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/logging" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" - "github.com/sirupsen/logrus" ) type PartialResultsEvaluators map[string]PartialEvaluator @@ -35,7 +35,7 @@ type PartialEvaluator struct { PartialEvaluator *rego.PartialResult } -func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (*PartialEvaluator, error) { +func createPartialEvaluator(ctx context.Context, logger logging.Logger, policy string, opaModuleConfig *OPAModuleConfig, options *OPAEvaluatorOptions) (*PartialEvaluator, error) { logger.WithField("policyName", policy).Info("precomputing rego policy") policyEvaluatorTime := time.Now() @@ -45,7 +45,7 @@ func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy st } logger. - WithFields(logrus.Fields{ + WithFields(map[string]any{ "policyName": policy, "computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds, }). @@ -54,12 +54,12 @@ func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy st return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil } -func (policyEvaluators PartialResultsEvaluators) AddFromConfig(ctx context.Context, logger *logrus.Entry, opaModuleConfig *OPAModuleConfig, rondConfig *RondConfig, options *OPAEvaluatorOptions) error { +func (policyEvaluators PartialResultsEvaluators) AddFromConfig(ctx context.Context, logger logging.Logger, opaModuleConfig *OPAModuleConfig, rondConfig *RondConfig, options *OPAEvaluatorOptions) error { allowPolicy := rondConfig.RequestFlow.PolicyName responsePolicy := rondConfig.ResponseFlow.PolicyName logger. - WithFields(logrus.Fields{ + WithFields(map[string]any{ "policyName": allowPolicy, "responsePolicyName": responsePolicy, }). @@ -144,6 +144,9 @@ func newPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf ctx = mongoclient.WithMongoClient(ctx, evaluatorOptions.MongoClient) options = append(options, custom_builtins.MongoFindOne, custom_builtins.MongoFindMany) } + if evaluatorOptions.Logger != nil { + ctx = logging.WithContext(ctx, evaluatorOptions.Logger) + } regoInstance := rego.New(options...) results, err := regoInstance.PartialResult(ctx) diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go index e955fd78..2e5fe9ec 100644 --- a/core/partialevaluators_test.go +++ b/core/partialevaluators_test.go @@ -22,13 +22,13 @@ import ( "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/logging" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" ) func TestPartialResultEvaluators(t *testing.T) { - logger := logrus.NewEntry(logrus.New()) + logger := logging.NewNoOpLogger() opaModule := &OPAModuleConfig{ Content: `package policies @@ -162,6 +162,45 @@ func TestPartialResultEvaluators(t *testing.T) { }) }) + t.Run("correctly create with logger", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "allow", + }, + } + + evalOpts := OPAEvaluatorOptions{ + Logger: logger, + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["allow"]) + + rondInput := Input{ + Request: InputRequest{ + PathParams: map[string]string{ + "projectId": "1234", + }, + }, + Response: InputResponse{}, + User: InputUser{}, + ClientType: "client-type", + } + + t.Run("find and evaluate policy", func(t *testing.T) { + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(context.Background(), "allow", input, &evalOpts) + require.NoError(t, err) + res, query, err := evaluator.PolicyEvaluation(logger, nil) + require.NoError(t, err) + require.Empty(t, query) + require.Empty(t, res) + }) + }) + t.Run("correctly create with mongo client with query generation", func(t *testing.T) { partialEvaluators := PartialResultsEvaluators{} rondConfig := &RondConfig{ diff --git a/core/utils.go b/core/utils.go index 6164d6b9..1709341f 100644 --- a/core/utils.go +++ b/core/utils.go @@ -14,9 +14,7 @@ package core -import "github.com/sirupsen/logrus" - -func addDataToLogFields(fields logrus.Fields, m map[string]string) { +func addDataToLogFields(fields map[string]any, m map[string]string) { for k, v := range m { fields[k] = v } diff --git a/core/utils_test.go b/core/utils_test.go index c1b75c2c..6888b6d1 100644 --- a/core/utils_test.go +++ b/core/utils_test.go @@ -17,7 +17,6 @@ package core import ( "testing" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" ) @@ -26,12 +25,12 @@ func TestAdditionalLogFields(t *testing.T) { "foo": "bar", } - actual := logrus.Fields{ + actual := map[string]any{ "taz": "ok", } addDataToLogFields(actual, logFieldsToAdd) - expected := logrus.Fields{ + expected := map[string]any{ "foo": "bar", "taz": "ok", } diff --git a/go.mod b/go.mod index 9b5e069b..2ec92684 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 - github.com/mia-platform/glogger/v2 v2.1.3 + github.com/mia-platform/glogger/v4 v4.0.0 github.com/mia-platform/go-crud-service-client v0.10.0 github.com/open-policy-agent/opa v0.54.0 github.com/prometheus/client_golang v1.16.0 @@ -43,7 +43,7 @@ require ( github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.16.3 // indirect + github.com/klauspost/compress v1.16.5 // indirect github.com/knadh/koanf v1.4.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -82,7 +82,7 @@ require ( golang.org/x/crypto v0.10.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect golang.org/x/text v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index da69e2a2..be460c84 100644 --- a/go.sum +++ b/go.sum @@ -311,8 +311,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/knadh/koanf v0.6.0/go.mod h1:HGb//qrjEExYwYqqoxit9S1rvFU+YCx1jz3GtcdZEy8= github.com/knadh/koanf v1.4.3 h1:rSJcSH5LSFhvzBRsAYfT3k7eLP0I4UxeZqjtAatk+wc= github.com/knadh/koanf v1.4.3/go.mod h1:5FAkuykKXZvLqhAbP4peWgM5CTcZmn7L1d27k/a+kfg= @@ -346,8 +346,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjKD5OWJ1s= github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= -github.com/mia-platform/glogger/v2 v2.1.3 h1:Qt/qHETYaFa+Oso9XZauysnHF9CE5816AQJmMo2Uh0Q= -github.com/mia-platform/glogger/v2 v2.1.3/go.mod h1:dUYhwmsXVcZe5Eg8V4FIcdv5P4pfk9a/8JExTmi8Uig= +github.com/mia-platform/glogger/v4 v4.0.0 h1:vap3r+0RRHsZkefVqanBz6H381FiD3rtg/j+xuNcRac= +github.com/mia-platform/glogger/v4 v4.0.0/go.mod h1:TqVXSfa4wuAaLoe0ye8D6hsPksClgs7p3Y1dC2oRxoQ= github.com/mia-platform/go-crud-service-client v0.10.0 h1:BUFKQVxfdcnI82cXNIU8Zp2mCI9d7JJS4eUrTUHfxHc= github.com/mia-platform/go-crud-service-client v0.10.0/go.mod h1:Sj3BEGMu0RTdeye/fPBVwRoojOjKuhWl5j+psY3s40s= github.com/mia-platform/jsonschema v0.1.0 h1:tjQf7TaYROsAqk7SXTL+44TrfKk3bSEvhRGPS51IA5Y= @@ -454,7 +454,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -722,7 +721,6 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -739,8 +737,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/mongoclient/mongoclient.go b/internal/mongoclient/mongoclient.go index 7f6cbc4a..96c35a83 100644 --- a/internal/mongoclient/mongoclient.go +++ b/internal/mongoclient/mongoclient.go @@ -23,13 +23,12 @@ import ( "strings" "time" - "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" + "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -86,7 +85,7 @@ func (mongoClient *MongoClient) Disconnect() error { // NewMongoClient tries to setup a new MongoClient instance. // The function returns a `nil` client if the environment variable `MongoDBUrl` is not specified. -func NewMongoClient(env config.EnvironmentVariables, logger *logrus.Logger) (*MongoClient, error) { +func NewMongoClient(env config.EnvironmentVariables, logger logging.Logger) (*MongoClient, error) { if env.MongoDBUrl == "" { logger.Info("No MongoDB configuration provided, skipping setup") return nil, nil @@ -198,7 +197,8 @@ func (mongoClient *MongoClient) RetrieveUserRolesByRolesID(ctx context.Context, func (mongoClient *MongoClient) FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) { collection := mongoClient.client.Database(mongoClient.databaseName).Collection(collectionName) - glogger.Get(ctx).WithFields(logrus.Fields{ + log := logging.FromContext(ctx) + log.WithFields(map[string]any{ "mongoQuery": query, "dbName": mongoClient.databaseName, "collectionName": collectionName, @@ -210,22 +210,22 @@ func (mongoClient *MongoClient) FindOne(ctx context.Context, collectionName stri err := result.Decode(&bsonDocument) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Warn("no document found") + log.WithField("error", map[string]any{"message": err.Error()}).Warn("no document found") return nil, nil } - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Error("failed query decode") + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query decode") return nil, err } temporaryBytes, err := bson.MarshalExtJSON(bsonDocument, true, true) if err != nil { - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Error("failed query result marshalling") + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result marshalling") return nil, err } var res map[string]interface{} if err := json.Unmarshal(temporaryBytes, &res); err != nil { - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Error("failed query result deserialization") + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result deserialization") return nil, err } return res, nil @@ -233,7 +233,8 @@ func (mongoClient *MongoClient) FindOne(ctx context.Context, collectionName stri func (mongoClient *MongoClient) FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) { collection := mongoClient.client.Database(mongoClient.databaseName).Collection(collectionName) - glogger.Get(ctx).WithFields(logrus.Fields{ + log := logging.FromContext(ctx) + log.WithFields(map[string]any{ "mongoQuery": query, "dbName": mongoClient.databaseName, "collectionName": collectionName, @@ -241,28 +242,28 @@ func (mongoClient *MongoClient) FindMany(ctx context.Context, collectionName str resultCursor, err := collection.Find(ctx, query) if err != nil { - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Error("failed query execution") + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query execution") return nil, err } results := make([]interface{}, 0) if err := resultCursor.All(ctx, &results); err != nil { - glogger.Get(ctx).WithField("error", logrus.Fields{"message": err.Error()}).Error("failed complete query result deserialization") + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed complete query result deserialization") return nil, err } for i := 0; i < len(results); i++ { temporaryBytes, err := bson.MarshalExtJSON(results[i], true, true) if err != nil { - glogger.Get(ctx).WithFields(logrus.Fields{ - "error": logrus.Fields{"message": err.Error()}, + log.WithFields(map[string]any{ + "error": map[string]any{"message": err.Error()}, "resultIndex": i, }).Error("failed query result marshalling") return nil, err } if err := json.Unmarshal(temporaryBytes, &results[i]); err != nil { - glogger.Get(ctx).WithFields(logrus.Fields{ - "error": logrus.Fields{"message": err.Error()}, + log.WithFields(map[string]any{ + "error": map[string]any{"message": err.Error()}, "resultIndex": i, }).Error("failed result document deserialization") return nil, err @@ -283,7 +284,7 @@ func RolesIDsFromBindings(bindings []types.Binding) []string { return rolesIds } -func RetrieveUserBindingsAndRoles(logger *logrus.Entry, req *http.Request, userHeaders types.UserHeadersKeys) (types.User, error) { +func RetrieveUserBindingsAndRoles(logger logging.Logger, req *http.Request, userHeaders types.UserHeadersKeys) (types.User, error) { requestContext := req.Context() mongoClient, err := GetMongoClientFromContext(requestContext) if err != nil { @@ -305,18 +306,18 @@ func RetrieveUserBindingsAndRoles(logger *logrus.Entry, req *http.Request, userH if mongoClient != nil && user.UserID != "" { user.UserBindings, err = mongoClient.RetrieveUserBindings(requestContext, &user) if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("something went wrong while retrieving user bindings") + logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user bindings") return types.User{}, fmt.Errorf("error while retrieving user bindings: %s", err.Error()) } userRolesIds := RolesIDsFromBindings(user.UserBindings) user.UserRoles, err = mongoClient.RetrieveUserRolesByRolesID(requestContext, userRolesIds) if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("something went wrong while retrieving user roles") + logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user roles") return types.User{}, fmt.Errorf("error while retrieving user Roles: %s", err.Error()) } - logger.WithFields(logrus.Fields{ + logger.WithFields(map[string]any{ "foundBindingsLength": len(user.UserBindings), "foundRolesLength": len(user.UserRoles), }).Trace("found bindings and roles") diff --git a/internal/mongoclient/mongoclient_test.go b/internal/mongoclient/mongoclient_test.go index cb9c956f..c3e364be 100644 --- a/internal/mongoclient/mongoclient_test.go +++ b/internal/mongoclient/mongoclient_test.go @@ -26,9 +26,8 @@ import ( "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/testutils" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) @@ -79,7 +78,7 @@ func TestGetMongoCollectionFromContext(t *testing.T) { func TestSetupMongoCollection(t *testing.T) { t.Run("if MongoDBUrl empty, returns nil", func(t *testing.T) { env := config.EnvironmentVariables{} - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() adapter, _ := NewMongoClient(env, log) require.True(t, adapter == nil, "MongoDBUrl is not nil") }) @@ -89,7 +88,7 @@ func TestSetupMongoCollection(t *testing.T) { MongoDBUrl: "MONGODB_URL", BindingsCollectionName: "Some different name", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() adapter, err := NewMongoClient(env, log) require.True(t, adapter == nil, "RolesCollectionName collection is not nil") require.Contains(t, err.Error(), `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "Some different name", RolesCollectionName: ""`) @@ -103,7 +102,7 @@ func TestSetupMongoCollection(t *testing.T) { RolesCollectionName: "something new", BindingsCollectionName: "Some different name", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() adapter, err := NewMongoClient(env, log) require.True(t, err != nil, "setup mongo not returns error") require.Contains(t, err.Error(), "failed MongoDB connection string validation:") @@ -123,7 +122,7 @@ func TestSetupMongoCollection(t *testing.T) { BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() @@ -146,7 +145,7 @@ func TestMongoCollections(t *testing.T) { BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") @@ -234,7 +233,7 @@ func TestMongoCollections(t *testing.T) { BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") @@ -284,7 +283,7 @@ func TestMongoCollections(t *testing.T) { BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") @@ -329,7 +328,7 @@ func TestMongoFindOne(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") @@ -387,7 +386,7 @@ func TestMongoFindMany(t *testing.T) { RolesCollectionName: "roles", BindingsCollectionName: "bindings", } - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") @@ -472,7 +471,7 @@ func TestRolesIDSFromBindings(t *testing.T) { } func TestRetrieveUserBindingsAndRoles(t *testing.T) { - logger, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() userHeaders := types.UserHeadersKeys{ GroupsHeaderKey: "thegroupsheader", IDHeaderKey: "theuserheader", @@ -483,7 +482,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(context.WithValue(req.Context(), types.MongoClientContextKey{}, "test")) - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, userHeaders) + _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.Error(t, err, "Unexpected error retrieving MongoDB Client from request context") }) @@ -492,7 +491,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logger), req, userHeaders) + user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.NoError(t, err) require.Equal(t, types.User{ UserID: "userId", @@ -508,7 +507,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(WithMongoClient(req.Context(), mock)) - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.NoError(t, err) }) @@ -521,7 +520,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.Error(t, err, "Error while retrieving user bindings: some error") }) @@ -537,7 +536,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.Error(t, err, "Error while retrieving user Roles: some error 2") }) @@ -558,7 +557,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.NoError(t, err) require.Equal(t, types.User{ UserID: "userId", @@ -582,7 +581,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req.Header.Set("thegroupsheader", "group1,group2") req.Header.Set("theuserheader", "userId") - user, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.NoError(t, err) require.Equal(t, types.User{ UserID: "userId", @@ -595,7 +594,7 @@ func TestRetrieveUserBindingsAndRoles(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.Header.Set("userproperties", "1") - _, err := RetrieveUserBindingsAndRoles(logrus.NewEntry(logrus.New()), req, userHeaders) + _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) require.ErrorContains(t, err, "user properties header is not valid:") }) } diff --git a/logging/logger.go b/logging/logger.go new file mode 100644 index 00000000..5e5bed94 --- /dev/null +++ b/logging/logger.go @@ -0,0 +1,62 @@ +// Copyright 2023 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 logging + +import "context" + +type Logger interface { + WithFields(fields map[string]any) Logger + WithField(field string, value any) Logger + + Error(msg any) + Warn(msg any) + Info(msg any) + Debug(msg any) + Trace(msg any) +} + +type noopLogger struct{} + +func (l *noopLogger) WithFields(fields map[string]any) Logger { + return l +} + +func (l *noopLogger) WithField(field string, value any) Logger { + return l +} + +func (l noopLogger) Error(msg any) {} +func (l noopLogger) Info(msg any) {} +func (l noopLogger) Debug(msg any) {} +func (l noopLogger) Trace(msg any) {} +func (l noopLogger) Warn(msg any) {} + +func NewNoOpLogger() Logger { + return &noopLogger{} +} + +type loggerKey struct{} + +func WithContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, loggerKey{}, logger) +} + +func FromContext(ctx context.Context) Logger { + logger, ok := ctx.Value(loggerKey{}).(Logger) + if !ok { + return &noopLogger{} + } + return logger +} diff --git a/logging/logrus/logger.go b/logging/logrus/logger.go new file mode 100644 index 00000000..48e57014 --- /dev/null +++ b/logging/logrus/logger.go @@ -0,0 +1,62 @@ +// Copyright 2023 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 rondlogrus + +import ( + "github.com/rond-authz/rond/logging" + "github.com/sirupsen/logrus" +) + +type logrusEntryWrapper struct { + *logrus.Entry +} + +func (l *logrusEntryWrapper) WithField(key string, value any) logging.Logger { + entry := l.Logger.WithField(key, value) + return &logrusEntryWrapper{entry} +} + +func (l *logrusEntryWrapper) WithFields(fields map[string]any) logging.Logger { + entry := l.Logger.WithFields(fields) + return &logrusEntryWrapper{entry} +} + +func (l logrusEntryWrapper) Info(msg any) { + l.Entry.Info(msg) +} + +func (l logrusEntryWrapper) Trace(msg any) { + l.Entry.Trace(msg) +} + +func (l logrusEntryWrapper) Debug(msg any) { + l.Entry.Debug(msg) +} + +func (l logrusEntryWrapper) Error(msg any) { + l.Entry.Error(msg) +} + +func (l logrusEntryWrapper) Warn(msg any) { + l.Entry.Warn(msg) +} + +func NewLogger(logger *logrus.Logger) logging.Logger { + return &logrusEntryWrapper{logrus.NewEntry(logger)} +} + +func NewEntry(entry *logrus.Entry) logging.Logger { + return &logrusEntryWrapper{entry} +} diff --git a/logging/logrus/logger_test.go b/logging/logrus/logger_test.go new file mode 100644 index 00000000..be6fc952 --- /dev/null +++ b/logging/logrus/logger_test.go @@ -0,0 +1,137 @@ +// Copyright 2023 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 rondlogrus + +import ( + "testing" + + "github.com/rond-authz/rond/logging" + + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" +) + +func TestLogrusAdapter(t *testing.T) { + testCases := []struct { + name string + test func(t *testing.T, log logging.Logger) + expectedMsg string + expectedLevel logrus.Level + expectedData logrus.Fields + }{ + { + name: "error", + test: func(t *testing.T, log logging.Logger) { + log.Error("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.ErrorLevel, + }, + { + name: "warn", + test: func(t *testing.T, log logging.Logger) { + log.Warn("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.WarnLevel, + }, + { + name: "info", + test: func(t *testing.T, log logging.Logger) { + log.Info("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.InfoLevel, + }, + { + name: "debug", + test: func(t *testing.T, log logging.Logger) { + log.Debug("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.DebugLevel, + }, + { + name: "trace", + test: func(t *testing.T, log logging.Logger) { + log.Trace("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.TraceLevel, + }, + { + name: "with fields", + test: func(t *testing.T, log logging.Logger) { + log.WithFields(map[string]any{ + "some": "value", + }).Info("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.InfoLevel, + expectedData: logrus.Fields{ + "some": "value", + }, + }, + { + name: "with field", + test: func(t *testing.T, log logging.Logger) { + log.WithField("some", "value").Info("a message") + }, + expectedMsg: "a message", + expectedLevel: logrus.InfoLevel, + expectedData: logrus.Fields{ + "some": "value", + }, + }, + } + + t.Run("from logger", func(t *testing.T) { + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + logrusLogger, hook := test.NewNullLogger() + logrusLogger.SetLevel(logrus.TraceLevel) + logger := NewLogger(logrusLogger) + + testCase.test(t, logger) + require.Len(t, hook.AllEntries(), 1) + require.Equal(t, testCase.expectedMsg, hook.LastEntry().Message) + require.Equal(t, testCase.expectedLevel, hook.LastEntry().Level) + if testCase.expectedData != nil { + require.Equal(t, testCase.expectedData, hook.LastEntry().Data) + } + }) + } + }) + + t.Run("from entry", func(t *testing.T) { + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + logrusLogger, hook := test.NewNullLogger() + logrusLogger.SetLevel(logrus.TraceLevel) + entry := logrus.NewEntry(logrusLogger) + logger := NewEntry(entry) + + testCase.test(t, logger) + require.Len(t, hook.AllEntries(), 1) + require.Equal(t, testCase.expectedMsg, hook.LastEntry().Message) + require.Equal(t, testCase.expectedLevel, hook.LastEntry().Level) + if testCase.expectedData != nil { + require.Equal(t, testCase.expectedData, hook.LastEntry().Data) + } + }) + } + }) +} diff --git a/logging/test/logger.go b/logging/test/logger.go new file mode 100644 index 00000000..a14e705f --- /dev/null +++ b/logging/test/logger.go @@ -0,0 +1,173 @@ +/* + * Copyright 2023 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 test + +import ( + "fmt" + "sync" + + "github.com/rond-authz/rond/logging" +) + +type Record struct { + Fields map[string]any + Message string + Level string +} + +type entry struct { + testLogger + records []Record + originalLogger *testLogger +} + +type testLogger struct { + mu sync.RWMutex + Fields map[string]any + entry *entry +} + +func (l *testLogger) setRecord(level string, msg any) { + l.mu.RLock() + defer l.mu.RUnlock() + + l.entry.records = append(l.entry.records, Record{ + Fields: l.Fields, + Message: fmt.Sprint(msg), + Level: level, + }) + + if originalLogger := l.entry.originalLogger; originalLogger != nil { + originalLogger.mu.RLock() + defer originalLogger.mu.RUnlock() + + originalLogger.entry.records = append(originalLogger.entry.records, Record{ + Fields: l.Fields, + Message: fmt.Sprint(msg), + Level: level, + }) + } +} + +func (l *testLogger) Warn(msg any) { + l.setRecord("warn", msg) +} + +func (l *testLogger) Error(msg any) { + l.setRecord("error", msg) +} + +func (l *testLogger) Debug(msg any) { + l.setRecord("debug", msg) +} + +func (l *testLogger) Info(msg any) { + l.setRecord("info", msg) +} + +func (l *testLogger) Trace(msg any) { + l.setRecord("trace", msg) +} + +func (l *testLogger) WithFields(fields map[string]any) logging.Logger { + l.mu.RLock() + defer l.mu.RUnlock() + + clonedFields := map[string]any{} + for k, v := range l.Fields { + clonedFields[k] = v + } + + originalLogger := l + if l.entry.originalLogger != nil { + originalLogger = l.entry.originalLogger + } + + logger := &testLogger{ + Fields: clonedFields, + entry: &entry{ + testLogger: testLogger{ + Fields: clonedFields, + entry: l.entry, + }, + originalLogger: originalLogger, + records: l.entry.records, + }, + } + for k, v := range fields { + logger.entry.Fields[k] = v + } + return logger +} + +func (l *testLogger) WithField(key string, value any) logging.Logger { + l.mu.RLock() + defer l.mu.RUnlock() + + clonedFields := map[string]any{} + for k, v := range l.Fields { + clonedFields[k] = v + } + + originalLogger := l + if l.entry.originalLogger != nil { + originalLogger = l.entry.originalLogger + } + + logger := &testLogger{ + Fields: clonedFields, + entry: &entry{ + testLogger: testLogger{ + Fields: clonedFields, + entry: l.entry, + }, + originalLogger: originalLogger, + records: l.entry.records, + }, + } + logger.entry.Fields[key] = value + return logger +} + +func (e *entry) AllRecords() []Record { + e.mu.Lock() + defer e.mu.Unlock() + return e.records +} + +func (l *testLogger) OriginalLogger() *entry { + l.mu.Lock() + defer l.mu.Unlock() + return l.entry +} + +func GetLogger() logging.Logger { + return &testLogger{ + Fields: map[string]any{}, + entry: &entry{ + records: []Record{}, + }, + } +} + +func GetRecords(log logging.Logger) ([]Record, error) { + testLog, ok := log.(*testLogger) + if ok { + return testLog.OriginalLogger().AllRecords(), nil + } + return nil, fmt.Errorf("cannot get test logger") +} diff --git a/logging/test/logger_test.go b/logging/test/logger_test.go new file mode 100644 index 00000000..14a06d41 --- /dev/null +++ b/logging/test/logger_test.go @@ -0,0 +1,369 @@ +/* + * Copyright 2023 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 test + +import ( + "testing" + + "github.com/rond-authz/rond/logging" + + "github.com/stretchr/testify/require" +) + +func TestFakeLogger(t *testing.T) { + t.Run("no fields", func(t *testing.T) { + t.Run("info log", func(t *testing.T) { + logger := GetLogger() + + logger.Info("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: map[string]any{}, + }, + }, records) + }) + + t.Run("trace log", func(t *testing.T) { + logger := GetLogger() + + logger.Trace("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "trace", + Message: "my msg", + Fields: map[string]any{}, + }, + }, records) + }) + + t.Run("warn log", func(t *testing.T) { + logger := GetLogger() + + logger.Warn("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "warn", + Message: "my msg", + Fields: map[string]any{}, + }, + }, records) + }) + + t.Run("error log", func(t *testing.T) { + logger := GetLogger() + + logger.Error("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "error", + Message: "my msg", + Fields: map[string]any{}, + }, + }, records) + }) + + t.Run("debug log", func(t *testing.T) { + logger := GetLogger() + + logger.Debug("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "debug", + Message: "my msg", + Fields: map[string]any{}, + }, + }, records) + }) + + t.Run("more logs", func(t *testing.T) { + logger := GetLogger() + + logger.Info("my msg") + logger.Trace("some other") + logger.Info("yeah") + + records := getRecords(t, logger) + require.Len(t, records, 3) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: map[string]any{}, + }, + { + Level: "trace", + Message: "some other", + Fields: map[string]any{}, + }, + { + Level: "info", + Message: "yeah", + Fields: map[string]any{}, + }, + }, records) + }) + }) + + t.Run("with fields", func(t *testing.T) { + expectedFields := map[string]any{ + "k1": "v1", + "k2": "v2", + } + + t.Run("info log", func(t *testing.T) { + logger := GetLogger() + + logger.WithFields(expectedFields).Info("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("trace log", func(t *testing.T) { + logger := GetLogger() + + logger.WithFields(expectedFields).Trace("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "trace", + Message: "my msg", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("more logs", func(t *testing.T) { + logger := GetLogger() + + logger.WithFields(expectedFields).Info("my msg") + logger.WithFields(map[string]any{ + "some": "value", + }).Trace("some other") + logger.WithFields(expectedFields).Info("yeah") + + records := getRecords(t, logger) + require.Len(t, records, 3) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + { + Level: "trace", + Message: "some other", + Fields: map[string]any{ + "some": "value", + }, + }, + { + Level: "info", + Message: "yeah", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("more logs with separate loggers", func(t *testing.T) { + logger := GetLogger() + + l1 := logger.WithFields(expectedFields) + l1.Info("my msg") + l1.WithFields(map[string]any{ + "some": "value", + }).Trace("some other") + + logger.WithFields(map[string]any{ + "a": "b", + }).Info("yeah") + + records := getRecords(t, logger) + require.Len(t, records, 3) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + { + Level: "trace", + Message: "some other", + Fields: map[string]any{ + "k1": "v1", + "k2": "v2", + "some": "value", + }, + }, + { + Level: "info", + Message: "yeah", + Fields: map[string]any{ + "a": "b", + }, + }, + }, records) + }) + }) + + t.Run("with field", func(t *testing.T) { + expectedFields := map[string]any{ + "k1": "v1", + } + + t.Run("info log", func(t *testing.T) { + logger := GetLogger() + + logger.WithField("k1", "v1").Info("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("trace log", func(t *testing.T) { + logger := GetLogger() + + logger.WithField("k1", "v1").Trace("my msg") + + records := getRecords(t, logger) + require.Len(t, records, 1) + require.Equal(t, []Record{ + { + Level: "trace", + Message: "my msg", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("more logs", func(t *testing.T) { + logger := GetLogger() + + logger.WithField("k1", "v1").Info("my msg") + logger.WithFields(map[string]any{ + "some": "value", + }).WithField("a", "b").Trace("some other") + logger.WithField("k1", "v1").Info("yeah") + + records := getRecords(t, logger) + require.Len(t, records, 3) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + { + Level: "trace", + Message: "some other", + Fields: map[string]any{ + "some": "value", + "a": "b", + }, + }, + { + Level: "info", + Message: "yeah", + Fields: expectedFields, + }, + }, records) + }) + + t.Run("more logs with separate loggers", func(t *testing.T) { + logger := GetLogger() + + l1 := logger.WithField("k1", "v1") + l1.Info("my msg") + l1.WithFields(map[string]any{ + "some": "value", + }).Trace("some other") + + logger.WithFields(map[string]any{ + "a": "b", + }).Info("yeah") + + records := getRecords(t, logger) + require.Len(t, records, 3) + require.Equal(t, []Record{ + { + Level: "info", + Message: "my msg", + Fields: expectedFields, + }, + { + Level: "trace", + Message: "some other", + Fields: map[string]any{ + "k1": "v1", + "some": "value", + }, + }, + { + Level: "info", + Message: "yeah", + Fields: map[string]any{ + "a": "b", + }, + }, + }, records) + }) + }) + + t.Run("get records fails", func(t *testing.T) { + _, err := GetRecords(nil) + require.EqualError(t, err, "cannot get test logger") + }) +} + +func getRecords(t *testing.T, log logging.Logger) []Record { + records, err := GetRecords(log) + require.NoError(t, err) + return records +} diff --git a/main.go b/main.go index 8ea0a196..01dd95fe 100644 --- a/main.go +++ b/main.go @@ -28,11 +28,13 @@ import ( "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/helpers" "github.com/rond-authz/rond/internal/mongoclient" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" - "github.com/mia-platform/glogger/v2" + "github.com/mia-platform/glogger/v4" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/sirupsen/logrus" ) @@ -45,7 +47,7 @@ func entrypoint(shutdown chan os.Signal) { env := config.GetEnvOrDie() // Init logger instance. - log, err := glogger.InitHelper(glogger.InitOptions{Level: env.LogLevel}) + log, err := glogrus.InitHelper(glogrus.InitOptions{Level: env.LogLevel}) if err != nil { panic(err.Error()) } @@ -68,7 +70,8 @@ func entrypoint(shutdown chan os.Signal) { } log.WithField("opaModuleFileName", opaModuleConfig.Name).Trace("rego module successfully loaded") - oas, err := openapi.LoadOASFromFileOrNetwork(log, openapi.LoadOptions{ + rondLogger := rondlogrus.NewLogger(log) + oas, err := openapi.LoadOASFromFileOrNetwork(rondLogger, openapi.LoadOptions{ APIPermissionsFilePath: env.APIPermissionsFilePath, TargetServiceOASPath: env.TargetServiceOASPath, TargetServiceHost: env.TargetServiceHost, @@ -86,7 +89,7 @@ func entrypoint(shutdown chan os.Signal) { "oasApiPath": env.TargetServiceOASPath, }).Trace("OAS successfully loaded") - mongoClient, err := mongoclient.NewMongoClient(env, log) + mongoClient, err := mongoclient.NewMongoClient(env, rondLogger) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, @@ -106,7 +109,7 @@ func entrypoint(shutdown chan os.Signal) { EnablePrintStatements: env.IsTraceLogLevel(), MongoClient: mongoClient, }, - Logger: logrus.NewEntry(log), + Logger: rondLogger, }) if err != nil { log.WithFields(logrus.Fields{ diff --git a/main_test.go b/main_test.go index b5cd3bef..972b0f0f 100644 --- a/main_test.go +++ b/main_test.go @@ -34,12 +34,12 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/mongo" @@ -1799,7 +1799,7 @@ filter_policy { MongoClient: mongoClient, }, Registry: registry, - Logger: logrus.NewEntry(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") @@ -1961,7 +1961,7 @@ filter_policy { MongoClient: mongoClient, }, Registry: registry, - Logger: logrus.NewEntry(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") diff --git a/openapi/evaluators.go b/openapi/evaluators.go index 8d559608..a949eeb4 100644 --- a/openapi/evaluators.go +++ b/openapi/evaluators.go @@ -1,3 +1,17 @@ +// Copyright 2023 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 openapi import ( @@ -5,11 +19,10 @@ import ( "fmt" "github.com/rond-authz/rond/core" - - "github.com/sirupsen/logrus" + "github.com/rond-authz/rond/logging" ) -func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *OpenAPISpec, opaModuleConfig *core.OPAModuleConfig, options *core.OPAEvaluatorOptions) (core.PartialResultsEvaluators, error) { +func SetupEvaluators(ctx context.Context, logger logging.Logger, oas *OpenAPISpec, opaModuleConfig *core.OPAModuleConfig, options *core.OPAEvaluatorOptions) (core.PartialResultsEvaluators, error) { if oas == nil { return nil, fmt.Errorf("oas must not be nil") } @@ -22,7 +35,7 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *OpenAPISpec } logger. - WithFields(logrus.Fields{ + WithFields(map[string]any{ "verb": verb, "path": path, }). diff --git a/openapi/evaluators_test.go b/openapi/evaluators_test.go index 5e92a0e0..17fda8dd 100644 --- a/openapi/evaluators_test.go +++ b/openapi/evaluators_test.go @@ -1,3 +1,17 @@ +// 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 openapi import ( @@ -5,23 +19,21 @@ import ( "testing" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/logging" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) func TestCreatePolicyEvaluators(t *testing.T) { t.Run("with simplified mock", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() ctx := context.Background() opaModuleDirectory := "../mocks/rego-policies" loadOptions := LoadOptions{ APIPermissionsFilePath: "../mocks/simplifiedMock.json", } - openApiSpec, err := LoadOASFromFileOrNetwork(log, loadOptions) + openApiSpec, err := LoadOASFromFileOrNetwork(logger, loadOptions) require.NoError(t, err, "unexpected error") opaModuleConfig, err := core.LoadRegoModule(opaModuleDirectory) @@ -33,8 +45,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { }) t.Run("with complete oas mock", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() ctx := context.Background() opaModulesDirectory := "../mocks/rego-policies" @@ -42,7 +53,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { loadOptions := LoadOptions{ APIPermissionsFilePath: "../mocks/pathsConfigAllInclusive.json", } - openApiSpec, err := LoadOASFromFileOrNetwork(log, loadOptions) + openApiSpec, err := LoadOASFromFileOrNetwork(logger, loadOptions) require.NoError(t, err, "unexpected error") opaModuleConfig, err := core.LoadRegoModule(opaModulesDirectory) @@ -54,8 +65,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { }) t.Run("with oas nil", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() ctx := context.Background() _, err := SetupEvaluators(ctx, logger, nil, nil, nil) @@ -63,8 +73,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { }) t.Run("with complete oas mock", func(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() ctx := context.Background() opaModulesDirectory := "../mocks/rego-policies" diff --git a/openapi/openapi_utils.go b/openapi/openapi_utils.go index c96ccb6e..51025005 100644 --- a/openapi/openapi_utils.go +++ b/openapi/openapi_utils.go @@ -29,8 +29,8 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/logging" - "github.com/sirupsen/logrus" "github.com/uptrace/bunrouter" ) @@ -286,7 +286,7 @@ func deserializeSpec(spec []byte, errorWrapper error) (*OpenAPISpec, error) { return &oas, nil } -func fetchOpenAPI(log *logrus.Logger, url string) (*OpenAPISpec, error) { +func fetchOpenAPI(log logging.Logger, url string) (*OpenAPISpec, error) { resp, err := http.DefaultClient.Get(url) if err != nil { return nil, fmt.Errorf("%w: %s", ErrRequestFailed, err) @@ -294,7 +294,9 @@ func fetchOpenAPI(log *logrus.Logger, url string) (*OpenAPISpec, error) { defer func() { err := resp.Body.Close() if err != nil { - log.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed response body close") + log.WithFields(map[string]any{ + "error": map[string]any{"message": err.Error()}, + }).Error("failed response body close") } }() @@ -320,12 +322,12 @@ type LoadOptions struct { TargetServiceHost string } -func LoadOASFromFileOrNetwork(log *logrus.Logger, config LoadOptions) (*OpenAPISpec, error) { +func LoadOASFromFileOrNetwork(log logging.Logger, config LoadOptions) (*OpenAPISpec, error) { if config.APIPermissionsFilePath != "" { log.WithField("oasFilePath", config.APIPermissionsFilePath).Debug("Attempt to load OAS from file") oas, err := LoadOASFile(config.APIPermissionsFilePath) if err != nil { - log.WithFields(logrus.Fields{ + log.WithFields(map[string]any{ "APIPermissionsFilePath": config.APIPermissionsFilePath, }).Warn("failed api permissions file read") return nil, err @@ -341,10 +343,10 @@ func LoadOASFromFileOrNetwork(log *logrus.Logger, config LoadOptions) (*OpenAPIS for { fetchedOAS, err := fetchOpenAPI(log, documentationURL) if err != nil { - log.WithFields(logrus.Fields{ + log.WithFields(map[string]any{ "targetServiceHost": config.TargetServiceHost, "targetOASPath": config.TargetServiceOASPath, - "error": logrus.Fields{"message": err.Error()}, + "error": map[string]any{"message": err.Error()}, }).Warn("failed OAS fetch, retry in 1s") time.Sleep(1 * time.Second) continue diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index 090a7c61..a96aa8c1 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -21,13 +21,14 @@ import ( "testing" "github.com/rond-authz/rond/core" - "github.com/sirupsen/logrus/hooks/test" + "github.com/rond-authz/rond/logging" + "github.com/stretchr/testify/require" "gopkg.in/h2non/gock.v1" ) func TestFetchOpenAPI(t *testing.T) { - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() t.Run("fetches json OAS", func(t *testing.T) { defer gock.Off() @@ -172,7 +173,7 @@ func TestLoadOASFile(t *testing.T) { } func TestLoadOAS(t *testing.T) { - log, _ := test.NewNullLogger() + log := logging.NewNoOpLogger() t.Run("if TargetServiceOASPath & APIPermissionsFilePath are set together, expect to read oas from static file", func(t *testing.T) { options := LoadOptions{ diff --git a/sdk/evaluator.go b/sdk/evaluator.go index f15bd32c..2c96cf51 100644 --- a/sdk/evaluator.go +++ b/sdk/evaluator.go @@ -20,9 +20,8 @@ import ( "fmt" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" - - "github.com/sirupsen/logrus" ) type PolicyResult struct { @@ -46,7 +45,7 @@ type Evaluator interface { } type evaluator struct { - logger *logrus.Entry + logger logging.Logger rondConfig core.RondConfig opaModuleConfig *core.OPAModuleConfig partialResultEvaluators core.PartialResultsEvaluators @@ -94,7 +93,7 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req core.RondInput _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, e.policyEvaluationOptions) if err != nil { - e.logger.WithField("error", logrus.Fields{ + e.logger.WithField("error", map[string]any{ "policyName": rondConfig.RequestFlow.PolicyName, "message": err.Error(), }).Error("RBAC policy evaluation failed") diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 8f6bd81d..bd1688f9 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -24,18 +24,18 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/logging/test" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/expfmt" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) func TestEvaluateRequestPolicy(t *testing.T) { - logger := logrus.NewEntry(logrus.New()) + logger := logging.NewNoOpLogger() t.Run("throws without RondInput", func(t *testing.T) { sdk := getOASSdk(t, nil) @@ -330,9 +330,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { registry: registry, }) - log, hook := test.NewNullLogger() - log.Level = logrus.DebugLevel - logger := logrus.NewEntry(log) + logger := test.GetLogger() evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) require.NoError(t, err) @@ -357,8 +355,10 @@ func TestEvaluateRequestPolicy(t *testing.T) { require.Equal(t, testCase.expectedPolicy, actual) t.Run("logger", func(t *testing.T) { - var actualEntry *logrus.Entry - for _, entry := range hook.AllEntries() { + var actualEntry test.Record + records, err := test.GetRecords(logger) + require.NoError(t, err) + for _, entry := range records { if entry.Message == "policy evaluation completed" { actualEntry = entry } @@ -366,14 +366,14 @@ func TestEvaluateRequestPolicy(t *testing.T) { evaluatorInfo := evaluate.(evaluator) require.NotNil(t, actual) - delete(actualEntry.Data, "evaluationTimeMicroseconds") + delete(actualEntry.Fields, "evaluationTimeMicroseconds") resultLength := 1 if !actual.Allowed { resultLength = 0 } - fields := logrus.Fields{ + fields := map[string]any{ "allowed": actual.Allowed, "requestedPath": testCase.path, "matchedPath": evaluatorInfo.policyEvaluationOptions.AdditionalLogFields["matchedPath"], @@ -386,7 +386,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { fields["resultsLength"] = resultLength } - require.Equal(t, fields, actualEntry.Data) + require.Equal(t, fields, actualEntry.Fields) }) t.Run("metrics", func(t *testing.T) { @@ -420,7 +420,7 @@ func assertCorrectMetrics(t *testing.T, registry *prometheus.Registry, expected } func TestEvaluateResponsePolicy(t *testing.T) { - logger := logrus.NewEntry(logrus.New()) + logger := logging.NewNoOpLogger() t.Run("throws without RondInput", func(t *testing.T) { sdk := getOASSdk(t, nil) @@ -531,9 +531,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { opaModuleContent = testCase.opaModuleContent } - log, hook := test.NewNullLogger() - log.Level = logrus.DebugLevel - logger := logrus.NewEntry(log) + logger := test.GetLogger() registry := prometheus.NewPedanticRegistry() sdk := getOASSdk(t, &sdkOptions{ opaModuleContent: opaModuleContent, @@ -577,8 +575,10 @@ func TestEvaluateResponsePolicy(t *testing.T) { } t.Run("logger", func(t *testing.T) { - var actual *logrus.Entry - for _, entry := range hook.AllEntries() { + var actual test.Record + records, err := test.GetRecords(logger) + require.NoError(t, err) + for _, entry := range records { if entry.Message == "policy evaluation completed" { actual = entry } @@ -586,8 +586,8 @@ func TestEvaluateResponsePolicy(t *testing.T) { evaluatorInfo := evaluate.(evaluator) require.NotNil(t, actual) - delete(actual.Data, "evaluationTimeMicroseconds") - require.Equal(t, logrus.Fields{ + delete(actual.Fields, "evaluationTimeMicroseconds") + require.Equal(t, map[string]any{ "allowed": !testCase.notAllowed, "requestedPath": testCase.path, "matchedPath": evaluatorInfo.policyEvaluationOptions.AdditionalLogFields["matchedPath"], @@ -595,7 +595,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { "partialEval": false, "policyName": evaluate.Config().ResponseFlow.PolicyName, "resultsLength": 1, - }, actual.Data) + }, actual.Fields) }) t.Run("metrics", func(t *testing.T) { @@ -614,8 +614,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") require.NoError(b, err) - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: testmongoMock, @@ -653,7 +652,7 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { h.Helper() } - logger := logrus.NewEntry(logrus.New()) + logger := logging.NewNoOpLogger() if options == nil { options = &sdkOptions{} } diff --git a/sdk/openapi.go b/sdk/openapi.go index 39cd311e..fcb54f24 100644 --- a/sdk/openapi.go +++ b/sdk/openapi.go @@ -17,9 +17,9 @@ package sdk import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/openapi" - "github.com/sirupsen/logrus" "github.com/uptrace/bunrouter" ) @@ -33,7 +33,7 @@ type oasImpl struct { metrics *metrics.Metrics } -func (r oasImpl) FindEvaluator(logger *logrus.Entry, method, path string) (Evaluator, error) { +func (r oasImpl) FindEvaluator(logger logging.Logger, method, path string) (Evaluator, error) { permission, routerInfo, err := r.oas.FindPermission(r.oasRouter, path, method) if err != nil { return nil, err @@ -57,5 +57,5 @@ func (r oasImpl) FindEvaluator(logger *logrus.Entry, method, path string) (Evalu } type OASEvaluatorFinder interface { - FindEvaluator(logger *logrus.Entry, method, path string) (Evaluator, error) + FindEvaluator(logger logging.Logger, method, path string) (Evaluator, error) } diff --git a/sdk/openapi_test.go b/sdk/openapi_test.go index 6d85472d..f46852c7 100644 --- a/sdk/openapi_test.go +++ b/sdk/openapi_test.go @@ -20,17 +20,15 @@ import ( "testing" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/openapi" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) func TestOasSDK(t *testing.T) { - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := logging.NewNoOpLogger() openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.Nil(t, err) diff --git a/sdk/sdk.go b/sdk/sdk.go index 93b83aa6..092e2132 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -20,16 +20,16 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/metrics" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/openapi" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" ) type Options struct { Registry *prometheus.Registry EvaluatorOptions *core.OPAEvaluatorOptions - Logger *logrus.Entry + Logger logging.Logger } func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, options *Options) (OASEvaluatorFinder, error) { @@ -40,9 +40,10 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas if options == nil { options = &Options{} } - if options.Logger == nil { - // TODO: default to a fake silent logger instead of return error - return nil, fmt.Errorf("logger is required inside options") + + logger := options.Logger + if logger == nil { + logger = logging.NewNoOpLogger() } var evaluatorOptions *core.OPAEvaluatorOptions @@ -51,7 +52,6 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas } metrics := setupMetrics(options.Registry) - logger := options.Logger evaluator, err := openapi.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) if err != nil { return nil, err @@ -79,20 +79,20 @@ func NewWithConfig(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, r if options == nil { options = &Options{} } - if options.Logger == nil { - // TODO: default to a fake silent logger instead of return error - return nil, fmt.Errorf("logger must be set in config options") + logger := options.Logger + if logger == nil { + logger = logging.NewNoOpLogger() } policyEvaluators := core.PartialResultsEvaluators{} - if err := policyEvaluators.AddFromConfig(ctx, options.Logger, opaModuleConfig, &rondConfig, options.EvaluatorOptions); err != nil { + if err := policyEvaluators.AddFromConfig(ctx, logger, opaModuleConfig, &rondConfig, options.EvaluatorOptions); err != nil { return nil, err } metrics := setupMetrics(options.Registry) return evaluator{ rondConfig: rondConfig, - logger: options.Logger, + logger: logger, opaModuleConfig: opaModuleConfig, partialResultEvaluators: policyEvaluators, diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 56026e95..4b6b117a 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -22,12 +22,11 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) @@ -42,25 +41,11 @@ func TestNewFromOas(t *testing.T) { openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") require.NoError(t, err) - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - + logger := logging.NewNoOpLogger() options := &Options{ Logger: logger, } - t.Run("throws if options is nil", func(t *testing.T) { - sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, nil) - require.EqualError(t, err, "logger is required inside options") - require.Nil(t, sdk) - }) - - t.Run("throws if logger is nil", func(t *testing.T) { - sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{}) - require.EqualError(t, err, "logger is required inside options") - require.Nil(t, sdk) - }) - t.Run("throws if opaModuleConfig is nil", func(t *testing.T) { sdk, err := NewFromOAS(ctx, nil, nil, options) require.EqualError(t, err, "OPAModuleConfig must not be nil") @@ -117,6 +102,30 @@ func TestNewFromOas(t *testing.T) { require.NotNil(t, evaluator) }) }) + + t.Run("ok if options is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, nil) + require.NoError(t, err) + require.NotNil(t, sdk) + + t.Run("and find evaluators", func(t *testing.T) { + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + require.NotNil(t, evaluator) + }) + }) + + t.Run("ok if logger is nil", func(t *testing.T) { + sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{}) + require.NoError(t, err) + require.NotNil(t, sdk) + + t.Run("and find evaluators", func(t *testing.T) { + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + require.NoError(t, err) + require.NotNil(t, evaluator) + }) + }) } func TestNewWithConfig(t *testing.T) { @@ -129,9 +138,7 @@ func TestNewWithConfig(t *testing.T) { } ctx := context.Background() - log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - + logger := logging.NewNoOpLogger() options := &Options{ Logger: logger, } @@ -141,12 +148,6 @@ func TestNewWithConfig(t *testing.T) { ResponseFlow: core.ResponseFlow{PolicyName: "projection_field"}, } - t.Run("throws if logger not passed", func(t *testing.T) { - evaluator, err := NewWithConfig(ctx, opaModule, core.RondConfig{}, nil) - require.EqualError(t, err, "logger must be set in config options") - require.Nil(t, evaluator) - }) - t.Run("throws if empty config", func(t *testing.T) { evaluator, err := NewWithConfig(ctx, opaModule, core.RondConfig{}, options) require.ErrorContains(t, err, core.ErrInvalidConfig.Error()) @@ -159,6 +160,36 @@ func TestNewWithConfig(t *testing.T) { require.Nil(t, sdk) }) + t.Run("ok with nil options", func(t *testing.T) { + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + + t.Run("run evaluator correctly", func(t *testing.T) { + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, result) + }) + }) + + t.Run("ok if logger not passed", func(t *testing.T) { + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + + t.Run("run evaluator correctly", func(t *testing.T) { + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, result) + }) + }) + t.Run("passes EvaluatorOptions and set metrics correctly", func(t *testing.T) { evalOpts := &core.OPAEvaluatorOptions{ EnablePrintStatements: true, diff --git a/service/handler.go b/service/handler.go index 7cdb6f0b..9ababeb0 100644 --- a/service/handler.go +++ b/service/handler.go @@ -24,12 +24,13 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/sdk" rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/sirupsen/logrus" ) @@ -68,7 +69,7 @@ func ReverseProxyOrResponse( func rbacHandler(w http.ResponseWriter, req *http.Request) { requestContext := req.Context() - logger := glogger.Get(requestContext) + logger := glogrus.FromContext(requestContext) env, err := config.GetEnv(requestContext) if err != nil { @@ -96,12 +97,11 @@ func EvaluateRequest( w http.ResponseWriter, evaluatorSdk sdk.Evaluator, ) error { - requestContext := req.Context() - logger := glogger.Get(requestContext) + logger := glogrus.FromContext(req.Context()) permission := evaluatorSdk.Config() - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(logger, req, types.UserHeadersKeys{ + userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(rondlogrus.NewEntry(logger), req, types.UserHeadersKeys{ IDHeaderKey: env.UserIdHeader, GroupsHeaderKey: env.UserGroupsHeader, PropertiesHeaderKey: env.UserPropertiesHeader, @@ -187,10 +187,10 @@ func ReverseProxy( func alwaysProxyHandler(w http.ResponseWriter, req *http.Request) { requestContext := req.Context() - logger := glogger.Get(req.Context()) + logger := glogrus.FromContext(req.Context()) env, err := config.GetEnv(requestContext) if err != nil { - glogger.Get(requestContext).WithError(err).Error("no env found in context") + logger.WithError(err).Error("no env found in context") utils.FailResponse(w, "no environment found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } diff --git a/service/handler_test.go b/service/handler_test.go index 38cac1a5..6ac515aa 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -33,11 +33,12 @@ import ( "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" + "github.com/mia-platform/glogger/v4" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" @@ -718,8 +719,9 @@ allow { log, hook := test.NewNullLogger() log.Level = logrus.TraceLevel - logger := logrus.NewEntry(log) - ctx = glogger.WithLogger(ctx, logger) + logEntry := logrus.NewEntry(log) + logger := rondlogrus.NewEntry(logEntry) + ctx = glogger.WithLogger(ctx, logEntry) registry := prometheus.NewRegistry() @@ -793,8 +795,9 @@ allow { log, hook := test.NewNullLogger() log.Level = logrus.TraceLevel - logger := logrus.NewEntry(log) - ctx = glogger.WithLogger(ctx, logger) + logEntry := logrus.NewEntry(log) + logger := rondlogrus.NewEntry(logEntry) + ctx = glogger.WithLogger(ctx, logEntry) registry := prometheus.NewRegistry() diff --git a/service/opa_transport.go b/service/opa_transport.go index ea079632..a299acc4 100644 --- a/service/opa_transport.go +++ b/service/opa_transport.go @@ -26,6 +26,7 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/sdk" rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" @@ -109,7 +110,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return nil, fmt.Errorf("%w: %s", ErrOPATransportInvalidResponseBody, err.Error()) } - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(t.logger, t.request, t.userHeaders) + userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(rondlogrus.NewEntry(t.logger), t.request, t.userHeaders) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil diff --git a/service/opa_transport_test.go b/service/opa_transport_test.go index c22283ee..0df69997 100644 --- a/service/opa_transport_test.go +++ b/service/opa_transport_test.go @@ -29,6 +29,7 @@ import ( "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" @@ -284,7 +285,7 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"application/json"}}, } - logEntry := logrus.NewEntry(logger) + logEntry := rondlogrus.NewLogger(logger) req = req.Clone(context.Background()) evaluator := getSdk(t, &sdkOptions{ @@ -422,7 +423,7 @@ func TestOPATransportRoundTrip(t *testing.T) { oasFilePath: "../mocks/rondOasConfig.json", opaModuleContent: "package policies responsepolicy [resources] { resources := input.response.body }", }) - logEntry := logrus.NewEntry(logger) + logEntry := rondlogrus.NewLogger(logger) evaluatorSDK, err := evaluator.FindEvaluator(logEntry, http.MethodGet, "/users/") require.NoError(t, err) @@ -488,7 +489,7 @@ func getSdk(t require.TestingT, options *sdkOptions) sdk.OASEvaluatorFinder { h.Helper() } - logger := logrus.NewEntry(logrus.New()) + logger := rondlogrus.NewLogger(logrus.New()) if options == nil { options = &sdkOptions{} } diff --git a/service/opamiddleware.go b/service/opamiddleware.go index 6c9b66d0..34a6b1f6 100644 --- a/service/opamiddleware.go +++ b/service/opamiddleware.go @@ -19,13 +19,14 @@ import ( "net/http" "strings" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" "github.com/sirupsen/logrus" ) @@ -53,9 +54,9 @@ func OPAMiddleware( path = strings.Replace(r.URL.EscapedPath(), options.PathPrefixStandalone, "", 1) } - logger := glogger.Get(r.Context()) + logger := glogrus.FromContext(r.Context()) - evaluator, err := rondSDK.FindEvaluator(logger, r.Method, path) + evaluator, err := rondSDK.FindEvaluator(rondlogrus.NewEntry(logger), r.Method, path) rondConfig := core.RondConfig{} if err == nil { rondConfig = evaluator.Config() diff --git a/service/opamiddleware_test.go b/service/opamiddleware_test.go index ecb1f02b..6ef85e56 100644 --- a/service/opamiddleware_test.go +++ b/service/opamiddleware_test.go @@ -25,11 +25,11 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" - "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) @@ -40,7 +40,7 @@ func TestOPAMiddleware(t *testing.T) { logger, _ := test.NewNullLogger() sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ - Logger: logrus.NewEntry(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") @@ -303,7 +303,7 @@ func TestOPAMiddlewareStandaloneIntegration(t *testing.T) { t.Helper() log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) + logger := rondlogrus.NewLogger(log) sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.Options{ Logger: logger, }) diff --git a/service/router.go b/service/router.go index 88488054..7c7cf15b 100644 --- a/service/router.go +++ b/service/router.go @@ -22,10 +22,6 @@ import ( "sort" "strings" - swagger "github.com/davidebianchi/gswagger" - "github.com/davidebianchi/gswagger/support/gorilla" - "github.com/getkin/kin-openapi/openapi3" - "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/metrics" @@ -35,8 +31,13 @@ import ( "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" + swagger "github.com/davidebianchi/gswagger" + "github.com/davidebianchi/gswagger/support/gorilla" + "github.com/getkin/kin-openapi/openapi3" "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" + gmux "github.com/mia-platform/glogger/v4/middleware/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" ) @@ -106,7 +107,7 @@ func SetupRouter( registry *prometheus.Registry, ) (*mux.Router, error) { router := mux.NewRouter().UseEncodedPath() - router.Use(glogger.RequestMiddlewareLogger(log, []string{"/-/"})) + router.Use(gmux.RequestMiddlewareLogger(glogrus.GetLogger(logrus.NewEntry(log)), []string{"/-/"})) serviceName := "rönd" StatusRoutes(router, serviceName, env.ServiceVersion) diff --git a/service/router_test.go b/service/router_test.go index 4c8bffbf..c3210314 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -23,15 +23,18 @@ import ( "sort" "testing" - "github.com/mia-platform/glogger/v2" - "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/fake" "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/logging" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" + + "github.com/mia-platform/glogger/v4" + "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -259,7 +262,7 @@ todo { true }`, var mockXPermission = core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} type evaluatorParams struct { - logger *logrus.Entry + logger logging.Logger registry *prometheus.Registry } @@ -281,8 +284,8 @@ func getEvaluator( logger := options.logger if logger == nil { - log, _ := test.NewNullLogger() - logger = logrus.NewEntry(log) + log := logrus.New() + logger = rondlogrus.NewLogger(log) } sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ @@ -305,8 +308,8 @@ func TestSetupRoutesIntegration(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/simplifiedMock.json") log, _ := test.NewNullLogger() - logger := logrus.NewEntry(log) - ctx := glogger.WithLogger(context.Background(), logger) + logger := rondlogrus.NewLogger(log) + ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("invokes known API", func(t *testing.T) { var invoked bool diff --git a/service/standalone_apis.go b/service/standalone_apis.go index 73792b6c..64f4f6ae 100644 --- a/service/standalone_apis.go +++ b/service/standalone_apis.go @@ -26,7 +26,7 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/mia-platform/go-crud-service-client" "github.com/sirupsen/logrus" ) @@ -45,7 +45,7 @@ type RevokeResponseBody struct { } func revokeHandler(w http.ResponseWriter, r *http.Request) { - logger := glogger.Get(r.Context()) + logger := glogrus.FromContext(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) @@ -166,7 +166,7 @@ type GrantResponseBody struct { } func grantHandler(w http.ResponseWriter, r *http.Request) { - logger := glogger.Get(r.Context()) + logger := glogrus.FromContext(r.Context()) env, err := config.GetEnv(r.Context()) if err != nil { utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) diff --git a/service/statusroutes.go b/service/statusroutes.go index a76f8edc..b0e7cf86 100644 --- a/service/statusroutes.go +++ b/service/statusroutes.go @@ -18,9 +18,10 @@ import ( "encoding/json" "net/http" - "github.com/gorilla/mux" - "github.com/mia-platform/glogger/v2" "github.com/rond-authz/rond/internal/utils" + + "github.com/gorilla/mux" + glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/sirupsen/logrus" ) @@ -53,7 +54,7 @@ func handleStatusEndpoint(serviceName, serviceVersion string) func(http.Response return func(w http.ResponseWriter, req *http.Request) { _, body := handleStatusRoutes(w, serviceName, serviceVersion) if _, err := w.Write(body); err != nil { - logger := glogger.Get(req.Context()) + logger := glogrus.FromContext(req.Context()) logger.WithField("error", logrus.Fields{"message": err.Error()}).Warn("failed response write") } } diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 3626063c..079b9193 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -26,11 +26,11 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" + rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/gorilla/mux" - "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" ) @@ -119,7 +119,7 @@ test_policy { true } MongoClient: mongoClient, }, Registry: registry, - Logger: logrus.NewEntry(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") diff --git a/types/rbactypes.go b/types/rbactypes.go index 054a38d7..69eb65bd 100644 --- a/types/rbactypes.go +++ b/types/rbactypes.go @@ -74,7 +74,9 @@ type IMongoClient interface { RetrieveRoles(ctx context.Context) ([]Role, error) RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]Role, error) + // FindOne is only used inside the custom_builtins FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) + // FindMany is only used inside the custom_builtins FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) } From 24994f672379d1e831d51a83773c5fb578a90bff Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 18 Jul 2023 18:31:42 +0200 Subject: [PATCH 117/172] feat: update metrics --- core/opaevaluator.go | 13 ++-- core/partialevaluators_test.go | 5 +- internal/metrics/metrics.go | 47 --------------- internal/metrics/metrics_test.go | 55 ----------------- main.go | 16 ++++- main_test.go | 19 +++--- metrics/metrics.go | 45 ++++++++++++++ metrics/metrics_test.go | 15 +++++ metrics/prometheus/prom.go | 53 ++++++++++++++++ metrics/prometheus/prom_test.go | 60 +++++++++++++++++++ sdk/evaluator_test.go | 8 ++- sdk/openapi.go | 2 +- sdk/openapi_test.go | 7 +-- sdk/sdk.go | 20 ++----- sdk/sdk_test.go | 15 +++-- .../routes.go => service/metricsroute.go | 12 ++-- .../metricsroute_test.go | 18 +++++- service/opa_transport_test.go | 5 +- service/router.go | 7 ++- service/router_test.go | 11 +++- service/statusroutes_test.go | 9 +-- 21 files changed, 267 insertions(+), 175 deletions(-) delete mode 100644 internal/metrics/metrics.go delete mode 100644 internal/metrics/metrics_test.go create mode 100644 metrics/metrics.go create mode 100644 metrics/metrics_test.go create mode 100644 metrics/prometheus/prom.go create mode 100644 metrics/prometheus/prom_test.go rename internal/metrics/routes.go => service/metricsroute.go (80%) rename internal/metrics/routes_test.go => service/metricsroute_test.go (70%) diff --git a/core/opaevaluator.go b/core/opaevaluator.go index b47de925..e6c2f15b 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -23,16 +23,15 @@ import ( "time" "github.com/rond-authz/rond/custom_builtins" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/types" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" - "github.com/prometheus/client_golang/prometheus" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -148,7 +147,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger logging.Logger, options opaEvaluationTime := time.Since(opaEvaluationTimeStart) - options.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + options.metrics().PolicyEvaluationDurationMilliseconds.With(metrics.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -189,7 +188,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger logging.Logger, options *PolicyEv } opaEvaluationTime := time.Since(opaEvaluationTimeStart) - options.metrics().PolicyEvaluationDurationMilliseconds.With(prometheus.Labels{ + options.metrics().PolicyEvaluationDurationMilliseconds.With(metrics.Labels{ "policy_name": evaluator.PolicyName, }).Observe(float64(opaEvaluationTime.Milliseconds())) @@ -235,11 +234,11 @@ type PolicyEvaluationOptions struct { AdditionalLogFields map[string]string } -func (evaluator *PolicyEvaluationOptions) metrics() metrics.Metrics { +func (evaluator *PolicyEvaluationOptions) metrics() *metrics.Metrics { if evaluator.Metrics != nil { - return *evaluator.Metrics + return evaluator.Metrics } - return metrics.SetupMetrics("rond") + return metrics.NoOpMetrics() } func (evaluator *OPAEvaluator) PolicyEvaluation(logger logging.Logger, options *PolicyEvaluationOptions) (interface{}, primitive.M, error) { diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go index 2e5fe9ec..04711b9f 100644 --- a/core/partialevaluators_test.go +++ b/core/partialevaluators_test.go @@ -20,9 +20,9 @@ import ( "fmt" "testing" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/stretchr/testify/require" ) @@ -326,9 +326,8 @@ func TestPartialResultEvaluators(t *testing.T) { require.NoError(t, err) evaluator, err := opaModule.CreateQueryEvaluator(context.Background(), logger, "filter_projects", input, &evalOpts) require.NoError(t, err) - metrics := metrics.SetupMetrics("rond") opts := PolicyEvaluationOptions{ - Metrics: &metrics, + Metrics: metrics.NoOpMetrics(), } res, query, err := evaluator.PolicyEvaluation(logger, &opts) require.NoError(t, err) diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go deleted file mode 100644 index a3d1e6f3..00000000 --- a/internal/metrics/metrics.go +++ /dev/null @@ -1,47 +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 metrics - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" -) - -type Metrics struct { - PolicyEvaluationDurationMilliseconds *prometheus.HistogramVec -} - -func SetupMetrics(prefix string) Metrics { - m := Metrics{ - PolicyEvaluationDurationMilliseconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: prefix, - Name: "policy_evaluation_duration_milliseconds", - Help: "A histogram of the policy evaluation durations in milliseconds.", - Buckets: []float64{1, 5, 10, 50, 100, 250, 500}, - }, []string{"policy_name"}), - } - - return m -} - -func (m Metrics) MustRegister(reg prometheus.Registerer) Metrics { - reg.MustRegister( - collectors.NewGoCollector(), - collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), - m.PolicyEvaluationDurationMilliseconds, - ) - - return m -} diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go deleted file mode 100644 index d5ab4acf..00000000 --- a/internal/metrics/metrics_test.go +++ /dev/null @@ -1,55 +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 metrics - -import ( - "strings" - "testing" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/testutil" - "github.com/stretchr/testify/require" -) - -func TestMetrics(t *testing.T) { - t.Run("setup and register metrics", func(t *testing.T) { - m := SetupMetrics("test_prefix") - registry := prometheus.NewPedanticRegistry() - m.MustRegister(registry) - - t.Run("PolicyEvaluationDurationMilliseconds", func(t *testing.T) { - m.PolicyEvaluationDurationMilliseconds.WithLabelValues("myPolicyName").Observe(10) - - metadata := ` - # HELP test_prefix_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. - # TYPE test_prefix_policy_evaluation_duration_milliseconds histogram -` - expected := ` - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="1"} 0 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="5"} 0 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="10"} 1 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="50"} 1 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="100"} 1 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="250"} 1 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="500"} 1 - test_prefix_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="+Inf"} 1 - test_prefix_policy_evaluation_duration_milliseconds_sum{policy_name="myPolicyName"} 10 - test_prefix_policy_evaluation_duration_milliseconds_count{policy_name="myPolicyName"} 1 -` - - require.NoError(t, testutil.CollectAndCompare(m.PolicyEvaluationDurationMilliseconds, strings.NewReader(metadata+expected), "test_prefix_policy_evaluation_duration_milliseconds")) - }) - }) -} diff --git a/main.go b/main.go index 01dd95fe..b1af03c6 100644 --- a/main.go +++ b/main.go @@ -24,11 +24,14 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/helpers" "github.com/rond-authz/rond/internal/mongoclient" rondlogrus "github.com/rond-authz/rond/logging/logrus" + "github.com/rond-authz/rond/metrics" + rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" @@ -102,9 +105,18 @@ func entrypoint(shutdown chan os.Signal) { logrus.NewEntry(log), ) - registry := prometheus.NewRegistry() + var m *metrics.Metrics + var registry *prometheus.Registry + if env.ExposeMetrics { + registry = prometheus.NewRegistry() + registry.MustRegister( + collectors.NewGoCollector(), + collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), + ) + m = rondprometheus.SetupMetrics(registry) + } sdk, err := sdk.NewFromOAS(ctx, opaModuleConfig, oas, &sdk.Options{ - Registry: registry, + Metrics: m, EvaluatorOptions: &core.OPAEvaluatorOptions{ EnablePrintStatements: env.IsTraceLogLevel(), MongoClient: mongoClient, diff --git a/main_test.go b/main_test.go index 972b0f0f..036c2ac6 100644 --- a/main_test.go +++ b/main_test.go @@ -35,6 +35,8 @@ import ( "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" + "github.com/rond-authz/rond/metrics" + rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/service" @@ -1792,18 +1794,16 @@ filter_policy { } var mongoClient *mongoclient.MongoClient - registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, - Registry: registry, - Logger: rondlogrus.NewLogger(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") - router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) + router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) require.NoError(t, err, "unexpected error") t.Run("some eval API", func(t *testing.T) { @@ -1956,15 +1956,20 @@ filter_policy { var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() + m := rondprometheus.SetupMetrics(registry) sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, - Registry: registry, - Logger: rondlogrus.NewLogger(logger), + Logger: rondlogrus.NewLogger(logger), + Metrics: m, }) require.NoError(t, err, "unexpected error") + m.PolicyEvaluationDurationMilliseconds.With(metrics.Labels{ + "policy_name": "myPolicy", + }).Observe(123) + router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) require.NoError(t, err, "unexpected error") @@ -1976,7 +1981,7 @@ filter_policy { require.Equal(t, http.StatusOK, w.Result().StatusCode) responseBody := getResponseBody(t, w) - require.Contains(t, string(responseBody), "go_gc_duration_seconds") + require.Contains(t, string(responseBody), "promhttp_metric_handler_errors_total") }) } diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 00000000..b2b13c52 --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,45 @@ +// 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 metrics + +type Labels map[string]string + +type Observer interface { + Observe(float64) +} + +type HistogramVec interface { + With(labels Labels) Observer +} + +type Metrics struct { + PolicyEvaluationDurationMilliseconds HistogramVec +} + +type noopHistogram struct{} + +func (h noopHistogram) With(labels Labels) Observer { + return noopObserver{} +} + +type noopObserver struct{} + +func (o noopObserver) Observe(float64) {} + +func NoOpMetrics() *Metrics { + return &Metrics{ + PolicyEvaluationDurationMilliseconds: noopHistogram{}, + } +} diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go new file mode 100644 index 00000000..7cf257e7 --- /dev/null +++ b/metrics/metrics_test.go @@ -0,0 +1,15 @@ +// 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 metrics diff --git a/metrics/prometheus/prom.go b/metrics/prometheus/prom.go new file mode 100644 index 00000000..b9f634c8 --- /dev/null +++ b/metrics/prometheus/prom.go @@ -0,0 +1,53 @@ +// Copyright 2023 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 rondprometheus + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/rond-authz/rond/metrics" +) + +const MetricsPrefix = "rond" + +func SetupMetrics(reg prometheus.Registerer) *metrics.Metrics { + duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: MetricsPrefix, + Name: "policy_evaluation_duration_milliseconds", + Help: "A histogram of the policy evaluation durations in milliseconds.", + Buckets: []float64{1, 5, 10, 50, 100, 250, 500}, + }, []string{"policy_name"}) + + m := &metrics.Metrics{ + PolicyEvaluationDurationMilliseconds: histogramVec{duration}, + } + + reg.MustRegister( + duration, + ) + + return m +} + +type histogramVec struct { + *prometheus.HistogramVec +} + +type observer struct { + prometheus.Observer +} + +func (h histogramVec) With(labels metrics.Labels) metrics.Observer { + return observer{h.HistogramVec.With(prometheus.Labels(labels))} +} diff --git a/metrics/prometheus/prom_test.go b/metrics/prometheus/prom_test.go new file mode 100644 index 00000000..5d099a1f --- /dev/null +++ b/metrics/prometheus/prom_test.go @@ -0,0 +1,60 @@ +// Copyright 2023 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 rondprometheus + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/rond-authz/rond/metrics" + "github.com/stretchr/testify/require" +) + +func TestMetrics(t *testing.T) { + t.Run("setup and register metrics", func(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + m := SetupMetrics(registry) + + policyDuration, ok := m.PolicyEvaluationDurationMilliseconds.(histogramVec) + require.True(t, ok) + + t.Run("PolicyEvaluationDurationMilliseconds", func(t *testing.T) { + m.PolicyEvaluationDurationMilliseconds.With(metrics.Labels{ + "policy_name": "myPolicyName", + }).Observe(10) + + metadata := ` + # HELP rond_policy_evaluation_duration_milliseconds A histogram of the policy evaluation durations in milliseconds. + # TYPE rond_policy_evaluation_duration_milliseconds histogram +` + expected := ` + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="1"} 0 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="5"} 0 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="10"} 1 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="50"} 1 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="100"} 1 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="250"} 1 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="500"} 1 + rond_policy_evaluation_duration_milliseconds_bucket{policy_name="myPolicyName",le="+Inf"} 1 + rond_policy_evaluation_duration_milliseconds_sum{policy_name="myPolicyName"} 10 + rond_policy_evaluation_duration_milliseconds_count{policy_name="myPolicyName"} 1 +` + + require.NoError(t, testutil.CollectAndCompare(policyDuration.HistogramVec, strings.NewReader(metadata+expected), "test_prefix_policy_evaluation_duration_milliseconds")) + }) + }) +} diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index bd1688f9..4c551ddb 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -26,6 +26,7 @@ import ( "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/logging/test" + rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" @@ -327,7 +328,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { opaModuleContent: testCase.opaModuleContent, oasFilePath: testCase.oasFilePath, mongoClient: testCase.mongoClient, - registry: registry, + metrics: rondprometheus.SetupMetrics(registry), }) logger := test.GetLogger() @@ -537,7 +538,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { opaModuleContent: opaModuleContent, oasFilePath: "../mocks/rondOasConfig.json", mongoClient: testCase.mongoClient, - registry: registry, + metrics: rondprometheus.SetupMetrics(registry), }) evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) @@ -672,8 +673,9 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if options.opaModuleContent != "" { opaModule.Content = options.opaModuleContent } + sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{ - Registry: options.registry, + Metrics: options.metrics, EvaluatorOptions: &core.OPAEvaluatorOptions{ EnablePrintStatements: true, MongoClient: options.mongoClient, diff --git a/sdk/openapi.go b/sdk/openapi.go index fcb54f24..e6de670f 100644 --- a/sdk/openapi.go +++ b/sdk/openapi.go @@ -16,8 +16,8 @@ package sdk import ( "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" "github.com/uptrace/bunrouter" diff --git a/sdk/openapi_test.go b/sdk/openapi_test.go index f46852c7..fdb23c0a 100644 --- a/sdk/openapi_test.go +++ b/sdk/openapi_test.go @@ -21,9 +21,9 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" ) @@ -37,10 +37,9 @@ func TestOasSDK(t *testing.T) { Content: `package policies very_very_composed_permission { true }`, } - registry := prometheus.NewRegistry() sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{ - Registry: registry, - Logger: logger, + Metrics: metrics.NoOpMetrics(), + Logger: logger, }) require.NoError(t, err) diff --git a/sdk/sdk.go b/sdk/sdk.go index 092e2132..7205ab66 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -19,15 +19,13 @@ import ( "fmt" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" - - "github.com/prometheus/client_golang/prometheus" ) type Options struct { - Registry *prometheus.Registry + Metrics *metrics.Metrics EvaluatorOptions *core.OPAEvaluatorOptions Logger logging.Logger } @@ -50,7 +48,6 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas if options.EvaluatorOptions != nil { evaluatorOptions = options.EvaluatorOptions } - metrics := setupMetrics(options.Registry) evaluator, err := openapi.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) if err != nil { @@ -71,7 +68,7 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas opaModuleConfig: opaModuleConfig, partialResultEvaluators: evaluator, opaEvaluatorOptions: evaluatorOptions, - metrics: metrics, + metrics: options.Metrics, }, nil } @@ -88,7 +85,6 @@ func NewWithConfig(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, r if err := policyEvaluators.AddFromConfig(ctx, logger, opaModuleConfig, &rondConfig, options.EvaluatorOptions); err != nil { return nil, err } - metrics := setupMetrics(options.Registry) return evaluator{ rondConfig: rondConfig, @@ -98,15 +94,7 @@ func NewWithConfig(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, r opaEvaluatorOptions: options.EvaluatorOptions, policyEvaluationOptions: &core.PolicyEvaluationOptions{ - Metrics: metrics, + Metrics: options.Metrics, }, }, nil } - -func setupMetrics(registry *prometheus.Registry) *metrics.Metrics { - m := metrics.SetupMetrics("rond") - if registry != nil { - m.MustRegister(registry) - } - return &m -} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 4b6b117a..a2d0b03b 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -23,10 +23,10 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" ) @@ -66,11 +66,10 @@ func TestNewFromOas(t *testing.T) { require.Nil(t, sdk) }) - t.Run("if registry is passed, setup metrics", func(t *testing.T) { - registry := prometheus.NewRegistry() + t.Run("if metrics is passed, setup metrics", func(t *testing.T) { sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{ - Registry: registry, - Logger: logger, + Metrics: metrics.NoOpMetrics(), + Logger: logger, }) require.NoError(t, err) require.NotEmpty(t, sdk) @@ -83,7 +82,7 @@ func TestNewFromOas(t *testing.T) { sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{ EvaluatorOptions: evalOpts, Logger: logger, - Registry: prometheus.NewRegistry(), + Metrics: metrics.NoOpMetrics(), }) require.NoError(t, err) require.NotEmpty(t, sdk) @@ -196,8 +195,8 @@ func TestNewWithConfig(t *testing.T) { } eval, err := NewWithConfig(ctx, opaModule, rondConfig, &Options{ EvaluatorOptions: evalOpts, + Metrics: metrics.NoOpMetrics(), Logger: logger, - Registry: prometheus.NewRegistry(), }) require.NoError(t, err) require.NotEmpty(t, eval) @@ -265,7 +264,7 @@ type sdkOptions struct { oasFilePath string mongoClient types.IMongoClient - registry *prometheus.Registry + metrics *metrics.Metrics } type tHelper interface { diff --git a/internal/metrics/routes.go b/service/metricsroute.go similarity index 80% rename from internal/metrics/routes.go rename to service/metricsroute.go index cca09386..59666c21 100644 --- a/internal/metrics/routes.go +++ b/service/metricsroute.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrics +package service import ( "github.com/gorilla/mux" @@ -20,10 +20,14 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -var MetricsRoutePath = "/-/rond/metrics" +var metricsRoutePath = "/-/rond/metrics" -func MetricsRoute(r *mux.Router, registry *prometheus.Registry) { - r.Handle(MetricsRoutePath, promhttp.InstrumentMetricHandler( +func metricsRoute(r *mux.Router, registry *prometheus.Registry) { + if registry == nil { + return + } + + r.Handle(metricsRoutePath, promhttp.InstrumentMetricHandler( registry, promhttp.HandlerFor(registry, promhttp.HandlerOpts{ Registry: registry, diff --git a/internal/metrics/routes_test.go b/service/metricsroute_test.go similarity index 70% rename from internal/metrics/routes_test.go rename to service/metricsroute_test.go index 2c3085ce..83ae5b8a 100644 --- a/internal/metrics/routes_test.go +++ b/service/metricsroute_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrics +package service import ( "net/http" @@ -28,13 +28,25 @@ func TestMetricsRoute(t *testing.T) { t.Run("exposes metrics route", func(t *testing.T) { router := mux.NewRouter() registry := prometheus.NewRegistry() - MetricsRoute(router, registry) + metricsRoute(router, registry) - req := httptest.NewRequest(http.MethodGet, MetricsRoutePath, nil) + req := httptest.NewRequest(http.MethodGet, metricsRoutePath, nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Result().StatusCode) }) + + t.Run("without registry not exposes route", func(t *testing.T) { + router := mux.NewRouter() + metricsRoute(router, nil) + + req := httptest.NewRequest(http.MethodGet, metricsRoutePath, nil) + w := httptest.NewRecorder() + + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusNotFound, w.Result().StatusCode) + }) } diff --git a/service/opa_transport_test.go b/service/opa_transport_test.go index 0df69997..4e0c6ff4 100644 --- a/service/opa_transport_test.go +++ b/service/opa_transport_test.go @@ -34,7 +34,6 @@ import ( "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" - "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" @@ -477,7 +476,6 @@ type sdkOptions struct { oasFilePath string mongoClient types.IMongoClient - registry *prometheus.Registry } type tHelper interface { @@ -515,8 +513,7 @@ func getSdk(t require.TestingT, options *sdkOptions) sdk.OASEvaluatorFinder { MongoClient: options.mongoClient, EnablePrintStatements: true, }, - Registry: options.registry, - Logger: logger, + Logger: logger, }) require.NoError(t, err) diff --git a/service/router.go b/service/router.go index 7c7cf15b..7c9c4ee4 100644 --- a/service/router.go +++ b/service/router.go @@ -24,7 +24,6 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/metrics" "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" @@ -41,7 +40,7 @@ import ( "github.com/sirupsen/logrus" ) -var routesToNotProxy = utils.Union(statusRoutes, []string{metrics.MetricsRoutePath}) +var routesToNotProxy = utils.Union(statusRoutes, []string{metricsRoutePath}) var revokeDefinitions = swagger.Definitions{ RequestBody: &swagger.ContentValue{ @@ -111,7 +110,9 @@ func SetupRouter( serviceName := "rönd" StatusRoutes(router, serviceName, env.ServiceVersion) - metrics.MetricsRoute(router, registry) + if env.ExposeMetrics { + metricsRoute(router, registry) + } router.Use(config.RequestMiddlewareEnvironments(env)) diff --git a/service/router_test.go b/service/router_test.go index c3210314..b8e2c7b5 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -29,6 +29,8 @@ import ( "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" rondlogrus "github.com/rond-authz/rond/logging/logrus" + "github.com/rond-authz/rond/metrics" + rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" "github.com/rond-authz/rond/types" @@ -288,12 +290,17 @@ func getEvaluator( logger = rondlogrus.NewLogger(log) } + var m *metrics.Metrics + if options.registry != nil { + m = rondprometheus.SetupMetrics(options.registry) + } + sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, - Registry: options.registry, - Logger: logger, + Logger: logger, + Metrics: m, }) require.NoError(t, err, "unexpected error") diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 079b9193..97f86537 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -22,7 +22,6 @@ import ( "net/http/httptest" "testing" - "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/mongoclient" @@ -112,14 +111,12 @@ test_policy { true } } var mongoClient *mongoclient.MongoClient - registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, - Registry: registry, - Logger: rondlogrus.NewLogger(logger), + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") @@ -129,7 +126,7 @@ test_policy { true } TargetServiceHost: "my-service:4444", PathPrefixStandalone: "/my-prefix", } - router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) + router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { @@ -162,7 +159,7 @@ test_policy { true } PathPrefixStandalone: "/my-prefix", ServiceVersion: "latest", } - router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) + router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() From c92dfaca6b02734d4cd3abe138f7a1f4ee7c4393 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 18 Jul 2023 19:17:17 +0200 Subject: [PATCH 118/172] refactor: remove prometheus deps from sdk --- metrics/metrics.go | 6 +++ metrics/metrics_test.go | 15 ------- metrics/prometheus/prom.go | 6 +-- metrics/test/metrics.go | 80 ++++++++++++++++++++++++++++++++++++++ sdk/evaluator_test.go | 56 +++++++++++--------------- 5 files changed, 110 insertions(+), 53 deletions(-) delete mode 100644 metrics/metrics_test.go create mode 100644 metrics/test/metrics.go diff --git a/metrics/metrics.go b/metrics/metrics.go index b2b13c52..5780a5d1 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -14,6 +14,12 @@ package metrics +const ( + Prefix = "rond" + + PolicyEvalDuration = "policy_evaluation_duration_milliseconds" +) + type Labels map[string]string type Observer interface { diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go deleted file mode 100644 index 7cf257e7..00000000 --- a/metrics/metrics_test.go +++ /dev/null @@ -1,15 +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 metrics diff --git a/metrics/prometheus/prom.go b/metrics/prometheus/prom.go index b9f634c8..4805d6fe 100644 --- a/metrics/prometheus/prom.go +++ b/metrics/prometheus/prom.go @@ -19,12 +19,10 @@ import ( "github.com/rond-authz/rond/metrics" ) -const MetricsPrefix = "rond" - func SetupMetrics(reg prometheus.Registerer) *metrics.Metrics { duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: MetricsPrefix, - Name: "policy_evaluation_duration_milliseconds", + Namespace: metrics.Prefix, + Name: metrics.PolicyEvalDuration, Help: "A histogram of the policy evaluation durations in milliseconds.", Buckets: []float64{1, 5, 10, 50, 100, 250, 500}, }, []string{"policy_name"}) diff --git a/metrics/test/metrics.go b/metrics/test/metrics.go new file mode 100644 index 00000000..e06dfe29 --- /dev/null +++ b/metrics/test/metrics.go @@ -0,0 +1,80 @@ +// Copyright 2023 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 metricstest + +import ( + "github.com/rond-authz/rond/metrics" +) + +type Entry struct { + Name string + Labels metrics.Labels + Value float64 +} + +type Entries []Entry + +type Hook struct { + Entries Entries +} + +func (h *Hook) setValueToLastEntry(v float64) { + h.Entries[len(h.Entries)-1].Value = v +} + +func (h *Hook) AllEntries() Entries { + return h.Entries +} + +func New() (*metrics.Metrics, *Hook) { + duration := histogramVec{ + Namespace: metrics.Prefix, + Name: metrics.PolicyEvalDuration, + Labels: []string{"policy_name"}, + + hook: &Hook{}, + } + + m := &metrics.Metrics{ + PolicyEvaluationDurationMilliseconds: duration, + } + + return m, duration.hook +} + +type histogramVec struct { + Namespace string + Name string + Labels []string + + hook *Hook +} + +type observer struct { + hook *Hook +} + +func (o observer) Observe(v float64) { + o.hook.setValueToLastEntry(v) +} + +func (h histogramVec) With(labels metrics.Labels) metrics.Observer { + h.hook.Entries = append(h.hook.Entries, Entry{ + Name: h.Name, + Labels: labels, + }) + + return observer{hook: h.hook} +} diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 4c551ddb..d7fc30df 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -15,9 +15,7 @@ package sdk import ( - "bytes" "context" - "fmt" "net/http" "net/http/httptest" "testing" @@ -26,12 +24,11 @@ import ( "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/logging/test" - rondprometheus "github.com/rond-authz/rond/metrics/prometheus" + "github.com/rond-authz/rond/metrics" + metricstest "github.com/rond-authz/rond/metrics/test" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/types" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/expfmt" "github.com/stretchr/testify/require" ) @@ -323,12 +320,12 @@ func TestEvaluateRequestPolicy(t *testing.T) { for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - registry := prometheus.NewPedanticRegistry() + testMetrics, hook := metricstest.New() sdk := getOASSdk(t, &sdkOptions{ opaModuleContent: testCase.opaModuleContent, oasFilePath: testCase.oasFilePath, mongoClient: testCase.mongoClient, - metrics: rondprometheus.SetupMetrics(registry), + metrics: testMetrics, }) logger := test.GetLogger() @@ -391,35 +388,20 @@ func TestEvaluateRequestPolicy(t *testing.T) { }) t.Run("metrics", func(t *testing.T) { - expected := fmt.Sprintf(`rond_policy_evaluation_duration_milliseconds_count{policy_name="%s"} 1`, evaluate.Config().RequestFlow.PolicyName) - assertCorrectMetrics(t, registry, expected) + require.Len(t, hook.AllEntries(), 1) + require.Equal(t, metricstest.Entry{ + Name: "policy_evaluation_duration_milliseconds", + Labels: metrics.Labels{ + "policy_name": evaluate.Config().RequestFlow.PolicyName, + }, + Value: hook.Entries[0].Value, + }, hook.Entries[0]) }) }) } }) } -func assertCorrectMetrics(t *testing.T, registry *prometheus.Registry, expected string) { - t.Helper() - - g := prometheus.ToTransactionalGatherer(registry) - got, done, err := g.Gather() - defer done() - require.NoError(t, err) - - for _, m := range got { - if m.GetName() == "rond_policy_evaluation_duration_milliseconds" { - var gotBuf bytes.Buffer - enc := expfmt.NewEncoder(&gotBuf, expfmt.FmtText) - err := enc.Encode(m) - require.NoError(t, err) - require.Contains(t, gotBuf.String(), expected) - return - } - } - require.Fail(t, "metrics must be retrieved") -} - func TestEvaluateResponsePolicy(t *testing.T) { logger := logging.NewNoOpLogger() @@ -533,12 +515,12 @@ func TestEvaluateResponsePolicy(t *testing.T) { } logger := test.GetLogger() - registry := prometheus.NewPedanticRegistry() + testMetrics, hook := metricstest.New() sdk := getOASSdk(t, &sdkOptions{ opaModuleContent: opaModuleContent, oasFilePath: "../mocks/rondOasConfig.json", mongoClient: testCase.mongoClient, - metrics: rondprometheus.SetupMetrics(registry), + metrics: testMetrics, }) evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) @@ -600,8 +582,14 @@ func TestEvaluateResponsePolicy(t *testing.T) { }) t.Run("metrics", func(t *testing.T) { - expected := fmt.Sprintf(`rond_policy_evaluation_duration_milliseconds_count{policy_name="%s"} 1`, evaluate.Config().ResponseFlow.PolicyName) - assertCorrectMetrics(t, registry, expected) + require.Len(t, hook.AllEntries(), 1) + require.Equal(t, metricstest.Entry{ + Name: "policy_evaluation_duration_milliseconds", + Labels: metrics.Labels{ + "policy_name": evaluate.Config().ResponseFlow.PolicyName, + }, + Value: hook.Entries[0].Value, + }, hook.Entries[0]) }) }) } From 173c0015b4d1ccc9d448a902b5956aec217acecb Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Wed, 19 Jul 2023 09:56:54 +0200 Subject: [PATCH 119/172] feat: update metrics --- go.mod | 2 +- main_test.go | 3 ++- metrics/metrics.go | 2 +- metrics/prometheus/prom.go | 2 +- metrics/prometheus/prom_test.go | 3 ++- metrics/test/metrics.go | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2ec92684..08897632 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/mia-platform/go-crud-service-client v0.10.0 github.com/open-policy-agent/opa v0.54.0 github.com/prometheus/client_golang v1.16.0 - github.com/prometheus/common v0.44.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 @@ -59,6 +58,7 @@ require ( github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/spf13/afero v1.9.2 // indirect diff --git a/main_test.go b/main_test.go index 036c2ac6..031aceda 100644 --- a/main_test.go +++ b/main_test.go @@ -1981,7 +1981,8 @@ filter_policy { require.Equal(t, http.StatusOK, w.Result().StatusCode) responseBody := getResponseBody(t, w) - require.Contains(t, string(responseBody), "promhttp_metric_handler_errors_total") + require.Contains(t, string(responseBody), "go_gc_duration_seconds") + require.Contains(t, string(responseBody), fmt.Sprintf("rond_%s", metrics.PolicyEvalDurationMetricName)) }) } diff --git a/metrics/metrics.go b/metrics/metrics.go index 5780a5d1..955f255a 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -17,7 +17,7 @@ package metrics const ( Prefix = "rond" - PolicyEvalDuration = "policy_evaluation_duration_milliseconds" + PolicyEvalDurationMetricName = "policy_evaluation_duration_milliseconds" ) type Labels map[string]string diff --git a/metrics/prometheus/prom.go b/metrics/prometheus/prom.go index 4805d6fe..7abd9eeb 100644 --- a/metrics/prometheus/prom.go +++ b/metrics/prometheus/prom.go @@ -22,7 +22,7 @@ import ( func SetupMetrics(reg prometheus.Registerer) *metrics.Metrics { duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: metrics.Prefix, - Name: metrics.PolicyEvalDuration, + Name: metrics.PolicyEvalDurationMetricName, Help: "A histogram of the policy evaluation durations in milliseconds.", Buckets: []float64{1, 5, 10, 50, 100, 250, 500}, }, []string{"policy_name"}) diff --git a/metrics/prometheus/prom_test.go b/metrics/prometheus/prom_test.go index 5d099a1f..e3fc1667 100644 --- a/metrics/prometheus/prom_test.go +++ b/metrics/prometheus/prom_test.go @@ -18,9 +18,10 @@ import ( "strings" "testing" + "github.com/rond-authz/rond/metrics" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" - "github.com/rond-authz/rond/metrics" "github.com/stretchr/testify/require" ) diff --git a/metrics/test/metrics.go b/metrics/test/metrics.go index e06dfe29..25e1d0f1 100644 --- a/metrics/test/metrics.go +++ b/metrics/test/metrics.go @@ -41,7 +41,7 @@ func (h *Hook) AllEntries() Entries { func New() (*metrics.Metrics, *Hook) { duration := histogramVec{ Namespace: metrics.Prefix, - Name: metrics.PolicyEvalDuration, + Name: metrics.PolicyEvalDurationMetricName, Labels: []string{"policy_name"}, hook: &Hook{}, From 985b84ccbedd6926ffedf2d914b13bad6ac9b570 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Wed, 19 Jul 2023 10:24:01 +0200 Subject: [PATCH 120/172] test: fix tests --- main_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/main_test.go b/main_test.go index 031aceda..7804301e 100644 --- a/main_test.go +++ b/main_test.go @@ -1981,7 +1981,6 @@ filter_policy { require.Equal(t, http.StatusOK, w.Result().StatusCode) responseBody := getResponseBody(t, w) - require.Contains(t, string(responseBody), "go_gc_duration_seconds") require.Contains(t, string(responseBody), fmt.Sprintf("rond_%s", metrics.PolicyEvalDurationMetricName)) }) } From 5b6152dcbb1222c1bd1a87497c036d720937e7c6 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Thu, 20 Jul 2023 13:14:22 +0200 Subject: [PATCH 121/172] fix: fix bench output in actions --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d77f88c4..660b9550 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: with: go-version: ${{ matrix.go_version }} - name: Run benchmark - run: make bench | tee output.txt + run: make bench && tee output.txt - name: Download previous benchmark data uses: actions/cache@v3 with: From e23eba33f51c42a15fcf45a1087e53a506dad224 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Thu, 20 Jul 2023 14:27:54 +0200 Subject: [PATCH 122/172] fix: fix benchmark --- mocks/bench.json | 74 ++++++++++++++++++++- sdk/evaluator_test.go | 148 +++++++++++++++++++++++++++++++++++++++++- sdk/sdk_test.go | 100 ---------------------------- 3 files changed, 219 insertions(+), 103 deletions(-) diff --git a/mocks/bench.json b/mocks/bench.json index ad8ff2de..d90f1016 100644 --- a/mocks/bench.json +++ b/mocks/bench.json @@ -13,10 +13,80 @@ "policyName": "allow_view_project" } }, - "summary": "Get a list of users", + "summary": "Get a list of project by id", "description": "The list can be filtered specifying the following parameters", "tags": [ - "Users" + "Projects" + ], + "parameters": [ + { + "type": "string", + "required": false, + "name": "projectId", + "in": "params" + } + ], + "responses": { + "200": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "pattern": "^[a-fA-F\\d]{24}$", + "description": "_id", + "example": "617973697254f500156168e2" + }, + "creatorId": { + "type": "string", + "description": "creatorId" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "example": "2020-09-16T12:00:00.000Z", + "description": "createdAt" + }, + "updaterId": { + "type": "string", + "description": "updaterId" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "example": "2020-09-16T12:00:00.000Z", + "description": "updatedAt" + }, + "__STATE__": { + "type": "string", + "description": "__STATE__" + }, + "name": { + "type": "string", + "description": "name of the user" + } + } + } + }, + "description": "Default Response" + } + } + } + }, + "/projects/": { + "get": { + "x-rond": { + "requestFlow": { + "policyName": "filter_projects", + "generateQuery": true + } + }, + "summary": "Get a list of projects", + "description": "The list can be filtered specifying the following parameters", + "tags": [ + "Projects" ], "parameters": [ { diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index d7fc30df..80fe8221 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -604,9 +604,32 @@ func BenchmarkEvaluateRequest(b *testing.B) { require.NoError(b, err) logger := logging.NewNoOpLogger() + mongoClient := mocks.MongoClientMock{ + FindOneResult: map[string]any{ + "_id": "project123", + "tenantId": "tenantId", + }, + FindOneExpectation: func(collectionName string, query interface{}) { + b.StopTimer() + require.Equal(b, "projects", collectionName) + + require.Equal(b, query, map[string]any{ + "$expr": map[string]any{ + "$eq": []any{ + "$_id", + map[string]any{ + "$toObjectId": "project123", + }, + }, + }, + }) + b.StartTimer() + }, + } + sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ - MongoClient: testmongoMock, + MongoClient: mergeMongoMockWithBindingsAndRoles(&mongoClient), }, }) require.NoError(b, err) @@ -636,6 +659,47 @@ func BenchmarkEvaluateRequest(b *testing.B) { } } +// func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { +// moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") +// require.NoError(b, err, "Unexpected error") + +// openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") +// require.NoError(b, err) + +// sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ +// EvaluatorOptions: &core.OPAEvaluatorOptions{ +// MongoClient: benchMongoMock, +// }, +// }) +// require.NoError(b, err) + +// b.ResetTimer() + +// for n := 0; n < b.N; n++ { +// b.StopTimer() +// headers := http.Header{} +// headers.Set("my-header", "value") +// recorder := httptest.NewRecorder() + +// rondInput := getFakeInput(b, core.InputRequest{ +// Path: "/projects/", +// Headers: headers, +// Method: http.MethodGet, +// PathParams: map[string]string{}, +// }, "") +// logger := logging.NewNoOpLogger() + +// b.StartTimer() +// evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/") +// require.NoError(b, err) +// evaluator.EvaluateRequestPolicy(context.Background(), rondInput, types.User{ +// UserID: "user1", +// }) +// b.StopTimer() +// require.Equal(b, http.StatusOK, recorder.Code) +// } +// } + func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if h, ok := t.(tHelper); ok { h.Helper() @@ -674,3 +738,85 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { return sdk } + +func mergeMongoMockWithBindingsAndRoles(mock *mocks.MongoClientMock) *mocks.MongoClientMock { + if mock.UserBindings != nil { + mock.UserBindings = []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"admin"}, + Groups: []string{"area_rocket"}, + Permissions: []string{"permission4"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "project123", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "notUsedByAnyone2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role6"}, + Permissions: []string{"permissionNotUsed"}, + CRUDDocumentState: "PRIVATE", + }, + { + BindingID: "binding-company-owner", + Subjects: []string{"user1"}, + Roles: []string{"company_owner"}, + Resource: &types.Resource{ + ResourceType: "company", + ResourceID: "myCompany", + }, + CRUDDocumentState: "PUBLIC", + }, + } + } + if mock.UserRoles != nil { + mock.UserRoles = []types.Role{ + { + RoleID: "company_owner", + Permissions: []string{"console.company.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "admin", + Permissions: []string{"console.project.view", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role6", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PRIVATE", + }, + { + RoleID: "notUsedByAnyone", + Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, + CRUDDocumentState: "PUBLIC", + }, + } + } + + return mock +} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index a2d0b03b..1025595b 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -271,106 +271,6 @@ type tHelper interface { Helper() } -var testmongoMock = &mocks.MongoClientMock{ - UserBindings: []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"admin"}, - Groups: []string{"area_rocket"}, - Permissions: []string{"permission4"}, - Resource: &types.Resource{ - ResourceType: "project", - ResourceID: "project123", - }, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding3", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission10", "permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding4", - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission11"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFiltering", - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFilteringFromSubject", - Subjects: []string{"filter_test"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role6"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PRIVATE", - }, - }, - UserRoles: []types.Role{ - { - RoleID: "admin", - Permissions: []string{"console.project.view", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role6", - Permissions: []string{"permission3", "permission5"}, - CRUDDocumentState: "PRIVATE", - }, - { - RoleID: "notUsedByAnyone", - Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, - CRUDDocumentState: "PUBLIC", - }, - }, -} - type FakeInput struct { request core.InputRequest clientType string From ad2653f88afb7ccb50b97b881377f820d00338d7 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Fri, 21 Jul 2023 17:53:11 +0200 Subject: [PATCH 123/172] fix benchmark --- Makefile | 2 +- mocks/bench-policies/policies.rego | 60 ++++++++++++------------- sdk/evaluator_test.go | 71 +++++++----------------------- 3 files changed, 46 insertions(+), 87 deletions(-) diff --git a/Makefile b/Makefile index 255fb49d..7d9b10cb 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ coverage: clean mongo-start .PHONY: bench bench: clean mongo-start - go test -benchmem -bench=^Bench ./... + go test -benchmem -bench=^Bench ./... -run=^Bench .PHONY: clean clean: diff --git a/mocks/bench-policies/policies.rego b/mocks/bench-policies/policies.rego index b9eb3ceb..4aa35532 100644 --- a/mocks/bench-policies/policies.rego +++ b/mocks/bench-policies/policies.rego @@ -8,7 +8,7 @@ allow_all { # filter_projects # -# Used permissions: +# Used permissions: # - "console.project.view" # - "console.company.project.view" # @@ -25,8 +25,8 @@ filter_projects { } # allow_view_project -# -# Used permissions: +# +# Used permissions: # - "console.project.view" # - "console.company.project.view" # @@ -37,20 +37,20 @@ allow_view_project { } { not user_has_permission_from_bindings("console.project.view", input.request.pathParams.id) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.id }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.id }] } }) user_has_permission_from_bindings("console.company.project.view", project.tenantId) } { not user_has_permission_from_bindings("console.project.view", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } }) user_has_permission_from_bindings("console.company.project.view", project.tenantId) } # allow_create_project -# -# Used groups: +# +# Used groups: # - "create_project" # allow_create_project { @@ -59,7 +59,7 @@ allow_create_project { # allow_commit # -# Used permissions: +# Used permissions: # - "console.project.configuration.update" # allow_commit { @@ -69,13 +69,13 @@ allow_commit { } { not user_has_permission_from_bindings("console.project.configuration.update", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.configuration.update", project.tenantId) } { not user_has_permission_from_bindings("console.project.configuration.update", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.configuration.update", project.tenantId) } @@ -93,13 +93,13 @@ allow_service_repository_creation { }{ not user_has_permission_from_bindings("console.project.service.repository.create", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.service.repository.create", project.tenantId) }{ not user_has_permission_from_bindings("console.project.service.repository.create", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.service.repository.create", project.tenantId) } @@ -116,13 +116,13 @@ allow_view_secret_envs_key { }{ not user_has_permission_from_bindings("console.project.view", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.view", project.tenantId) } { not user_has_permission_from_bindings("console.project.view", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.view", project.tenantId) } @@ -139,13 +139,13 @@ allow_manage_secret_envs { } { not user_has_permission_from_bindings("console.project.secreted_variables.manage", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.secreted_variables.manage", project.tenantId) } { not user_has_permission_from_bindings("console.project.secreted_variables.manage", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.secreted_variables.manage", project.tenantId) } @@ -159,7 +159,7 @@ allow_manage_secret_envs { # - console.company.project.environment.view projection_projects_list_environments [projects] { - projects := [projects_with_envs_filtered | + projects := [projects_with_envs_filtered | project := input.response.body[_] projectId := project._id allow_view_project with input.request.pathParams.id as projectId @@ -183,7 +183,7 @@ projection_project_environments [project] { allow_view_project not user_has_permission_from_bindings("console.project.environment.view", input.request.pathParams.projectId) project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } }) user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) project := input.response.body @@ -191,7 +191,7 @@ projection_project_environments [project] { allow_view_project not user_has_permission_from_bindings("console.project.environment.view", input.request.pathParams.projectId) project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } }) not user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) originalProject := input.response.body @@ -205,14 +205,14 @@ filter_envs(project) = allowed_envs { } { not user_has_permission_from_bindings("console.project.environment.view", project._id) project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } }) user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) allowed_envs := project.environments } { not user_has_permission_from_bindings("console.project.environment.view", project._id) project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } }) not user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) allowed_envs := [y | @@ -230,10 +230,10 @@ filter_envs(project) = allowed_envs { filter_values_from_secreted_envs [env_variables] { not user_has_permission_from_bindings("console.project.secreted_variables.manage", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } }) not user_has_permission_from_bindings("console.company.project.secreted_variables.manage", project.tenantId) - env_variables:= [x | + env_variables:= [x | envs_from_body:= input.response.body[_] x = {"key": envs_from_body.key, "value": ""} ] @@ -256,13 +256,13 @@ allow_deploy { } { not user_has_permission_from_bindings("console.project.deploy.trigger", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.deploy.trigger", project.tenantId) } { not user_has_permission_from_bindings("console.project.deploy.trigger", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.deploy.trigger", project.tenantId) } @@ -282,13 +282,13 @@ allow_pod_delete { } { not user_has_permission_from_bindings("console.project.k8s.pod.delete", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.k8s.pod.delete", project.tenantId) } { not user_has_permission_from_bindings("console.project.k8s.pod.delete", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.k8s.pod.delete", project.tenantId) } @@ -308,13 +308,13 @@ allow_manage_dashboard { } { not user_has_permission_from_bindings("console.project.dashboard.manage", input.request.pathParams.projectId) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId }] } }) user_has_permission_from_bindings("console.company.project.dashboard.manage", project.tenantId) } { not user_has_permission_from_bindings("console.project.dashboard.manage", input.request.query.projectId[0]) project := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } + "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.query.projectId[0]}] } }) user_has_permission_from_bindings("console.company.project.dashboard.manage", project.tenantId) } @@ -332,7 +332,7 @@ allow_manage_dashboard { # - ENABLE_PERMISSION_RESTART_POD_{ENVID} # - ENABLE_PERMISSION_MANAGE_DASHBOARD_{ENVID} projection_features_toggle[res] { - ft_not_allowed := {x | + ft_not_allowed := {x | some key, val in input.response.body ft_checker with input.ft as key x = key diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 80fe8221..e40ec1f6 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -604,7 +604,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { require.NoError(b, err) logger := logging.NewNoOpLogger() - mongoClient := mocks.MongoClientMock{ + mongoClient := &mocks.MongoClientMock{ FindOneResult: map[string]any{ "_id": "project123", "tenantId": "tenantId", @@ -629,7 +629,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ - MongoClient: mergeMongoMockWithBindingsAndRoles(&mongoClient), + MongoClient: mongoClient, }, }) require.NoError(b, err) @@ -640,7 +640,6 @@ func BenchmarkEvaluateRequest(b *testing.B) { b.StopTimer() headers := http.Header{} headers.Set("my-header", "value") - recorder := httptest.NewRecorder() rondInput := getFakeInput(b, core.InputRequest{ Path: "/projects/project123", @@ -653,53 +652,17 @@ func BenchmarkEvaluateRequest(b *testing.B) { b.StartTimer() evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/project123") require.NoError(b, err) - evaluator.EvaluateRequestPolicy(context.Background(), rondInput, types.User{}) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, getUserMockWithBindingsAndRoles()) b.StopTimer() - require.Equal(b, http.StatusOK, recorder.Code) + + require.NoError(b, err) + require.Equal(b, PolicyResult{ + QueryToProxy: []byte(""), + Allowed: true, + }, policyResult) } } -// func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { -// moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") -// require.NoError(b, err, "Unexpected error") - -// openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") -// require.NoError(b, err) - -// sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ -// EvaluatorOptions: &core.OPAEvaluatorOptions{ -// MongoClient: benchMongoMock, -// }, -// }) -// require.NoError(b, err) - -// b.ResetTimer() - -// for n := 0; n < b.N; n++ { -// b.StopTimer() -// headers := http.Header{} -// headers.Set("my-header", "value") -// recorder := httptest.NewRecorder() - -// rondInput := getFakeInput(b, core.InputRequest{ -// Path: "/projects/", -// Headers: headers, -// Method: http.MethodGet, -// PathParams: map[string]string{}, -// }, "") -// logger := logging.NewNoOpLogger() - -// b.StartTimer() -// evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/") -// require.NoError(b, err) -// evaluator.EvaluateRequestPolicy(context.Background(), rondInput, types.User{ -// UserID: "user1", -// }) -// b.StopTimer() -// require.Equal(b, http.StatusOK, recorder.Code) -// } -// } - func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if h, ok := t.(tHelper); ok { h.Helper() @@ -739,9 +702,9 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { return sdk } -func mergeMongoMockWithBindingsAndRoles(mock *mocks.MongoClientMock) *mocks.MongoClientMock { - if mock.UserBindings != nil { - mock.UserBindings = []types.Binding{ +func getUserMockWithBindingsAndRoles() types.User { + return types.User{ + UserBindings: []types.Binding{ { BindingID: "binding1", Subjects: []string{"user1"}, @@ -786,10 +749,8 @@ func mergeMongoMockWithBindingsAndRoles(mock *mocks.MongoClientMock) *mocks.Mong }, CRUDDocumentState: "PUBLIC", }, - } - } - if mock.UserRoles != nil { - mock.UserRoles = []types.Role{ + }, + UserRoles: []types.Role{ { RoleID: "company_owner", Permissions: []string{"console.company.project.view"}, @@ -815,8 +776,6 @@ func mergeMongoMockWithBindingsAndRoles(mock *mocks.MongoClientMock) *mocks.Mong Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, CRUDDocumentState: "PUBLIC", }, - } + }, } - - return mock } From 97d7c85a89a4a272bcc309b53ceff37ea6543120 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Fri, 21 Jul 2023 18:02:13 +0200 Subject: [PATCH 124/172] feat: add benchmark to query generation policy --- sdk/evaluator_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index e40ec1f6..2bed14ec 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -663,6 +663,45 @@ func BenchmarkEvaluateRequest(b *testing.B) { } } +func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { + moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") + require.NoError(b, err, "Unexpected error") + + openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") + require.NoError(b, err) + + sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, nil) + require.NoError(b, err) + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + b.StopTimer() + headers := http.Header{} + headers.Set("my-header", "value") + + rondInput := getFakeInput(b, core.InputRequest{ + Path: "/projects/", + Headers: headers, + Method: http.MethodGet, + PathParams: map[string]string{}, + }, "") + logger := logging.NewNoOpLogger() + + b.StartTimer() + evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/") + require.NoError(b, err) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, getUserMockWithBindingsAndRoles()) + b.StopTimer() + + require.NoError(b, err) + require.Equal(b, PolicyResult{ + QueryToProxy: []byte(`{"$or":[{"$and":[{"tenantId":{"$eq":"myCompany"}}]},{"$and":[{"_id":{"$eq":"project123"}}]}]}`), + Allowed: true, + }, policyResult) + } +} + func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if h, ok := t.(tHelper); ok { h.Helper() @@ -704,6 +743,7 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { func getUserMockWithBindingsAndRoles() types.User { return types.User{ + UserID: "user1", UserBindings: []types.Binding{ { BindingID: "binding1", From 16a053eb7c4108f5bf0036191efa812ea1116e13 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 13:52:15 +0200 Subject: [PATCH 125/172] add bench on response policy --- mocks/bench-policies/policies.rego | 97 ++++---- sdk/evaluator_test.go | 357 +++++++++++++++++++++-------- 2 files changed, 310 insertions(+), 144 deletions(-) diff --git a/mocks/bench-policies/policies.rego b/mocks/bench-policies/policies.rego index 4aa35532..7696501a 100644 --- a/mocks/bench-policies/policies.rego +++ b/mocks/bench-policies/policies.rego @@ -159,67 +159,64 @@ allow_manage_secret_envs { # - console.company.project.environment.view projection_projects_list_environments [projects] { - projects := [projects_with_envs_filtered | - project := input.response.body[_] - projectId := project._id - allow_view_project with input.request.pathParams.id as projectId - allowed_envs := filter_envs(project) - projects_with_envs_filtered := json.patch(project, [{"op": "replace", "path": "/environments", "value": allowed_envs}]) - ] + projects := [projects_with_envs_filtered | + project := input.response.body[_] + isAllowed := user_can_access_project_given_project(project) + isAllowed + allowed_envs := filter_envs(project.environments, project._id, project.tenantId) + projects_with_envs_filtered := json.patch(project, [{"op": "replace", "path": "/environments", "value": allowed_envs}]) + ] } # projection_project_environments -# + +user_can_access_project_given_project(project) = allowed { + allowed := user_has_permission_from_bindings("console.company.project.view", project.tenantId) +} { + not user_has_permission_from_bindings("console.company.project.view", project.tenantId) + allowed := user_has_permission_from_bindings("console.project.view", project._id) +} + # Used permissions: # - console.project.view # - console.company.project.view # - console.environment.view # - console.project.environment.view projection_project_environments [project] { - allow_view_project - user_has_permission_from_bindings("console.project.environment.view", input.request.pathParams.projectId) - project := input.response.body -} { - allow_view_project - not user_has_permission_from_bindings("console.project.environment.view", input.request.pathParams.projectId) - project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } - }) - user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) - project := input.response.body -}{ - allow_view_project - not user_has_permission_from_bindings("console.project.environment.view", input.request.pathParams.projectId) - project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": input.request.pathParams.projectId}] } - }) - not user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) - originalProject := input.response.body - allowed_envs := filter_envs(originalProject) - project := json.patch(originalProject, [{"op": "replace", "path": "environments", "value": allowed_envs}]) + project := input.response.body + project + user_can_access_project_given_project(project) + user_has_permission_from_bindings("console.project.environment.view", project._id) +} { + project := input.response.body + project + user_can_access_project_given_project(project) + not user_has_permission_from_bindings("console.project.environment.view", project._id) + user_has_permission_from_bindings("console.company.project.environment.view", project.tenantId) +} { + originalProject := input.response.body + user_can_access_project_given_project(originalProject) + not user_has_permission_from_bindings("console.project.environment.view", originalProject._id) + not user_has_permission_from_bindings("console.company.project.environment.view", originalProject.tenantId) + allowed_envs := filter_envs(originalProject.environments, originalProject._id, originalProject.tenantId) + project := json.patch(originalProject, [{"op": "replace", "path": "environments", "value": allowed_envs}]) } -filter_envs(project) = allowed_envs { - user_has_permission_from_bindings("console.project.environment.view", project._id) - allowed_envs := project.environments -} { - not user_has_permission_from_bindings("console.project.environment.view", project._id) - project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } - }) - user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) - allowed_envs := project.environments -} { - not user_has_permission_from_bindings("console.project.environment.view", project._id) - project_from_db := find_one("projects", { - "$expr": { "$eq": ["$_id", { "$toObjectId": project._id}] } - }) - not user_has_permission_from_bindings("console.company.project.environment.view", project_from_db.tenantId) - allowed_envs := [y | - environment := project.environments[_] - user_has_permission_from_bindings("console.environment.view", concat(":", [project._id, environment.envId])) - y := environment - ] +filter_envs(environments, mongoProjectId, tenantId) = allowed_envs { + user_has_permission_from_bindings("console.project.environment.view", mongoProjectId) + allowed_envs := environments +} { + not user_has_permission_from_bindings("console.project.environment.view", mongoProjectId) + user_has_permission_from_bindings("console.company.project.environment.view", tenantId) + allowed_envs := environments +} { + not user_has_permission_from_bindings("console.project.environment.view", mongoProjectId) + not user_has_permission_from_bindings("console.company.project.environment.view", tenantId) + allowed_envs := [y | + environment := environments[_] + user_has_permission_from_bindings("console.environment.view", concat(":", [mongoProjectId, environment.envId])) + y := environment + ] } # filter_values_from_secreted_envs diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 2bed14ec..6e96d47f 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -16,6 +16,7 @@ package sdk import ( "context" + "encoding/json" "net/http" "net/http/httptest" "testing" @@ -600,10 +601,6 @@ func BenchmarkEvaluateRequest(b *testing.B) { moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") require.NoError(b, err, "Unexpected error") - openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") - require.NoError(b, err) - - logger := logging.NewNoOpLogger() mongoClient := &mocks.MongoClientMock{ FindOneResult: map[string]any{ "_id": "project123", @@ -627,7 +624,76 @@ func BenchmarkEvaluateRequest(b *testing.B) { }, } - sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, &Options{ + user := types.User{ + UserID: "user1", + UserBindings: []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"admin"}, + Groups: []string{"area_rocket"}, + Permissions: []string{"permission4"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "project123", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding-company-owner", + Subjects: []string{"user1"}, + Roles: []string{"company_owner"}, + Resource: &types.Resource{ + ResourceType: "company", + ResourceID: "myCompany", + }, + CRUDDocumentState: "PUBLIC", + }, + }, + UserRoles: []types.Role{ + { + RoleID: "company_owner", + Permissions: []string{"console.company.project.view", "console.company.project.environment.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "admin", + Permissions: []string{"console.project.view", "console.project.environment.view", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role6", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PRIVATE", + }, + { + RoleID: "notUsedByAnyone", + Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, + CRUDDocumentState: "PUBLIC", + }, + }, + } + + config := core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "allow_view_project", + }, + } + + evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ EvaluatorOptions: &core.OPAEvaluatorOptions{ MongoClient: mongoClient, }, @@ -649,10 +715,9 @@ func BenchmarkEvaluateRequest(b *testing.B) { "projectId": "project123", }, }, "") + b.StartTimer() - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/project123") - require.NoError(b, err) - policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, getUserMockWithBindingsAndRoles()) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, user) b.StopTimer() require.NoError(b, err) @@ -667,10 +732,81 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") require.NoError(b, err, "Unexpected error") - openAPISpec, err := openapi.LoadOASFile("../mocks/bench.json") - require.NoError(b, err) + config := core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "filter_projects", + GenerateQuery: true, + }, + } + + user := types.User{ + UserID: "user1", + UserBindings: []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"admin"}, + Groups: []string{"area_rocket"}, + Permissions: []string{"permission4"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "project123", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding-company-owner", + Subjects: []string{"user1"}, + Roles: []string{"company_owner"}, + Resource: &types.Resource{ + ResourceType: "company", + ResourceID: "myCompany", + }, + CRUDDocumentState: "PUBLIC", + }, + }, + UserRoles: []types.Role{ + { + RoleID: "company_owner", + Permissions: []string{"console.company.project.view", "console.company.project.environment.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "admin", + Permissions: []string{"console.project.view", "console.project.environment.view", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role6", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PRIVATE", + }, + { + RoleID: "notUsedByAnyone", + Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, + CRUDDocumentState: "PUBLIC", + }, + }, + } - sdk, err := NewFromOAS(context.Background(), moduleConfig, openAPISpec, nil) + evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: mocks.MongoClientMock{}, + }, + }) require.NoError(b, err) b.ResetTimer() @@ -686,12 +822,9 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { Method: http.MethodGet, PathParams: map[string]string{}, }, "") - logger := logging.NewNoOpLogger() b.StartTimer() - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/projects/") - require.NoError(b, err) - policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, getUserMockWithBindingsAndRoles()) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, user) b.StopTimer() require.NoError(b, err) @@ -702,6 +835,121 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { } } +func BenchmarkEvaluateResponse(b *testing.B) { + moduleConfig, err := core.LoadRegoModule("../mocks/bench-policies") + require.NoError(b, err, "Unexpected error") + + config := core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "allow_all", + }, + ResponseFlow: core.ResponseFlow{ + PolicyName: "projection_project_environments", + }, + } + + user := types.User{ + UserID: "user1", + UserBindings: []types.Binding{{ + BindingID: "binding-env", + Subjects: []string{"user1"}, + Roles: []string{"env-reader"}, + Resource: &types.Resource{ + ResourceType: "environment", + ResourceID: "projectWithEnv:my-preprod-env", + }, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding-projectWithEnv-project-reader", + Subjects: []string{"user1"}, + Roles: []string{"project-reader"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "projectWithEnv", + }, + CRUDDocumentState: "PUBLIC", + }, + }, + UserRoles: []types.Role{ + { + RoleID: "env-reader", + Permissions: []string{"console.environment.view"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "project-reader", + Permissions: []string{"console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + }, + } + + evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ + EvaluatorOptions: &core.OPAEvaluatorOptions{ + MongoClient: &mocks.MongoClientMock{}, + }, + }) + require.NoError(b, err) + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + b.StopTimer() + headers := http.Header{} + headers.Set("my-header", "value") + + rondInput := getFakeInput(b, core.InputRequest{ + Path: "/projects/projectWithEnv", + Headers: headers, + Method: http.MethodGet, + PathParams: map[string]string{ + "projectId": "projectWithEnv", + }, + }, "") + + decodedBody := map[string]any{ + "_id": "projectWithEnv", + "projectId": "my-project", + "tenantId": "my-tenant", + "environments": []map[string]any{ + { + "envId": "my-dev-env", + }, + { + "envId": "my-preprod-env", + }, + { + "envId": "my-prod-env", + }, + }, + } + + b.StartTimer() + policyResult, err := evaluator.EvaluateResponsePolicy(context.Background(), rondInput, user, decodedBody) + b.StopTimer() + + require.NoError(b, err) + + expectedProject := map[string]any{ + "_id": "projectWithEnv", + "projectId": "my-project", + "tenantId": "my-tenant", + "environments": []interface{}{ + map[string]any{ + "envId": "my-preprod-env", + }, + }, + } + + actualProject := map[string]any{} + err = json.Unmarshal(policyResult, &actualProject) + require.NoError(b, err) + + require.Equal(b, expectedProject, actualProject) + } +} + func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { if h, ok := t.(tHelper); ok { h.Helper() @@ -740,82 +988,3 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { return sdk } - -func getUserMockWithBindingsAndRoles() types.User { - return types.User{ - UserID: "user1", - UserBindings: []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"admin"}, - Groups: []string{"area_rocket"}, - Permissions: []string{"permission4"}, - Resource: &types.Resource{ - ResourceType: "project", - ResourceID: "project123", - }, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "notUsedByAnyone2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role6"}, - Permissions: []string{"permissionNotUsed"}, - CRUDDocumentState: "PRIVATE", - }, - { - BindingID: "binding-company-owner", - Subjects: []string{"user1"}, - Roles: []string{"company_owner"}, - Resource: &types.Resource{ - ResourceType: "company", - ResourceID: "myCompany", - }, - CRUDDocumentState: "PUBLIC", - }, - }, - UserRoles: []types.Role{ - { - RoleID: "company_owner", - Permissions: []string{"console.company.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "admin", - Permissions: []string{"console.project.view", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role6", - Permissions: []string{"permission3", "permission5"}, - CRUDDocumentState: "PRIVATE", - }, - { - RoleID: "notUsedByAnyone", - Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, - CRUDDocumentState: "PUBLIC", - }, - }, - } -} From a0d25e48a0ce023044d42da7810b0023c4a5311f Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 15:09:33 +0200 Subject: [PATCH 126/172] update bench --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 660b9550..d77f88c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: with: go-version: ${{ matrix.go_version }} - name: Run benchmark - run: make bench && tee output.txt + run: make bench | tee output.txt - name: Download previous benchmark data uses: actions/cache@v3 with: From 940fff327825026c0004c1373995ce655852d255 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 15:55:42 +0200 Subject: [PATCH 127/172] remove cache --- .github/workflows/test.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d77f88c4..e494b50c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,6 +55,16 @@ jobs: external-data-json-path: ./cache/benchmark-data.json comment-on-alert: false fail-on-alert: true + # only execute this step when cache was restored + # do not fail hard here, as the key might not exist + - name: clear + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + gh extension install actions/gh-actions-cache + gh actions-cache delete '${{ runner.os }}-benchmark' --confirm + continue-on-error: true build: name: Build docker image From 4c0424e6d9967af12e20658a846d6521e0750b36 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 16:05:05 +0200 Subject: [PATCH 128/172] update cache management --- .github/workflows/test.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e494b50c..71c54ed2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - name: Run benchmark run: make bench | tee output.txt - name: Download previous benchmark data - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ./cache key: ${{ runner.os }}-benchmark @@ -57,14 +57,20 @@ jobs: fail-on-alert: true # only execute this step when cache was restored # do not fail hard here, as the key might not exist - - name: clear + - name: Clear cache shell: bash + if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} env: GH_TOKEN: ${{ github.token }} run: | gh extension install actions/gh-actions-cache gh actions-cache delete '${{ runner.os }}-benchmark' --confirm continue-on-error: true + - name: Download previous benchmark data + uses: actions/cache/save@v3 + with: + path: ./cache + key: ${{ runner.os }}-benchmark build: name: Build docker image From 237c078342ca0f257c007d72f91df3869f769e77 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 16:21:52 +0200 Subject: [PATCH 129/172] update only on default branch --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 71c54ed2..40633d7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,6 +67,7 @@ jobs: gh actions-cache delete '${{ runner.os }}-benchmark' --confirm continue-on-error: true - name: Download previous benchmark data + if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} uses: actions/cache/save@v3 with: path: ./cache From 6b079bb153efe09beb405864fdce786e15fd24d0 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 16:25:27 +0200 Subject: [PATCH 130/172] remove bench.json oas --- mocks/bench.json | 149 ----------------------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 mocks/bench.json diff --git a/mocks/bench.json b/mocks/bench.json deleted file mode 100644 index d90f1016..00000000 --- a/mocks/bench.json +++ /dev/null @@ -1,149 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Crud Service", - "description": "HTTP interface to perform CRUD operations on MongoDB collections defined in the API Console", - "version": "3.2.3" - }, - "paths": { - "/projects/:projectId": { - "get": { - "x-rond": { - "requestFlow": { - "policyName": "allow_view_project" - } - }, - "summary": "Get a list of project by id", - "description": "The list can be filtered specifying the following parameters", - "tags": [ - "Projects" - ], - "parameters": [ - { - "type": "string", - "required": false, - "name": "projectId", - "in": "params" - } - ], - "responses": { - "200": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "pattern": "^[a-fA-F\\d]{24}$", - "description": "_id", - "example": "617973697254f500156168e2" - }, - "creatorId": { - "type": "string", - "description": "creatorId" - }, - "createdAt": { - "type": "string", - "format": "date-time", - "example": "2020-09-16T12:00:00.000Z", - "description": "createdAt" - }, - "updaterId": { - "type": "string", - "description": "updaterId" - }, - "updatedAt": { - "type": "string", - "format": "date-time", - "example": "2020-09-16T12:00:00.000Z", - "description": "updatedAt" - }, - "__STATE__": { - "type": "string", - "description": "__STATE__" - }, - "name": { - "type": "string", - "description": "name of the user" - } - } - } - }, - "description": "Default Response" - } - } - } - }, - "/projects/": { - "get": { - "x-rond": { - "requestFlow": { - "policyName": "filter_projects", - "generateQuery": true - } - }, - "summary": "Get a list of projects", - "description": "The list can be filtered specifying the following parameters", - "tags": [ - "Projects" - ], - "parameters": [ - { - "type": "string", - "required": false, - "name": "projectId", - "in": "params" - } - ], - "responses": { - "200": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "pattern": "^[a-fA-F\\d]{24}$", - "description": "_id", - "example": "617973697254f500156168e2" - }, - "creatorId": { - "type": "string", - "description": "creatorId" - }, - "createdAt": { - "type": "string", - "format": "date-time", - "example": "2020-09-16T12:00:00.000Z", - "description": "createdAt" - }, - "updaterId": { - "type": "string", - "description": "updaterId" - }, - "updatedAt": { - "type": "string", - "format": "date-time", - "example": "2020-09-16T12:00:00.000Z", - "description": "updatedAt" - }, - "__STATE__": { - "type": "string", - "description": "__STATE__" - }, - "name": { - "type": "string", - "description": "name of the user" - } - } - } - }, - "description": "Default Response" - } - } - } - } - } -} From c1c415728c93cc29a36f53d2bd10cecf00f6ae5c Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Mon, 24 Jul 2023 16:45:45 +0200 Subject: [PATCH 131/172] update benchmark data --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40633d7c..f7799cde 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: gh extension install actions/gh-actions-cache gh actions-cache delete '${{ runner.os }}-benchmark' --confirm continue-on-error: true - - name: Download previous benchmark data + - name: Update benchmark data if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} uses: actions/cache/save@v3 with: From 7aec23dba3e0ab15e512763f44e6b182242addf3 Mon Sep 17 00:00:00 2001 From: Davide Bianchi Date: Tue, 25 Jul 2023 11:12:17 +0200 Subject: [PATCH 132/172] feat: add multi arch image --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7799cde..a3a2cf52 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -102,6 +102,9 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Docker Login to ghcr.io uses: docker/login-action@v2 with: @@ -127,6 +130,7 @@ jobs: uses: docker/build-push-action@v4 with: context: . + platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From 5a7fd69ad51dabba0385113db688008cda287f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:10:09 +0200 Subject: [PATCH 133/172] chore(deps): bump github.com/open-policy-agent/opa from 0.54.0 to 0.55.0 (#231) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.54.0 to 0.55.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.54.0...v0.55.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 15 ++++++++------- go.sum | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 08897632..f50ad835 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v4 v4.0.0 github.com/mia-platform/go-crud-service-client v0.10.0 - github.com/open-policy-agent/opa v0.54.0 + github.com/open-policy-agent/opa v0.55.0 github.com/prometheus/client_golang v1.16.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 @@ -76,14 +76,15 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/sdk v1.14.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect - golang.org/x/crypto v0.10.0 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect - golang.org/x/sync v0.2.0 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index be460c84..1efe0c3a 100644 --- a/go.sum +++ b/go.sum @@ -72,7 +72,7 @@ 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/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 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= @@ -387,8 +387,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.54.0 h1:mGEsK+R5ZTMV8fzzbNzmYDGbTmY30wmRCIHmtm2VqWs= -github.com/open-policy-agent/opa v0.54.0/go.mod h1:d8I8jWygKGi4+T4H07qrbeCdH1ITLsEfT0M+bsvxWw0= +github.com/open-policy-agent/opa v0.55.0 h1:s7Vm4ph6zDqqP/KzvUSw9fsKVsm9lhbTZhYGxxTK7mo= +github.com/open-policy-agent/opa v0.55.0/go.mod h1:2Vh8fj/bXCqSwGMbBiHGrw+O8yrho6T/fdaHt5ROmaQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -539,18 +539,19 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0 h1:yt2NKzK7Vyo6h0+X8BA4FpreZQTlVEIarnsBP/H5mzs= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= -go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= -go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= -go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -570,8 +571,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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= @@ -650,7 +651,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= 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= @@ -672,8 +673,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -753,8 +754,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -902,7 +903,7 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= +google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= 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= From fe510668afcf1365811afa2302f13ad626427f17 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 31 Jul 2023 18:12:46 +0200 Subject: [PATCH 134/172] feat(#207): add user id to input struct --- core/input.go | 1 + main_test.go | 17 +++++++++++++++++ mocks/rego-policies/example.rego | 6 +++++- mocks/simplifiedMock.json | 7 +++++++ openapi/evaluators_test.go | 2 +- openapi/openapi_utils_test.go | 14 ++++++++++++++ sdk/evaluator_test.go | 2 ++ sdk/rondinput/http/input.go | 1 + sdk/rondinput/http/input_test.go | 19 +++++++++++++++++++ sdk/sdk_test.go | 1 + 10 files changed, 68 insertions(+), 2 deletions(-) diff --git a/core/input.go b/core/input.go index a2384fbf..d9e840d4 100644 --- a/core/input.go +++ b/core/input.go @@ -46,6 +46,7 @@ type InputResponse struct { } type InputUser struct { + ID string `json:"id,omitempty"` Properties map[string]interface{} `json:"properties,omitempty"` Groups []string `json:"groups,omitempty"` Bindings []types.Binding `json:"bindings,omitempty"` diff --git a/main_test.go b/main_test.go index 7804301e..abf832bb 100644 --- a/main_test.go +++ b/main_test.go @@ -312,6 +312,23 @@ func TestEntrypoint(t *testing.T) { require.True(t, gock.IsDone(), "the proxy blocks the request when the permissions are granted.") }) + t.Run("ok - user assertions", func(t *testing.T) { + gock.Flush() + gock.New("http://localhost:3001/"). + Get("/assert-user"). + Reply(200) + + req, err := http.NewRequest(http.MethodGet, "http://localhost:3000/assert-user", nil) + require.Nil(t, err) + + req.Header.Set("miauserid", "the-user-id") + resp, err := http.DefaultClient.Do(req) + + require.Equal(t, nil, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + require.True(t, gock.IsDone(), "the proxy blocks the request when the permissions are granted.") + }) + t.Run("forbidden - opa evaluation fail", func(t *testing.T) { gock.Flush() gock.New("http://localhost:3001/"). diff --git a/mocks/rego-policies/example.rego b/mocks/rego-policies/example.rego index 5031aff4..349c59d4 100644 --- a/mocks/rego-policies/example.rego +++ b/mocks/rego-policies/example.rego @@ -83,6 +83,10 @@ testingPathParamsAbsence { } allow_view { - id := object.get(input,["request","pathParams", "id"], false) + id := object.get(input,["request","pathParams", "id"], false) id } + +assert_user { + input.user.id == "the-user-id" +} \ No newline at end of file diff --git a/mocks/simplifiedMock.json b/mocks/simplifiedMock.json index 138cbd08..1d1edc33 100644 --- a/mocks/simplifiedMock.json +++ b/mocks/simplifiedMock.json @@ -6,6 +6,13 @@ "version": "3.2.3" }, "paths": { + "/assert-user": { + "get": { + "x-permission": { + "allow": "assert_user" + } + } + }, "/users/": { "head": { "x-permission": { diff --git a/openapi/evaluators_test.go b/openapi/evaluators_test.go index 17fda8dd..b3d09cf3 100644 --- a/openapi/evaluators_test.go +++ b/openapi/evaluators_test.go @@ -41,7 +41,7 @@ func TestCreatePolicyEvaluators(t *testing.T) { policyEvals, err := SetupEvaluators(ctx, logger, openApiSpec, opaModuleConfig, nil) require.NoError(t, err, "unexpected error creating evaluators") - require.Len(t, policyEvals, 4, "unexpected length") + require.Len(t, policyEvals, 5, "unexpected length") }) t.Run("with complete oas mock", func(t *testing.T) { diff --git a/openapi/openapi_utils_test.go b/openapi/openapi_utils_test.go index a96aa8c1..c7d94cd2 100644 --- a/openapi/openapi_utils_test.go +++ b/openapi/openapi_utils_test.go @@ -46,6 +46,13 @@ func TestFetchOpenAPI(t *testing.T) { require.NoError(t, err, "unexpected error") require.NotNil(t, openApiSpec, "unexpected nil result") require.Equal(t, OpenAPIPaths{ + "/assert-user": PathVerbs{ + "get": VerbConfig{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "assert_user"}, + }, + }, + }, "/users/": PathVerbs{ "get": VerbConfig{ PermissionV2: &core.RondConfig{ @@ -226,6 +233,13 @@ func TestLoadOAS(t *testing.T) { require.NoError(t, err, "unexpected error") require.NotNil(t, openApiSpec, "unexpected nil result") require.Equal(t, OpenAPIPaths{ + "/assert-user": PathVerbs{ + "get": VerbConfig{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "assert_user"}, + }, + }, + }, "/users/": PathVerbs{ "get": VerbConfig{ PermissionV2: &core.RondConfig{ diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 6e96d47f..413fdfb5 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -150,6 +150,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { method: http.MethodGet, path: "/users/", user: types.User{ + UserID: "the-user-id", UserGroups: []string{"my-group"}, UserRoles: []types.Role{ { @@ -169,6 +170,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, opaModuleContent: `package policies todo { + input.user.id == "the-user-id" input.user.groups[0] == "my-group" input.user.roles[0].roleId == "rid" input.user.bindings[0].resource.resourceType == "my-resource" diff --git a/sdk/rondinput/http/input.go b/sdk/rondinput/http/input.go index 79ae226b..b6fd4252 100644 --- a/sdk/rondinput/http/input.go +++ b/sdk/rondinput/http/input.go @@ -63,6 +63,7 @@ func (req requestInfo) Input(user types.User, responseBody any) (core.Input, err Body: responseBody, }, User: core.InputUser{ + ID: user.UserID, Properties: user.Properties, Groups: user.UserGroups, Bindings: user.UserBindings, diff --git a/sdk/rondinput/http/input_test.go b/sdk/rondinput/http/input_test.go index e8635d12..5b1b8324 100644 --- a/sdk/rondinput/http/input_test.go +++ b/sdk/rondinput/http/input_test.go @@ -101,4 +101,23 @@ func TestRondInput(t *testing.T) { require.Nil(t, input.Request.Body) }) }) + + t.Run("request userinfo remapping", func(t *testing.T) { + user := types.User{ + UserID: "UserID", + UserGroups: []string{"UserGroups"}, + UserRoles: []types.Role{}, + UserBindings: []types.Binding{}, + Properties: map[string]any{"key": "val"}, + } + + req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader([]byte{})) + + rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) + input, err := rondRequest.Input(user, nil) + + require.NoError(t, err, "Unexpected error") + require.Equal(t, user.UserID, input.User.ID) + require.EqualValues(t, user.Properties, input.User.Properties) + }) } diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 1025595b..a79d84a3 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -279,6 +279,7 @@ type FakeInput struct { func (i FakeInput) Input(user types.User, responseBody any) (core.Input, error) { return core.Input{ User: core.InputUser{ + ID: user.UserID, Properties: user.Properties, Groups: user.UserGroups, Bindings: user.UserBindings, From c7732b1664d10756b14ead28eb9fc41349c36234 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 31 Jul 2023 18:18:41 +0200 Subject: [PATCH 135/172] refactor: example rego policies lint --- mocks/rego-policies/example.rego | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mocks/rego-policies/example.rego b/mocks/rego-policies/example.rego index 349c59d4..c4860620 100644 --- a/mocks/rego-policies/example.rego +++ b/mocks/rego-policies/example.rego @@ -83,10 +83,10 @@ testingPathParamsAbsence { } allow_view { - id := object.get(input,["request","pathParams", "id"], false) - id + id := object.get(input,["request","pathParams", "id"], false) + id } assert_user { input.user.id == "the-user-id" -} \ No newline at end of file +} From 5830acf46bdf8a1f289363479cebcd9cf7c8d760 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 31 Jul 2023 18:28:28 +0200 Subject: [PATCH 136/172] fix: ports --- main_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/main_test.go b/main_test.go index abf832bb..542b9ca9 100644 --- a/main_test.go +++ b/main_test.go @@ -314,18 +314,19 @@ func TestEntrypoint(t *testing.T) { t.Run("ok - user assertions", func(t *testing.T) { gock.Flush() + gock.New("http://localhost:3001/"). Get("/assert-user"). Reply(200) req, err := http.NewRequest(http.MethodGet, "http://localhost:3000/assert-user", nil) require.Nil(t, err) - req.Header.Set("miauserid", "the-user-id") - resp, err := http.DefaultClient.Do(req) + resp, err := http.DefaultClient.Do(req) require.Equal(t, nil, err) require.Equal(t, http.StatusOK, resp.StatusCode) + require.True(t, gock.IsDone(), "the proxy blocks the request when the permissions are granted.") }) @@ -1300,7 +1301,7 @@ func TestEntrypoint(t *testing.T) { File("./mocks/pathsWithWildCardCollision2.json") setEnvs(t, []env{ - {name: "HTTP_PORT", value: "3039"}, + {name: "HTTP_PORT", value: "3060"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3038"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, {name: "OPA_MODULES_DIRECTORY", value: "./mocks/rego-policies"}, @@ -1350,7 +1351,7 @@ func TestEntrypoint(t *testing.T) { gock.New("http://localhost:3038/foo/count"). Get("/foo/count"). Reply(200) - req, err := http.NewRequest("GET", "http://localhost:3039/foo/count", nil) + req, err := http.NewRequest("GET", "http://localhost:3060/foo/count", nil) require.NoError(t, err) req.Header.Set("miausergroups", "group1") @@ -1384,7 +1385,7 @@ func TestEntrypoint(t *testing.T) { File("./mocks/pathsWithWildCardCollision.json") setEnvs(t, []env{ - {name: "HTTP_PORT", value: "3039"}, + {name: "HTTP_PORT", value: "3070"}, {name: "TARGET_SERVICE_HOST", value: "localhost:3038"}, {name: "TARGET_SERVICE_OAS_PATH", value: "/documentation/json"}, {name: "OPA_MODULES_DIRECTORY", value: "./mocks/rego-policies"}, @@ -1434,7 +1435,7 @@ func TestEntrypoint(t *testing.T) { gock.New("http://localhost:3038/foo/count"). Patch("/foo/count"). Reply(200) - req, err := http.NewRequest("PATCH", "http://localhost:3039/foo/count", nil) + req, err := http.NewRequest("PATCH", "http://localhost:3070/foo/count", nil) require.NoError(t, err) req.Header.Set("miausergroups", "group1") From 529f43134ec9147a2b10d5ae49b7817de40397a6 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 31 Jul 2023 18:39:08 +0200 Subject: [PATCH 137/172] fix: mock --- main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main_test.go b/main_test.go index 542b9ca9..94ac63cc 100644 --- a/main_test.go +++ b/main_test.go @@ -273,7 +273,7 @@ func TestEntrypoint(t *testing.T) { if r.URL.Path == "/documentation/json" { return false } - if r.URL.Path == "/users/" && r.URL.Host == "localhost:3001" { + if (r.URL.Path == "/users/" || r.URL.Path == "/assert-user") && r.URL.Host == "localhost:3001" { return false } return true From bb6f1a563a3fd2b9335c720f6e529bb7352f0490 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Mon, 31 Jul 2023 18:40:04 +0200 Subject: [PATCH 138/172] refactor: removed empty line from rego file --- mocks/rego-policies/example.rego | 1 - 1 file changed, 1 deletion(-) diff --git a/mocks/rego-policies/example.rego b/mocks/rego-policies/example.rego index c4860620..9223f358 100644 --- a/mocks/rego-policies/example.rego +++ b/mocks/rego-policies/example.rego @@ -60,7 +60,6 @@ ft_checker { true } - response_policy1 { true } From a0a816aa4fa28749c8d4b865ff4756d8585dc5a5 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 1 Aug 2023 09:43:59 +0200 Subject: [PATCH 139/172] Upgrade version to v1.9.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7976196e..b1486f76 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" -ENV SERVICE_VERSION="1.8.0" +ENV SERVICE_VERSION="1.9.0" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From f4212ffc5a019289642cfa7f7f5e4e06abb2acf7 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 1 Aug 2023 09:44:45 +0200 Subject: [PATCH 140/172] ci: changed commit message for version bump --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7d9b10cb..fd8d8274 100644 --- a/Makefile +++ b/Makefile @@ -37,5 +37,5 @@ version: sed -i.bck "s|SERVICE_VERSION=\"[0-9]*.[0-9]*.[0-9]*.*\"|SERVICE_VERSION=\"${VERSION}\"|" "Dockerfile" rm -fr "Dockerfile.bck" git add "Dockerfile" - git commit -m "Upgrade version to v${VERSION}" + git commit -m "v${VERSION}" git tag v${VERSION} From eee1130b518d42da5de72b370a276682d745b524 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Tue, 1 Aug 2023 10:53:15 +0200 Subject: [PATCH 141/172] ci: new job names --- .github/workflows/test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a3a2cf52..580e0361 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,8 @@ on: push: jobs: tests: - name: Test with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + name: Test + run-name: Tests with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: @@ -29,7 +30,8 @@ jobs: path-to-profile: coverage.out bench: - name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + name: Benchmark + run-name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: From 18219b26fe0fe82e4d86fe9c4de44c31ed881a52 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:31:13 +0200 Subject: [PATCH 142/172] fix: commented invalid run-name --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 580e0361..5eb4b4c9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: jobs: tests: name: Test - run-name: Tests with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + #run-name: Tests with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: @@ -31,7 +31,7 @@ jobs: bench: name: Benchmark - run-name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} + #run-name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: From 04545e01dd029136e9b4f8a4937090648adb020a Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:33:42 +0200 Subject: [PATCH 143/172] update: go 1.20 (added latest) --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5eb4b4c9..13819573 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - go_version: [1.19] + go_version: ['1.20', latest] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -35,7 +35,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - go_version: [1.19] + go_version: ['1.20'] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 From 3baf8ac47b15d209f90bc187a88882785341f6d6 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:40:36 +0200 Subject: [PATCH 144/172] feat: add latest version tests --- .github/workflows/test.yml | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13819573..285ec339 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,12 +6,12 @@ on: jobs: tests: name: Test - #run-name: Tests with go version ${{ matrix.go_version }} on OS ${{matrix.os}} - runs-on: ${{ matrix.os }} strategy: matrix: go_version: ['1.20', latest] os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v3 - name: Use golang ${{ matrix.go_version }} @@ -29,9 +29,32 @@ jobs: with: path-to-profile: coverage.out + test-latest: + name: Test latest + strategy: + matrix: + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + continue-on-error: true + steps: + - uses: actions/checkout@v3 + - name: Use golang ${{ matrix.go_version }} + uses: actions/setup-go@v4 + with: + check-latest: true + - name: Go version + run: go version + - name: Go get dependencies + run: go get -v -t -d ./... + - name: Run tests + run: make coverage + - name: Send the coverage output + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: coverage.out + bench: name: Benchmark - #run-name: Bench with go version ${{ matrix.go_version }} on OS ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: From 21fea347c7bf41f4fb5aa6d1276952f7c3bf0455 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:42:09 +0200 Subject: [PATCH 145/172] fix: rem latest from matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 285ec339..082f6c64 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: name: Test strategy: matrix: - go_version: ['1.20', latest] + go_version: ['1.20'] os: [ubuntu-latest] runs-on: ${{ matrix.os }} From 04073d57900f3b9e4258630a6b526d6faea4ac0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:37:49 +0200 Subject: [PATCH 146/172] chore(deps): bump golang from 1.20.6 to 1.20.7 (#236) Bumps golang from 1.20.6 to 1.20.7. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b1486f76..fb7fa6bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.20.6 AS builder +FROM golang:1.20.7 AS builder WORKDIR /app From 8e71623dc68305282631673086660ad231802ba4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:39:25 +0200 Subject: [PATCH 147/172] chore(deps): bump go.mongodb.org/mongo-driver from 1.12.0 to 1.12.1 (#237) Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.12.0 to 1.12.1. - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.12.0...v1.12.1) --- updated-dependencies: - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f50ad835..5a312d7a 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 github.com/uptrace/bunrouter v1.0.20 - go.mongodb.org/mongo-driver v1.12.0 + go.mongodb.org/mongo-driver v1.12.1 gopkg.in/h2non/gock.v1 v1.1.2 ) diff --git a/go.sum b/go.sum index 1efe0c3a..fc349903 100644 --- a/go.sum +++ b/go.sum @@ -531,8 +531,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= -go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= +go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= 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= From a971f8d82bd02195b7d1bbc23c3156676931002a Mon Sep 17 00:00:00 2001 From: Mattia Date: Wed, 9 Aug 2023 09:31:31 +0200 Subject: [PATCH 148/172] feat: revoke binding by resource type and resource id (#238) * test * impl * better impl * better impl * rename conditions * better list query --- service/standalone_apis.go | 22 +++++++++---- service/standalone_apis_test.go | 57 +++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/service/standalone_apis.go b/service/standalone_apis.go index 64f4f6ae..595b1b33 100644 --- a/service/standalone_apis.go +++ b/service/standalone_apis.go @@ -59,12 +59,11 @@ func revokeHandler(w http.ResponseWriter, r *http.Request) { } resourceType := mux.Vars(r)["resourceType"] - if resourceType != "" && len(reqBody.ResourceIDs) == 0 { - utils.FailResponseWithCode(w, http.StatusBadRequest, "empty resources list", utils.GENERIC_BUSINESS_ERROR_MESSAGE) - return - } - if len(reqBody.Subjects) == 0 && len(reqBody.Groups) == 0 { - utils.FailResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists", utils.GENERIC_BUSINESS_ERROR_MESSAGE) + hasResourceID := resourceType != "" && len(reqBody.ResourceIDs) > 0 + hasSubjectsOrGroups := len(reqBody.Subjects) > 0 || len(reqBody.Groups) > 0 + + if !hasResourceID && !hasSubjectsOrGroups { + utils.FailResponseWithCode(w, http.StatusBadRequest, "empty subjects and groups lists or resource ids", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } @@ -265,6 +264,13 @@ func buildQuery(resourceType string, resourceIDs []string, subjects []string, gr return queryPartForSubjectOrGroups } + if parts := queryPartForSubjectOrGroups["$or"].([]map[string]any); len(parts) == 0 { + return map[string]any{ + "resource.resourceType": resourceType, + "resource.resourceId": map[string]any{"$in": resourceIDs}, + } + } + query := map[string]interface{}{ "$and": []map[string]interface{}{ { @@ -317,6 +323,10 @@ func prepareBindings(bindings []types.Binding, reqBody RevokeRequestBody) ([]typ var bindingToPatch []types.Binding var bindingToDelete []types.Binding + if len(reqBody.Groups) == 0 && len(reqBody.Subjects) == 0 { + return bindingToPatch, bindings + } + for _, binding := range bindings { binding.Subjects = utils.FilterList(binding.Subjects, reqBody.Subjects) if binding.Subjects == nil { diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 5956b5d7..46edd28c 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "net/http" "net/http/httptest" "strings" @@ -45,9 +46,8 @@ func TestRevokeHandler(t *testing.T) { t.Run("400 on missing subjects and groups", func(t *testing.T) { reqBody := setupRevokeRequestBody(t, RevokeRequestBody{ - Subjects: []string{}, - Groups: []string{}, - ResourceIDs: []string{"mike"}, + Subjects: []string{}, + Groups: []string{}, }) req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/", bytes.NewBuffer(reqBody)) require.NoError(t, err, "unexpected error") @@ -59,10 +59,7 @@ func TestRevokeHandler(t *testing.T) { }) t.Run("400 on missing resourceIds from body if resourceType request param is present", func(t *testing.T) { - reqBody := setupRevokeRequestBody(t, RevokeRequestBody{ - Subjects: []string{"piero"}, - Groups: []string{"litfiba"}, - }) + reqBody := setupRevokeRequestBody(t, RevokeRequestBody{}) req := requestWithParams(t, ctx, http.MethodPost, "/", bytes.NewBuffer(reqBody), map[string]string{ "resourceType": "project", @@ -177,6 +174,52 @@ func TestRevokeHandler(t *testing.T) { require.Equal(t, http.StatusOK, w.Result().StatusCode) }) + t.Run("performs correct delete query only on resource type and id", func(t *testing.T) { + bindingsFromCrud := []types.Binding{ + { + BindingID: "bindingToDelete", + Subjects: []string{"piero"}, + Resource: &types.Resource{ + ResourceType: "project", + ResourceID: "mike", + }, + }, + } + gock.DisableNetworking() + newGockScope(t, "http://crud-service", http.MethodGet, "/bindings/"). + AddMatcher(func(req *http.Request, greq *gock.Request) (bool, error) { + expected := `{"resource.resourceId":{"$in":["mike"]},"resource.resourceType":"myResource"}` + mongoQuery := req.URL.Query().Get("_q") + return assert.Equal(t, expected, mongoQuery), nil + }). + Reply(http.StatusOK). + JSON(bindingsFromCrud) + + newGockScope(t, "http://crud-service", http.MethodDelete, "/bindings/"). + AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { + expected := `{"bindingId":{"$in":["bindingToDelete"]}}` + mongoQuery := req.URL.Query().Get("_q") + return assert.Equal(t, expected, mongoQuery), nil + }). + Reply(http.StatusOK). + BodyString("1") + + reqBody := setupRevokeRequestBody(t, RevokeRequestBody{ + ResourceIDs: []string{"mike"}, + }) + + req := requestWithParams(t, ctx, http.MethodPost, "/", bytes.NewBuffer(reqBody), map[string]string{ + "resourceType": "myResource", + }) + w := httptest.NewRecorder() + + revokeHandler(w, req) + + body, _ := ioutil.ReadAll(w.Body) + require.Equal(t, http.StatusOK, w.Result().StatusCode, string(body)) + require.True(t, gock.IsDone()) + }) + t.Run("performs correct delete query only on subject", func(t *testing.T) { defer gock.Flush() From 3aef48e05e1ee19b31ef2f5f023a40ada4c77b7d Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:00:43 +0200 Subject: [PATCH 149/172] feat: added OCI description label in dockerfile (#239) --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index fb7fa6bf..8b90b7b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,8 @@ LABEL maintainer="rond@rond-authz.io" \ name="rond" \ vcs.sha="$COMMIT_SHA" +LABEL org.opencontainers.image.description "Rönd is a lightweight container that distributes security policy enforcement throughout your application." + ENV SERVICE_VERSION="1.9.0" # Import the user and group files from the builder. From 1dd00abd1bf18c1cabd406e6a9bb4bf5c4e658f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:15:09 +0200 Subject: [PATCH 150/172] chore(deps): bump golang from 1.20.7 to 1.21.0 (#240) Bumps golang from 1.20.7 to 1.21.0. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8b90b7b8..03f02ca4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.20.7 AS builder +FROM golang:1.21.0 AS builder WORKDIR /app From 69d2df9969b72a2b494b05edeed626e7bc143e64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:04:25 +0200 Subject: [PATCH 151/172] chore(deps): bump github.com/google/uuid from 1.3.0 to 1.3.1 (#241) Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/google/uuid/releases) - [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) --- updated-dependencies: - dependency-name: github.com/google/uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5a312d7a..cc24b562 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/davidebianchi/gswagger v0.9.0 github.com/getkin/kin-openapi v0.118.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v4 v4.0.0 diff --git a/go.sum b/go.sum index fc349903..877d98b6 100644 --- a/go.sum +++ b/go.sum @@ -231,8 +231,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= From 376d071956dd74f032b0900bd576bec26256b569 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Wed, 30 Aug 2023 14:32:58 +0200 Subject: [PATCH 152/172] feat: update issue template (#244) --- .github/ISSUE_TEMPLATE/bug_report.md | 20 ++++++++++---------- .github/ISSUE_TEMPLATE/feature_request.md | 16 ++++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9f172a88..66111abe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,20 +7,20 @@ assignees: '' --- -**Describe the bug** +#### Describe the bug -A clear and concise description of what the bug is. + -**Expected behavior** +#### Expected behavior -A clear and concise description of what you expected to happen. + -**Replication info** +#### Replication info - - Rönd version: - - Running mode: sidecar/standalone - - policy type: +- Rönd version: +- Running mode: sidecar/standalone +- policy type: -**Additional context** +#### Additional context -Add any other context about the problem here. + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 24473dee..010de47e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,14 +7,14 @@ assignees: '' --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +#### Is your feature request related to a problem? Please describe + -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +#### Describe the solution you'd like + -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +#### Describe alternatives you've considered + -**Additional context** -Add any other context or screenshots about the feature request here. \ No newline at end of file +#### Additional context + From deb9595745e3b851858e3a68d7eea2da9e33467e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 14:37:43 +0200 Subject: [PATCH 153/172] chore(deps): bump github.com/mia-platform/go-crud-service-client (#242) Bumps [github.com/mia-platform/go-crud-service-client](https://github.com/mia-platform/go-crud-service-client) from 0.10.0 to 0.11.0. - [Release notes](https://github.com/mia-platform/go-crud-service-client/releases) - [Commits](https://github.com/mia-platform/go-crud-service-client/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: github.com/mia-platform/go-crud-service-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cc24b562..813cd9af 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v4 v4.0.0 - github.com/mia-platform/go-crud-service-client v0.10.0 + github.com/mia-platform/go-crud-service-client v0.11.0 github.com/open-policy-agent/opa v0.55.0 github.com/prometheus/client_golang v1.16.0 github.com/samber/lo v1.38.1 diff --git a/go.sum b/go.sum index 877d98b6..be72a9c0 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,8 @@ github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjK github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= github.com/mia-platform/glogger/v4 v4.0.0 h1:vap3r+0RRHsZkefVqanBz6H381FiD3rtg/j+xuNcRac= github.com/mia-platform/glogger/v4 v4.0.0/go.mod h1:TqVXSfa4wuAaLoe0ye8D6hsPksClgs7p3Y1dC2oRxoQ= -github.com/mia-platform/go-crud-service-client v0.10.0 h1:BUFKQVxfdcnI82cXNIU8Zp2mCI9d7JJS4eUrTUHfxHc= -github.com/mia-platform/go-crud-service-client v0.10.0/go.mod h1:Sj3BEGMu0RTdeye/fPBVwRoojOjKuhWl5j+psY3s40s= +github.com/mia-platform/go-crud-service-client v0.11.0 h1:6/4OFe2N2z2gQuVrAocTrKBPgttb244f74JzZ1ZNz4g= +github.com/mia-platform/go-crud-service-client v0.11.0/go.mod h1:Sj3BEGMu0RTdeye/fPBVwRoojOjKuhWl5j+psY3s40s= github.com/mia-platform/jsonschema v0.1.0 h1:tjQf7TaYROsAqk7SXTL+44TrfKk3bSEvhRGPS51IA5Y= github.com/mia-platform/jsonschema v0.1.0/go.mod h1:r2DJjPA/+6S+WPnXZt1xONMvO2b4hlhfXfUYV0po/Dk= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= From d8c2c8b9544afe16f5722638eb51f098373a5cf1 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:41:41 +0200 Subject: [PATCH 154/172] Improve SDK interface (#243) * feat: separate mongoclient implementation for scopes: custom_builtins and user roles and bindings retrieve * feat: change evaluator interface to take directly rond.Input as params * add sdk integration examples * feat: add inputuser function in sdk * fix: import order * remove unused method * test: add some test * rename input user client mock * test: add tests to getInputUser * add tests and fix log * add copyright * fix: comment * fix: comment * fix: logging * fix: fix mongo user filter * add comment * Update internal/fake/inputuser.go * Update sdk/inputuser/mongo/mongoclient_test.go * rename: GetUserInput to Get --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/input.go | 4 - core/opaevaluator.go | 7 +- core/opaevaluator_test.go | 12 +- core/partialevaluators.go | 7 +- core/partialevaluators_test.go | 2 +- .../mocks/mongoclient.go | 26 +- custom_builtins/mongo.go | 6 +- custom_builtins/mongoclient.go | 149 +++++ custom_builtins/mongoclient_test.go | 212 +++++++ internal/fake/inputuser.go | 46 ++ internal/fake/sdk.go | 5 +- internal/mocks/evaluator.go | 33 -- internal/mongoclient/mongoclient.go | 264 +-------- internal/mongoclient/mongoclient_test.go | 545 +----------------- main.go | 36 +- main_test.go | 16 +- .../example.rego | 2 + sdk/README.md | 3 + sdk/evaluator.go | 68 ++- sdk/evaluator_test.go | 199 ++++--- sdk/inputuser/client.go | 65 +++ sdk/inputuser/client_test.go | 77 +++ sdk/inputuser/mongo/mongoclient.go | 145 +++++ sdk/inputuser/mongo/mongoclient_test.go | 317 ++++++++++ sdk/inputuser/user.go | 67 +++ sdk/inputuser/user_test.go | 180 ++++++ sdk/integration_test.go | 138 +++++ sdk/openapi.go | 10 +- sdk/openapi_test.go | 6 +- sdk/rondinput/http/input.go | 40 +- sdk/rondinput/http/input_test.go | 38 +- sdk/sdk.go | 36 +- sdk/sdk_test.go | 62 +- service/handler.go | 97 +++- service/handler_test.go | 425 +++++++++----- service/opa_transport.go | 17 +- service/opa_transport_test.go | 132 +---- service/opamiddleware.go | 3 +- service/router.go | 8 +- service/router_test.go | 40 +- service/standalone_apis_test.go | 2 + service/statusroutes_test.go | 15 +- types/rbactypes.go | 42 +- 43 files changed, 2139 insertions(+), 1465 deletions(-) rename {internal => custom_builtins}/mocks/mongoclient.go (70%) create mode 100644 custom_builtins/mongoclient.go create mode 100644 custom_builtins/mongoclient_test.go create mode 100644 internal/fake/inputuser.go delete mode 100644 internal/mocks/evaluator.go create mode 100644 sdk/inputuser/client.go create mode 100644 sdk/inputuser/client_test.go create mode 100644 sdk/inputuser/mongo/mongoclient.go create mode 100644 sdk/inputuser/mongo/mongoclient_test.go create mode 100644 sdk/inputuser/user.go create mode 100644 sdk/inputuser/user_test.go create mode 100644 sdk/integration_test.go diff --git a/core/input.go b/core/input.go index d9e840d4..dc1ecce0 100644 --- a/core/input.go +++ b/core/input.go @@ -110,7 +110,3 @@ func CreateRegoQueryInput( Trace("input creation time") return inputBytes, nil } - -type RondInput interface { - Input(user types.User, responseBody any) (Input, error) -} diff --git a/core/opaevaluator.go b/core/opaevaluator.go index e6c2f15b..7de43307 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -23,7 +23,6 @@ import ( "time" "github.com/rond-authz/rond/custom_builtins" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/logging" @@ -72,14 +71,14 @@ type OPAEvaluator struct { PolicyName string context context.Context - mongoClient types.IMongoClient + mongoClient custom_builtins.IMongoClient generateQuery bool logger logging.Logger } type OPAEvaluatorOptions struct { EnablePrintStatements bool - MongoClient types.IMongoClient + MongoClient custom_builtins.IMongoClient Logger logging.Logger } @@ -221,7 +220,7 @@ func (evaluator *OPAEvaluator) getContext() context.Context { ctx = context.Background() } if evaluator.mongoClient != nil { - ctx = mongoclient.WithMongoClient(ctx, evaluator.mongoClient) + ctx = custom_builtins.WithMongoClient(ctx, evaluator.mongoClient) } if evaluator.logger != nil { ctx = logging.WithContext(ctx, evaluator.logger) diff --git a/core/opaevaluator_test.go b/core/opaevaluator_test.go index 39f9c965..0b718b39 100644 --- a/core/opaevaluator_test.go +++ b/core/opaevaluator_test.go @@ -20,8 +20,8 @@ import ( "net/http" "testing" - "github.com/rond-authz/rond/internal/mocks" - "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/custom_builtins" + "github.com/rond-authz/rond/custom_builtins/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/types" @@ -51,7 +51,7 @@ func TestOPAEvaluator(t *testing.T) { ctx := opaEval.getContext() require.NotNil(t, ctx) - client, err := mongoclient.GetMongoClientFromContext(ctx) + client, err := custom_builtins.GetMongoClientFromContext(ctx) require.NoError(t, err) require.Nil(t, client) @@ -61,14 +61,14 @@ func TestOPAEvaluator(t *testing.T) { t.Run("passed context with mongo client", func(t *testing.T) { mongoClient := mocks.MongoClientMock{} - originalContext := mongoclient.WithMongoClient(context.Background(), mongoClient) + originalContext := custom_builtins.WithMongoClient(context.Background(), mongoClient) opaEval := OPAEvaluator{ context: originalContext, } ctx := opaEval.getContext() require.NotNil(t, ctx) - client, err := mongoclient.GetMongoClientFromContext(ctx) + client, err := custom_builtins.GetMongoClientFromContext(ctx) require.NoError(t, err) require.Equal(t, mongoClient, client) }) @@ -82,7 +82,7 @@ func TestOPAEvaluator(t *testing.T) { ctx := opaEval.getContext() require.NotNil(t, ctx) - client, err := mongoclient.GetMongoClientFromContext(ctx) + client, err := custom_builtins.GetMongoClientFromContext(ctx) require.NoError(t, err) require.Equal(t, mongoClient, client) }) diff --git a/core/partialevaluators.go b/core/partialevaluators.go index 6b79ad34..43e8667d 100644 --- a/core/partialevaluators.go +++ b/core/partialevaluators.go @@ -22,7 +22,6 @@ import ( "time" "github.com/rond-authz/rond/custom_builtins" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/logging" "github.com/open-policy-agent/opa/ast" @@ -46,8 +45,8 @@ func createPartialEvaluator(ctx context.Context, logger logging.Logger, policy s logger. WithFields(map[string]any{ - "policyName": policy, - "computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds, + "policyName": policy, + "computationTimeMicroseconds": time.Since(policyEvaluatorTime).Microseconds(), }). Info("precomputation time") @@ -141,7 +140,7 @@ func newPartialResultEvaluator(ctx context.Context, policy string, opaModuleConf custom_builtins.GetHeaderFunction, } if evaluatorOptions.MongoClient != nil { - ctx = mongoclient.WithMongoClient(ctx, evaluatorOptions.MongoClient) + ctx = custom_builtins.WithMongoClient(ctx, evaluatorOptions.MongoClient) options = append(options, custom_builtins.MongoFindOne, custom_builtins.MongoFindMany) } if evaluatorOptions.Logger != nil { diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go index 04711b9f..c03c6631 100644 --- a/core/partialevaluators_test.go +++ b/core/partialevaluators_test.go @@ -20,7 +20,7 @@ import ( "fmt" "testing" - "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/custom_builtins/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/metrics" diff --git a/internal/mocks/mongoclient.go b/custom_builtins/mocks/mongoclient.go similarity index 70% rename from internal/mocks/mongoclient.go rename to custom_builtins/mocks/mongoclient.go index bfb84b55..e2083458 100644 --- a/internal/mocks/mongoclient.go +++ b/custom_builtins/mocks/mongoclient.go @@ -1,4 +1,4 @@ -// Copyright 2021 Mia srl +// Copyright 2023 Mia srl // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package mocks import ( "context" - - "github.com/rond-authz/rond/types" ) type FindOneExpectation struct { @@ -27,14 +25,10 @@ type FindOneExpectation struct { type MongoClientMock struct { FindOneError error - UserBindingsError error - UserRolesError error FindOneResult interface{} FindManyError error FindOneExpectation func(collectionName string, query interface{}) FindManyExpectation func(collectionName string, query interface{}) - UserRoles []types.Role - UserBindings []types.Binding FindManyResult []interface{} } @@ -42,24 +36,6 @@ func (mongoClient MongoClientMock) Disconnect() error { return nil } -func (mongoClient MongoClientMock) RetrieveRoles(ctx context.Context) ([]types.Role, error) { - return nil, nil -} - -func (mongoClient MongoClientMock) RetrieveUserBindings(ctx context.Context, user *types.User) ([]types.Binding, error) { - if mongoClient.UserBindings != nil { - return mongoClient.UserBindings, nil - } - return nil, mongoClient.UserBindingsError -} - -func (mongoClient MongoClientMock) RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]types.Role, error) { - if mongoClient.UserRoles != nil { - return mongoClient.UserRoles, nil - } - return nil, mongoClient.UserRolesError -} - func (mongoClient MongoClientMock) FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) { if mongoClient.FindOneExpectation == nil { panic("FindOneExpectation is required") diff --git a/custom_builtins/mongo.go b/custom_builtins/mongo.go index 30856b61..5f26d190 100644 --- a/custom_builtins/mongo.go +++ b/custom_builtins/mongo.go @@ -17,8 +17,6 @@ package custom_builtins import ( "fmt" - "github.com/rond-authz/rond/internal/mongoclient" - "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/types" @@ -41,7 +39,7 @@ var MongoFindOne = rego.Function2( Decl: MongoFindOneDecl.Decl, }, func(ctx rego.BuiltinContext, collectionNameTerm, queryTerm *ast.Term) (*ast.Term, error) { - mongoClient, err := mongoclient.GetMongoClientFromContext(ctx.Context) + mongoClient, err := GetMongoClientFromContext(ctx.Context) if err != nil { return nil, err } @@ -90,7 +88,7 @@ var MongoFindMany = rego.Function2( Decl: MongoFindManyDecl.Decl, }, func(ctx rego.BuiltinContext, collectionNameTerm, queryTerm *ast.Term) (*ast.Term, error) { - mongoClient, err := mongoclient.GetMongoClientFromContext(ctx.Context) + mongoClient, err := GetMongoClientFromContext(ctx.Context) if err != nil { return nil, err } diff --git a/custom_builtins/mongoclient.go b/custom_builtins/mongoclient.go new file mode 100644 index 00000000..4023c00b --- /dev/null +++ b/custom_builtins/mongoclient.go @@ -0,0 +1,149 @@ +// Copyright 2023 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 custom_builtins + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/logging" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +type IMongoClient interface { + FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) + FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) + Disconnect() error +} + +type mongoClientCustomBuiltinContextKey struct{} + +func WithMongoClient(ctx context.Context, mongoClient IMongoClient) context.Context { + return context.WithValue(ctx, mongoClientCustomBuiltinContextKey{}, mongoClient) +} + +func GetMongoClientFromContext(ctx context.Context) (IMongoClient, error) { + clientInterface := ctx.Value(mongoClientCustomBuiltinContextKey{}) + if clientInterface == nil { + return nil, nil + } + + client, ok := clientInterface.(IMongoClient) + if !ok { + return nil, fmt.Errorf("no MongoDB client found in context") + } + return client, nil +} + +type MongoClient struct { + client *mongoclient.MongoClient +} + +func NewMongoClient(logger logging.Logger, mongodbURL string) (IMongoClient, error) { + mongoClient, err := mongoclient.NewMongoClient(logger, mongodbURL) + if err != nil { + return nil, err + } + if mongoClient == nil { + return nil, nil + } + return &MongoClient{ + client: mongoClient, + }, nil +} + +func (mongoClient *MongoClient) FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) { + collection := mongoClient.client.Collection(collectionName) + log := logging.FromContext(ctx) + log.WithFields(map[string]any{ + "mongoQuery": query, + "collectionName": collectionName, + }).Debug("performing query") + + result := collection.FindOne(ctx, query) + + var bsonDocument bson.D + err := result.Decode(&bsonDocument) + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + log.WithField("error", map[string]any{"message": err.Error()}).Warn("no document found") + return nil, nil + } + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query decode") + return nil, err + } + + temporaryBytes, err := bson.MarshalExtJSON(bsonDocument, true, true) + if err != nil { + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result marshalling") + return nil, err + } + + var res map[string]interface{} + if err := json.Unmarshal(temporaryBytes, &res); err != nil { + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result deserialization") + return nil, err + } + return res, nil +} + +func (mongoClient *MongoClient) FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) { + collection := mongoClient.client.Collection(collectionName) + log := logging.FromContext(ctx) + log.WithFields(map[string]any{ + "mongoQuery": query, + "collectionName": collectionName, + }).Debug("performing query") + + resultCursor, err := collection.Find(ctx, query) + if err != nil { + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query execution") + return nil, err + } + + results := make([]interface{}, 0) + if err := resultCursor.All(ctx, &results); err != nil { + log.WithField("error", map[string]any{"message": err.Error()}).Error("failed complete query result deserialization") + return nil, err + } + + for i := 0; i < len(results); i++ { + temporaryBytes, err := bson.MarshalExtJSON(results[i], true, true) + if err != nil { + log.WithFields(map[string]any{ + "error": map[string]any{"message": err.Error()}, + "resultIndex": i, + }).Error("failed query result marshalling") + return nil, err + } + if err := json.Unmarshal(temporaryBytes, &results[i]); err != nil { + log.WithFields(map[string]any{ + "error": map[string]any{"message": err.Error()}, + "resultIndex": i, + }).Error("failed result document deserialization") + return nil, err + } + } + return results, nil +} + +func (mongoClient *MongoClient) Disconnect() error { + return mongoClient.client.Disconnect() +} diff --git a/custom_builtins/mongoclient_test.go b/custom_builtins/mongoclient_test.go new file mode 100644 index 00000000..2285a8e0 --- /dev/null +++ b/custom_builtins/mongoclient_test.go @@ -0,0 +1,212 @@ +// Copyright 2023 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 custom_builtins + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/rond-authz/rond/internal/testutils" + "github.com/rond-authz/rond/logging" + + "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" +) + +func TestNewMongoClient(t *testing.T) { + t.Run("return nil client if mongodb url not passed", func(t *testing.T) { + client, err := NewMongoClient(logging.NewNoOpLogger(), "") + require.NoError(t, err) + require.Nil(t, client) + }) + + t.Run("fails if mongo url is wrong", func(t *testing.T) { + client, err := NewMongoClient(logging.NewNoOpLogger(), "wrong-url") + require.EqualError(t, err, "failed MongoDB connection string validation: error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"") + require.Nil(t, client) + }) +} + +func TestGetMongoCollectionFromContext(t *testing.T) { + t.Run(`config not found in context`, func(t *testing.T) { + ctx := context.Background() + config, err := GetMongoClientFromContext(ctx) + require.True(t, config == nil) + require.NoError(t, err, "no error expected") + }) + + t.Run(`config found in context`, func(t *testing.T) { + testClient := &MongoClient{} + ctx := WithMongoClient(context.Background(), testClient) + foundConfig, err := GetMongoClientFromContext(ctx) + require.NoError(t, err, "unexpected error") + require.True(t, foundConfig != nil) + }) + + t.Run(`throws if client not correctly in context`, func(t *testing.T) { + ctx := context.WithValue(context.Background(), mongoClientCustomBuiltinContextKey{}, "") + foundConfig, err := GetMongoClientFromContext(ctx) + require.EqualError(t, err, "no MongoDB client found in context") + require.Nil(t, foundConfig) + }) +} + +func TestMongoFindOne(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + dbName := testutils.GetRandomName(10) + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName)) + require.NoError(t, err) + defer mongoClient.Disconnect() + require.True(t, err == nil, "setup mongo returns error") + + collectionName := "my-collection" + populateCollection(t, dbName, collectionName) + + t.Run("finds a document", func(t *testing.T) { + result, err := mongoClient.FindOne(context.Background(), collectionName, map[string]interface{}{ + "id": "my-id-1", + }) + require.NoError(t, err) + require.NotNil(t, result) + resultMap := result.(map[string]interface{}) + require.True(t, resultMap["_id"] != nil) + + delete(resultMap, "_id") + require.Equal(t, map[string]interface{}{ + "some": "field", + "id": "my-id-1", + "nested": map[string]interface{}{ + "some": "think", + }, + "array": []interface{}{"some", "value"}, + }, result) + }) + + t.Run("does not find a document", func(t *testing.T) { + result, err := mongoClient.FindOne(context.Background(), collectionName, map[string]interface{}{ + "key": 42, + }) + require.NoError(t, err) + require.True(t, result == nil) + }) +} + +func TestMongoFindMany(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + dbName := testutils.GetRandomName(10) + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName)) + require.NoError(t, err) + defer mongoClient.Disconnect() + require.True(t, err == nil, "setup mongo returns error") + + collectionName := "my-collection" + populateCollection(t, dbName, collectionName) + + t.Run("finds multiple documents", func(t *testing.T) { + result, err := mongoClient.FindMany(context.Background(), collectionName, map[string]interface{}{ + "some": "field", + }) + require.NoError(t, err) + + require.Len(t, result, 2) + resultMap := result[0].(map[string]interface{}) + require.True(t, resultMap["_id"] != nil) + + delete(resultMap, "_id") + require.Equal(t, map[string]interface{}{ + "some": "field", + "id": "my-id-1", + "nested": map[string]interface{}{ + "some": "think", + }, + "array": []interface{}{"some", "value"}, + }, resultMap) + + result1Map := result[1].(map[string]interface{}) + require.True(t, result1Map["_id"] != nil) + + delete(result1Map, "_id") + require.Equal(t, map[string]interface{}{ + "some": "field", + "id": "my-id-2", + }, result1Map) + }) + + t.Run("does not find any document", func(t *testing.T) { + result, err := mongoClient.FindMany(context.Background(), collectionName, map[string]interface{}{ + "id": "not-exists", + }) + require.NoError(t, err) + require.Len(t, result, 0) + }) + + t.Run("returns error on invalid query", func(t *testing.T) { + result, err := mongoClient.FindMany(context.Background(), collectionName, map[string]interface{}{ + "$UNKWNONW": "invalid", + }) + require.Contains(t, err.Error(), "unknown top level operator") + require.Len(t, result, 0) + }) +} + +func populateCollection(t *testing.T, dbName string, collectionName string) { + t.Helper() + + client := testutils.GetMongoClient(t) + ctx := context.Background() + + db := client.Database(dbName) + collection := db.Collection(collectionName) + _, err := collection.DeleteMany(ctx, bson.D{}) + require.NoError(t, err) + _, err = collection.InsertMany(ctx, []interface{}{ + map[string]any{ + "some": "field", + "id": "my-id-1", + "nested": map[string]string{ + "some": "think", + }, + "array": []string{"some", "value"}, + }, + map[string]any{ + "some": "field", + "id": "my-id-2", + }, + map[string]any{ + "some": "other", + "id": "my-id-3", + }, + }) + require.NoError(t, err) + + t.Cleanup(func() { + collection.Drop(ctx) + db.Drop(ctx) + }) +} diff --git a/internal/fake/inputuser.go b/internal/fake/inputuser.go new file mode 100644 index 00000000..3c3f066b --- /dev/null +++ b/internal/fake/inputuser.go @@ -0,0 +1,46 @@ +// Copyright 2023 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 fake + +import ( + "context" + + "github.com/rond-authz/rond/types" +) + +type InputUserClient struct { + UserBindingsError error + UserRolesError error + UserRoles []types.Role + UserBindings []types.Binding +} + +func (iUser InputUserClient) Disconnect() error { + return nil +} + +func (iUser InputUserClient) RetrieveUserBindings(ctx context.Context, user types.User) ([]types.Binding, error) { + if iUser.UserBindings != nil { + return iUser.UserBindings, nil + } + return nil, iUser.UserBindingsError +} + +func (iUser InputUserClient) RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]types.Role, error) { + if iUser.UserRoles != nil { + return iUser.UserRoles, nil + } + return nil, iUser.UserRolesError +} diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go index a48a09ba..e7e65056 100644 --- a/internal/fake/sdk.go +++ b/internal/fake/sdk.go @@ -19,7 +19,6 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/sdk" - "github.com/rond-authz/rond/types" ) type RequestPolicyEvaluatorResult struct { @@ -46,14 +45,14 @@ func NewSDKEvaluator( } } -func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (sdk.PolicyResult, error) { +func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.Input, options *sdk.EvaluateOptions) (sdk.PolicyResult, error) { if s.requestPolicyEvaluatorResult == nil { return sdk.PolicyResult{}, nil } return sdk.PolicyResult{}, s.requestPolicyEvaluatorResult.Err } -func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) { +func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.Input, options *sdk.EvaluateOptions) ([]byte, error) { return nil, nil } diff --git a/internal/mocks/evaluator.go b/internal/mocks/evaluator.go deleted file mode 100644 index 9df47ccb..00000000 --- a/internal/mocks/evaluator.go +++ /dev/null @@ -1,33 +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 mocks - -import ( - "context" - - "github.com/open-policy-agent/opa/rego" -) - -type MockEvaluator struct { - ResultError error - ResultSet rego.ResultSet -} - -func (m *MockEvaluator) Eval(ctx context.Context, options ...rego.EvalOption) (rego.ResultSet, error) { - if m.ResultError != nil { - return nil, m.ResultError - } - return m.ResultSet, nil -} diff --git a/internal/mongoclient/mongoclient.go b/internal/mongoclient/mongoclient.go index 96c35a83..c97de9f9 100644 --- a/internal/mongoclient/mongoclient.go +++ b/internal/mongoclient/mongoclient.go @@ -16,20 +16,11 @@ package mongoclient import ( "context" - "encoding/json" - "errors" "fmt" - "net/http" - "strings" "time" - "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/logging" - "github.com/rond-authz/rond/types" - "github.com/gorilla/mux" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" @@ -38,74 +29,28 @@ import ( type MongoClient struct { client *mongo.Client - bindings *mongo.Collection - roles *mongo.Collection databaseName string } const STATE string = "__STATE__" const PUBLIC string = "PUBLIC" -// MongoClientInjectorMiddleware will inject into request context the -// mongo collections. -func MongoClientInjectorMiddleware(collections types.IMongoClient) mux.MiddlewareFunc { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := WithMongoClient(r.Context(), collections) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} - -func WithMongoClient(ctx context.Context, mongoClient types.IMongoClient) context.Context { - return context.WithValue(ctx, types.MongoClientContextKey{}, mongoClient) -} - -// GetMongoClientFromContext extracts mongo collections adapter struct from -// provided context. -func GetMongoClientFromContext(ctx context.Context) (types.IMongoClient, error) { - collectionInterface := ctx.Value(types.MongoClientContextKey{}) - if collectionInterface == nil { - return nil, nil - } - - collections, ok := collectionInterface.(types.IMongoClient) - if !ok { - return nil, fmt.Errorf("no MongoDB collection found in context") - } - return collections, nil -} - -func (mongoClient *MongoClient) Disconnect() error { - if mongoClient != nil { - return mongoClient.client.Disconnect(context.Background()) - } - return nil -} - // NewMongoClient tries to setup a new MongoClient instance. // The function returns a `nil` client if the environment variable `MongoDBUrl` is not specified. -func NewMongoClient(env config.EnvironmentVariables, logger logging.Logger) (*MongoClient, error) { - if env.MongoDBUrl == "" { +func NewMongoClient(logger logging.Logger, mongodbURL string) (*MongoClient, error) { + if mongodbURL == "" { logger.Info("No MongoDB configuration provided, skipping setup") return nil, nil } logger.Trace("Start MongoDB client set up") - if env.RolesCollectionName == "" || env.BindingsCollectionName == "" { - return nil, fmt.Errorf( - `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "%s", RolesCollectionName: "%s"`, - env.BindingsCollectionName, - env.RolesCollectionName, - ) - } - parsedConnectionString, err := connstring.ParseAndValidate(env.MongoDBUrl) + parsedConnectionString, err := connstring.ParseAndValidate(mongodbURL) if err != nil { return nil, fmt.Errorf("failed MongoDB connection string validation: %s", err.Error()) } - clientOpts := options.Client().ApplyURI(env.MongoDBUrl) + clientOpts := options.Client().ApplyURI(mongodbURL) client, err := mongo.Connect(context.Background(), clientOpts) if err != nil { return nil, fmt.Errorf("error connecting to MongoDB: %s", err.Error()) @@ -120,207 +65,22 @@ func NewMongoClient(env config.EnvironmentVariables, logger logging.Logger) (*Mo mongoClient := MongoClient{ client: client, databaseName: parsedConnectionString.Database, - roles: client.Database(parsedConnectionString.Database).Collection(env.RolesCollectionName), - bindings: client.Database(parsedConnectionString.Database).Collection(env.BindingsCollectionName), } logger.Info("MongoDB client set up completed") return &mongoClient, nil } -func (mongoClient *MongoClient) RetrieveUserBindings(ctx context.Context, user *types.User) ([]types.Binding, error) { - filter := bson.M{ - "$and": []bson.M{ - { - "$or": []bson.M{ - {"subjects": bson.M{"$elemMatch": bson.M{"$eq": user.UserID}}}, - {"groups": bson.M{"$elemMatch": bson.M{"$in": user.UserGroups}}}, - }, - }, - {STATE: PUBLIC}, - }, - } - cursor, err := mongoClient.bindings.Find( - ctx, - filter, - ) - if err != nil { - return nil, err - } - bindingsResult := make([]types.Binding, 0) - if err = cursor.All(ctx, &bindingsResult); err != nil { - return nil, err - } - return bindingsResult, nil -} - -func (mongoClient *MongoClient) RetrieveRoles(ctx context.Context) ([]types.Role, error) { - filter := bson.M{ - STATE: PUBLIC, - } - cursor, err := mongoClient.roles.Find( - ctx, - filter, - ) - if err != nil { - return nil, err - } - rolesResult := make([]types.Role, 0) - if err = cursor.All(ctx, &rolesResult); err != nil { - return nil, err - } - return rolesResult, nil -} - -func (mongoClient *MongoClient) RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]types.Role, error) { - filter := bson.M{ - "$and": []bson.M{ - { - "roleId": bson.M{"$in": userRolesId}, - }, - {STATE: PUBLIC}, - }, - } - cursor, err := mongoClient.roles.Find( - ctx, - filter, - ) - if err != nil { - return nil, err - } - rolesResult := make([]types.Role, 0) - if err = cursor.All(ctx, &rolesResult); err != nil { - return nil, err - } - return rolesResult, nil -} - -func (mongoClient *MongoClient) FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) { - collection := mongoClient.client.Database(mongoClient.databaseName).Collection(collectionName) - log := logging.FromContext(ctx) - log.WithFields(map[string]any{ - "mongoQuery": query, - "dbName": mongoClient.databaseName, - "collectionName": collectionName, - }).Debug("performing query") - - result := collection.FindOne(ctx, query) - - var bsonDocument bson.D - err := result.Decode(&bsonDocument) - if err != nil { - if errors.Is(err, mongo.ErrNoDocuments) { - log.WithField("error", map[string]any{"message": err.Error()}).Warn("no document found") - return nil, nil - } - log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query decode") - return nil, err - } - - temporaryBytes, err := bson.MarshalExtJSON(bsonDocument, true, true) - if err != nil { - log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result marshalling") - return nil, err - } - - var res map[string]interface{} - if err := json.Unmarshal(temporaryBytes, &res); err != nil { - log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query result deserialization") - return nil, err - } - return res, nil -} - -func (mongoClient *MongoClient) FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) { - collection := mongoClient.client.Database(mongoClient.databaseName).Collection(collectionName) - log := logging.FromContext(ctx) - log.WithFields(map[string]any{ - "mongoQuery": query, - "dbName": mongoClient.databaseName, - "collectionName": collectionName, - }).Debug("performing query") - - resultCursor, err := collection.Find(ctx, query) - if err != nil { - log.WithField("error", map[string]any{"message": err.Error()}).Error("failed query execution") - return nil, err - } - - results := make([]interface{}, 0) - if err := resultCursor.All(ctx, &results); err != nil { - log.WithField("error", map[string]any{"message": err.Error()}).Error("failed complete query result deserialization") - return nil, err - } - - for i := 0; i < len(results); i++ { - temporaryBytes, err := bson.MarshalExtJSON(results[i], true, true) - if err != nil { - log.WithFields(map[string]any{ - "error": map[string]any{"message": err.Error()}, - "resultIndex": i, - }).Error("failed query result marshalling") - return nil, err - } - if err := json.Unmarshal(temporaryBytes, &results[i]); err != nil { - log.WithFields(map[string]any{ - "error": map[string]any{"message": err.Error()}, - "resultIndex": i, - }).Error("failed result document deserialization") - return nil, err - } - } - return results, nil -} - -func RolesIDsFromBindings(bindings []types.Binding) []string { - rolesIds := []string{} - for _, binding := range bindings { - for _, role := range binding.Roles { - if !utils.Contains(rolesIds, role) { - rolesIds = append(rolesIds, role) - } - } +func (m *MongoClient) Collection(collectionName string) *mongo.Collection { + if m != nil { + return m.client.Database(m.databaseName).Collection(collectionName) } - return rolesIds + return nil } -func RetrieveUserBindingsAndRoles(logger logging.Logger, req *http.Request, userHeaders types.UserHeadersKeys) (types.User, error) { - requestContext := req.Context() - mongoClient, err := GetMongoClientFromContext(requestContext) - if err != nil { - return types.User{}, fmt.Errorf("unexpected error retrieving MongoDB Client from request context") - } - - var user types.User - - user.UserGroups = strings.Split(req.Header.Get(userHeaders.GroupsHeaderKey), ",") - user.UserID = req.Header.Get(userHeaders.IDHeaderKey) - - userProperties := make(map[string]interface{}) - _, err = utils.UnmarshalHeader(req.Header, userHeaders.PropertiesHeaderKey, &userProperties) - if err != nil { - return types.User{}, fmt.Errorf("user properties header is not valid: %s", err.Error()) - } - user.Properties = userProperties - - if mongoClient != nil && user.UserID != "" { - user.UserBindings, err = mongoClient.RetrieveUserBindings(requestContext, &user) - if err != nil { - logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user bindings") - return types.User{}, fmt.Errorf("error while retrieving user bindings: %s", err.Error()) - } - - userRolesIds := RolesIDsFromBindings(user.UserBindings) - user.UserRoles, err = mongoClient.RetrieveUserRolesByRolesID(requestContext, userRolesIds) - if err != nil { - logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user roles") - - return types.User{}, fmt.Errorf("error while retrieving user Roles: %s", err.Error()) - } - logger.WithFields(map[string]any{ - "foundBindingsLength": len(user.UserBindings), - "foundRolesLength": len(user.UserRoles), - }).Trace("found bindings and roles") +func (mongoClient *MongoClient) Disconnect() error { + if mongoClient != nil { + return mongoClient.client.Disconnect(context.Background()) } - return user, nil + return nil } diff --git a/internal/mongoclient/mongoclient_test.go b/internal/mongoclient/mongoclient_test.go index c3e364be..ccbc4b55 100644 --- a/internal/mongoclient/mongoclient_test.go +++ b/internal/mongoclient/mongoclient_test.go @@ -15,586 +15,71 @@ package mongoclient import ( - "context" "fmt" - "net/http" - "net/http/httptest" "os" - "reflect" "testing" - "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/logging" - "github.com/rond-authz/rond/types" + "github.com/stretchr/testify/require" ) -func TestMongoCollectionInjectorMiddleware(t *testing.T) { - testCollections := &MongoClient{} - - t.Run(`Context gets updated`, func(t *testing.T) { - invoked := false - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - invoked = true - collection, ok := r.Context().Value(types.MongoClientContextKey{}).(*MongoClient) - require.True(t, ok, "Collection not found") - require.Equal(t, testCollections, collection) - - w.WriteHeader(http.StatusOK) - }) - - middleware := MongoClientInjectorMiddleware(testCollections) - builtMiddleware := middleware(next) - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "/", nil) - - builtMiddleware.ServeHTTP(w, r) - - require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code") - require.True(t, invoked, "Next middleware not invoked") - }) -} - -func TestGetMongoCollectionFromContext(t *testing.T) { - t.Run(`config not found in context`, func(t *testing.T) { - ctx := context.Background() - config, err := GetMongoClientFromContext(ctx) - require.True(t, config == nil) - require.NoError(t, err, "no error expected") - }) - - t.Run(`config found in context`, func(t *testing.T) { - testCollections := &MongoClient{} - ctx := context.WithValue(context.Background(), types.MongoClientContextKey{}, testCollections) - foundConfig, err := GetMongoClientFromContext(ctx) - require.NoError(t, err, "unexpected error") - require.True(t, foundConfig != nil) - }) -} - func TestSetupMongoCollection(t *testing.T) { t.Run("if MongoDBUrl empty, returns nil", func(t *testing.T) { - env := config.EnvironmentVariables{} log := logging.NewNoOpLogger() - adapter, _ := NewMongoClient(env, log) + adapter, _ := NewMongoClient(log, "") require.True(t, adapter == nil, "MongoDBUrl is not nil") }) - t.Run("if RolesCollectionName empty, returns error", func(t *testing.T) { - env := config.EnvironmentVariables{ - MongoDBUrl: "MONGODB_URL", - BindingsCollectionName: "Some different name", - } - log := logging.NewNoOpLogger() - adapter, err := NewMongoClient(env, log) - require.True(t, adapter == nil, "RolesCollectionName collection is not nil") - require.Contains(t, err.Error(), `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "Some different name", RolesCollectionName: ""`) - }) - t.Run("throws if mongo url is without protocol", func(t *testing.T) { mongoHost := "not-valid-mongo-url" - env := config.EnvironmentVariables{ - MongoDBUrl: mongoHost, - RolesCollectionName: "something new", - BindingsCollectionName: "Some different name", - } log := logging.NewNoOpLogger() - adapter, err := NewMongoClient(env, log) + adapter, err := NewMongoClient(log, mongoHost) require.True(t, err != nil, "setup mongo not returns error") require.Contains(t, err.Error(), "failed MongoDB connection string validation:") require.True(t, adapter == nil) }) - t.Run("correctly returns mongodb collection", func(t *testing.T) { + t.Run("correctly returns mongodb client", func(t *testing.T) { mongoHost := os.Getenv("MONGO_HOST_CI") if mongoHost == "" { mongoHost = testutils.LocalhostMongoDB t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") } - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) + mongoClient, err := NewMongoClient(log, fmt.Sprintf("mongodb://%s/%s", mongoHost, testutils.GetRandomName(10))) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") require.True(t, mongoClient != nil) }) -} -func TestMongoCollections(t *testing.T) { - t.Run("testing retrieve user bindings from mongo", func(t *testing.T) { - mongoHost := os.Getenv("MONGO_HOST_CI") - if mongoHost == "" { - mongoHost = testutils.LocalhostMongoDB - t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") - } - - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) - defer mongoClient.Disconnect() - require.True(t, err == nil, "setup mongo returns error") - client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) - mongoClient.client = client - mongoClient.roles = rolesCollection - mongoClient.bindings = bindingsCollection - - ctx := context.Background() - - testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) - - result, _ := mongoClient.RetrieveUserBindings(ctx, &types.User{UserID: "user1", UserGroups: []string{"group1", "group2"}}) - expected := []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"role1", "role2"}, - Groups: []string{"group1"}, - Permissions: []string{"permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding3", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission10", "permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding4", - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission11"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFiltering", - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "bindingForRowFilteringFromSubject", - Subjects: []string{"filter_test"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group1"}, - Permissions: []string{"console.project.view"}, - Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - } - require.True(t, reflect.DeepEqual(result, expected), - "Error while getting permissions") - }) - - t.Run("retrieve all roles from mongo", func(t *testing.T) { + t.Run("correctly returns mongodb collection", func(t *testing.T) { mongoHost := os.Getenv("MONGO_HOST_CI") if mongoHost == "" { mongoHost = testutils.LocalhostMongoDB t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") } - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) - defer mongoClient.Disconnect() - require.True(t, err == nil, "setup mongo returns error") - client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) - mongoClient.client = client - mongoClient.roles = rolesCollection - mongoClient.bindings = bindingsCollection + mongoClient, err := NewMongoClient(log, fmt.Sprintf("mongodb://%s/%s", mongoHost, testutils.GetRandomName(10))) - ctx := context.Background() + collName := "a-collection" + coll := mongoClient.Collection(collName) + require.Equal(t, collName, coll.Name()) - testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) - - result, _ := mongoClient.RetrieveRoles(ctx) - expected := []types.Role{ - { - RoleID: "role1", - RoleName: "Role1", - Permissions: []string{"permission1", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - RoleName: "Role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "notUsedByAnyone", - RoleName: "Not Used By Anyone", - Permissions: []string{"permissionNotUsed1", "permissionNotUsed2"}, - CRUDDocumentState: "PUBLIC", - }, - } - require.True(t, reflect.DeepEqual(result, expected), "Error while getting permissions") - }) - - t.Run("retrieve all roles by id from mongo", func(t *testing.T) { - mongoHost := os.Getenv("MONGO_HOST_CI") - if mongoHost == "" { - mongoHost = testutils.LocalhostMongoDB - t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") - } - - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) defer mongoClient.Disconnect() require.True(t, err == nil, "setup mongo returns error") - client, _, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) - mongoClient.client = client - mongoClient.roles = rolesCollection - mongoClient.bindings = bindingsCollection - - ctx := context.Background() - - testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) - - result, _ := mongoClient.RetrieveUserRolesByRolesID(ctx, []string{"role1", "role3", "notExistingRole"}) - expected := []types.Role{ - { - RoleID: "role1", - RoleName: "Role1", - Permissions: []string{"permission1", "permission2", "foobar"}, - CRUDDocumentState: "PUBLIC", - }, - { - RoleID: "role3", - RoleName: "Role3", - Permissions: []string{"permission3", "permission5", "console.project.view"}, - CRUDDocumentState: "PUBLIC", - }, - } - require.True(t, reflect.DeepEqual(result, expected), - "Error while getting permissions") - }) -} - -func TestMongoFindOne(t *testing.T) { - mongoHost := os.Getenv("MONGO_HOST_CI") - if mongoHost == "" { - mongoHost = testutils.LocalhostMongoDB - t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") - } - - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) - defer mongoClient.Disconnect() - require.True(t, err == nil, "setup mongo returns error") - - client, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) - mongoClient.client = client - mongoClient.databaseName = dbName - mongoClient.roles = rolesCollection - mongoClient.bindings = bindingsCollection - - ctx := context.Background() - - testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) - - t.Run("finds a document", func(t *testing.T) { - result, err := mongoClient.FindOne(context.Background(), "roles", map[string]interface{}{ - "roleId": "role3", - "name": "Role3", - }) - require.NoError(t, err) - resultMap := result.(map[string]interface{}) - require.True(t, resultMap["_id"] != nil) - - delete(resultMap, "_id") - require.Equal(t, map[string]interface{}{ - "roleId": "role3", - "name": "Role3", - "__STATE__": "PUBLIC", - "permissions": []interface{}{ - string("permission3"), - string("permission5"), - string("console.project.view"), - }, - }, result) - }) - - t.Run("does not find a document", func(t *testing.T) { - result, err := mongoClient.FindOne(context.Background(), "roles", map[string]interface{}{ - "key": 42, - }) - require.NoError(t, err) - require.True(t, result == nil) - }) -} - -func TestMongoFindMany(t *testing.T) { - mongoHost := os.Getenv("MONGO_HOST_CI") - if mongoHost == "" { - mongoHost = testutils.LocalhostMongoDB - t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") - } - - env := config.EnvironmentVariables{ - MongoDBUrl: fmt.Sprintf("mongodb://%s/test", mongoHost), - RolesCollectionName: "roles", - BindingsCollectionName: "bindings", - } - log := logging.NewNoOpLogger() - mongoClient, err := NewMongoClient(env, log) - defer mongoClient.Disconnect() - require.True(t, err == nil, "setup mongo returns error") - - client, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) - mongoClient.client = client - mongoClient.databaseName = dbName - mongoClient.roles = rolesCollection - mongoClient.bindings = bindingsCollection - - ctx := context.Background() - - testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) - - t.Run("finds multiple documents", func(t *testing.T) { - result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ - "$or": []map[string]interface{}{ - {"roleId": "role3", "name": "Role3"}, - {"roleId": "role9999"}, - {"roleId": "role6", "name": "Role6"}, - }, - }) - require.NoError(t, err) - - require.Len(t, result, 2) - resultMap := result[0].(map[string]interface{}) - require.True(t, resultMap["_id"] != nil) - - delete(resultMap, "_id") - require.Equal(t, map[string]interface{}{ - "roleId": "role3", - "name": "Role3", - "__STATE__": "PUBLIC", - "permissions": []interface{}{ - string("permission3"), - string("permission5"), - string("console.project.view"), - }, - }, resultMap) - - result1Map := result[1].(map[string]interface{}) - require.True(t, result1Map["_id"] != nil) - - delete(result1Map, "_id") - require.Equal(t, map[string]interface{}{ - "roleId": "role6", - "name": "Role6", - "__STATE__": "PRIVATE", - "permissions": []interface{}{ - string("permission3"), - string("permission5"), - }, - }, result1Map) - }) - - t.Run("does not find any document", func(t *testing.T) { - result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ - "roleId": "role9999", - }) - require.NoError(t, err) - require.Len(t, result, 0) - }) - - t.Run("returns error on invalid query", func(t *testing.T) { - result, err := mongoClient.FindMany(context.Background(), "roles", map[string]interface{}{ - "$UNKWNONW": "role9999", - }) - require.Contains(t, err.Error(), "unknown top level operator") - require.Len(t, result, 0) - }) -} - -func TestRolesIDSFromBindings(t *testing.T) { - result := RolesIDsFromBindings([]types.Binding{ - {Roles: []string{"a", "b"}}, - {Roles: []string{"a", "b"}}, - {Roles: []string{"c", "d"}}, - {Roles: []string{"e"}}, - }) - - require.Equal(t, []string{"a", "b", "c", "d", "e"}, result) -} - -func TestRetrieveUserBindingsAndRoles(t *testing.T) { - log := logging.NewNoOpLogger() - userHeaders := types.UserHeadersKeys{ - GroupsHeaderKey: "thegroupsheader", - IDHeaderKey: "theuserheader", - PropertiesHeaderKey: "userproperties", - } - - t.Run("fails if MongoClient is in context but of the wrong type", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(context.WithValue(req.Context(), types.MongoClientContextKey{}, "test")) - - _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.Error(t, err, "Unexpected error retrieving MongoDB Client from request context") - }) - - t.Run("extract user from request without querying MongoDB", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Set("thegroupsheader", "group1,group2") - req.Header.Set("theuserheader", "userId") - - user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.NoError(t, err) - require.Equal(t, types.User{ - UserID: "userId", - UserGroups: []string{"group1", "group2"}, - Properties: map[string]interface{}{}, - }, user) - }) - - t.Run("extract user with no id in headers does not perform queries", func(t *testing.T) { - mock := mocks.MongoClientMock{ - UserBindingsError: fmt.Errorf("some error"), - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(WithMongoClient(req.Context(), mock)) - - _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.NoError(t, err) - }) - - t.Run("extract user but retrieve bindings fails", func(t *testing.T) { - mock := mocks.MongoClientMock{ - UserBindingsError: fmt.Errorf("some error"), - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(WithMongoClient(req.Context(), mock)) - req.Header.Set("thegroupsheader", "group1,group2") - req.Header.Set("theuserheader", "userId") - - _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.Error(t, err, "Error while retrieving user bindings: some error") - }) - - t.Run("extract user bindings but retrieve roles by role id fails", func(t *testing.T) { - mock := mocks.MongoClientMock{ - UserBindings: []types.Binding{ - {Roles: []string{"r1", "r2"}}, - }, - UserRolesError: fmt.Errorf("some error 2"), - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(WithMongoClient(req.Context(), mock)) - req.Header.Set("thegroupsheader", "group1,group2") - req.Header.Set("theuserheader", "userId") - - _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.Error(t, err, "Error while retrieving user Roles: some error 2") - }) - - t.Run("extract user bindings and roles", func(t *testing.T) { - mock := mocks.MongoClientMock{ - UserBindings: []types.Binding{ - {Roles: []string{"r1", "r2"}}, - {Roles: []string{"r3"}}, - }, - UserRoles: []types.Role{ - {RoleID: "r1", Permissions: []string{"p1", "p2"}}, - {RoleID: "r2", Permissions: []string{"p3", "p4"}}, - {RoleID: "r3", Permissions: []string{"p5"}}, - }, - } - req := httptest.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(WithMongoClient(req.Context(), mock)) - req.Header.Set("thegroupsheader", "group1,group2") - req.Header.Set("theuserheader", "userId") - - user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.NoError(t, err) - require.Equal(t, types.User{ - UserID: "userId", - UserGroups: []string{"group1", "group2"}, - UserBindings: []types.Binding{ - {Roles: []string{"r1", "r2"}}, - {Roles: []string{"r3"}}, - }, - UserRoles: []types.Role{ - {RoleID: "r1", Permissions: []string{"p1", "p2"}}, - {RoleID: "r2", Permissions: []string{"p3", "p4"}}, - {RoleID: "r3", Permissions: []string{"p5"}}, - }, - Properties: map[string]interface{}{}, - }, user) - }) - - t.Run("allow empty userproperties header", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Set("userproperties", "") - req.Header.Set("thegroupsheader", "group1,group2") - req.Header.Set("theuserheader", "userId") - - user, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.NoError(t, err) - require.Equal(t, types.User{ - UserID: "userId", - UserGroups: []string{"group1", "group2"}, - Properties: map[string]interface{}{}, - }, user) + require.True(t, mongoClient != nil) }) - t.Run("fail on invalid userproperties header value", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/", nil) - req.Header.Set("userproperties", "1") + t.Run("if client is nil", func(t *testing.T) { + var mongoClient *MongoClient - _, err := RetrieveUserBindingsAndRoles(log, req, userHeaders) - require.ErrorContains(t, err, "user properties header is not valid:") + require.NoError(t, mongoClient.Disconnect()) + require.Nil(t, mongoClient.Collection("name")) }) } diff --git a/main.go b/main.go index b1af03c6..1e6f65b2 100644 --- a/main.go +++ b/main.go @@ -26,17 +26,17 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/helpers" - "github.com/rond-authz/rond/internal/mongoclient" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/metrics" rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" + mongoclient "github.com/rond-authz/rond/sdk/inputuser/mongo" "github.com/rond-authz/rond/service" - "github.com/mia-platform/glogger/v4" glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/sirupsen/logrus" ) @@ -92,18 +92,31 @@ func entrypoint(shutdown chan os.Signal) { "oasApiPath": env.TargetServiceOASPath, }).Trace("OAS successfully loaded") - mongoClient, err := mongoclient.NewMongoClient(env, rondLogger) + mongoClient, err := mongoclient.NewMongoClient(rondLogger, mongoclient.Config{ + MongoDBURL: env.MongoDBUrl, + RolesCollectionName: env.RolesCollectionName, + BindingsCollectionName: env.BindingsCollectionName, + }) if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, }).Errorf("MongoDB setup failed") return } + if mongoClient != nil { + defer mongoClient.Disconnect() + } - ctx := glogger.WithLogger( - mongoclient.WithMongoClient(context.Background(), mongoClient), - logrus.NewEntry(log), - ) + mongoClientForBuiltin, err := custom_builtins.NewMongoClient(rondLogger, env.MongoDBUrl) + if err != nil { + log.WithFields(logrus.Fields{ + "error": logrus.Fields{"message": err.Error()}, + }).Errorf("MongoDB for builtin setup failed") + return + } + if mongoClientForBuiltin != nil { + defer mongoClientForBuiltin.Disconnect() + } var m *metrics.Metrics var registry *prometheus.Registry @@ -115,11 +128,11 @@ func entrypoint(shutdown chan os.Signal) { ) m = rondprometheus.SetupMetrics(registry) } - sdk, err := sdk.NewFromOAS(ctx, opaModuleConfig, oas, &sdk.Options{ + sdk, err := sdk.NewFromOAS(context.Background(), opaModuleConfig, oas, &sdk.Options{ Metrics: m, - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &sdk.EvaluatorOptions{ EnablePrintStatements: env.IsTraceLogLevel(), - MongoClient: mongoClient, + MongoClient: mongoClientForBuiltin, }, Logger: rondLogger, }) @@ -132,9 +145,6 @@ func entrypoint(shutdown chan os.Signal) { // Routing router, err := service.SetupRouter(log, env, opaModuleConfig, oas, sdk, mongoClient, registry) - if mongoClient != nil { - defer mongoClient.Disconnect() - } if err != nil { log.WithFields(logrus.Fields{ "error": logrus.Fields{"message": err.Error()}, diff --git a/main_test.go b/main_test.go index 94ac63cc..c4a38696 100644 --- a/main_test.go +++ b/main_test.go @@ -31,7 +31,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" @@ -1811,17 +1810,14 @@ filter_policy { }, } - var mongoClient *mongoclient.MongoClient logger, _ := test.NewNullLogger() sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ - MongoClient: mongoClient, - }, - Logger: rondlogrus.NewLogger(logger), + EvaluatorOptions: &sdk.EvaluatorOptions{}, + Logger: rondlogrus.NewLogger(logger), }) require.NoError(t, err, "unexpected error") - router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) + router, err := service.SetupRouter(log, env, opa, oas, sdk, nil, nil) require.NoError(t, err, "unexpected error") t.Run("some eval API", func(t *testing.T) { @@ -1971,14 +1967,10 @@ filter_policy { }, } - var mongoClient *mongoclient.MongoClient registry := prometheus.NewRegistry() logger, _ := test.NewNullLogger() m := rondprometheus.SetupMetrics(registry) sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ - MongoClient: mongoClient, - }, Logger: rondlogrus.NewLogger(logger), Metrics: m, }) @@ -1988,7 +1980,7 @@ filter_policy { "policy_name": "myPolicy", }).Observe(123) - router, err := service.SetupRouter(log, env, opa, oas, sdk, mongoClient, registry) + router, err := service.SetupRouter(log, env, opa, oas, sdk, nil, registry) require.NoError(t, err, "unexpected error") t.Run("metrics API exposed correctly", func(t *testing.T) { diff --git a/mocks/rego-policies-with-mongo-builtins/example.rego b/mocks/rego-policies-with-mongo-builtins/example.rego index 61c0b326..d0d87a5a 100644 --- a/mocks/rego-policies-with-mongo-builtins/example.rego +++ b/mocks/rego-policies-with-mongo-builtins/example.rego @@ -4,6 +4,8 @@ import future.keywords.in foobar = true +todo = true + foo_bar = true allow_commit { diff --git a/sdk/README.md b/sdk/README.md index 6bbac0a2..ec2ad092 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -2,3 +2,6 @@ The Rönd SDK API is experimental and currently under heavy development; for this reason it could undergo breaking changes regardless of the rönd package version. +## Usage + +It is possible to see some example usages of the SDK in [tests](./integration_test.go). diff --git a/sdk/evaluator.go b/sdk/evaluator.go index 2c96cf51..39be1efb 100644 --- a/sdk/evaluator.go +++ b/sdk/evaluator.go @@ -17,11 +17,9 @@ package sdk import ( "context" "encoding/json" - "fmt" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/logging" - "github.com/rond-authz/rond/types" ) type PolicyResult struct { @@ -37,20 +35,18 @@ type Evaluator interface { // EvaluateResponsePolicy evaluate request policy. In the response, it is specified if the // request is allowed and the request query (if filter generation is requested) - EvaluateRequestPolicy(ctx context.Context, input core.RondInput, userInfo types.User) (PolicyResult, error) - // EvaluateResponsePolicy evaluate response policy, take as input the decodedBody body from response - // (unmarshalled) and it is usable as `input.response.body` in the policy. The response is the response + EvaluateRequestPolicy(ctx context.Context, input core.Input, options *EvaluateOptions) (PolicyResult, error) + // EvaluateResponsePolicy evaluate response policy. The response is the response // value returned by the policy. - EvaluateResponsePolicy(ctx context.Context, input core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) + EvaluateResponsePolicy(ctx context.Context, input core.Input, options *EvaluateOptions) ([]byte, error) } type evaluator struct { - logger logging.Logger rondConfig core.RondConfig opaModuleConfig *core.OPAModuleConfig partialResultEvaluators core.PartialResultsEvaluators - opaEvaluatorOptions *core.OPAEvaluatorOptions + evaluatorOptions *EvaluatorOptions policyEvaluationOptions *core.PolicyEvaluationOptions } @@ -58,42 +54,53 @@ func (e evaluator) Config() core.RondConfig { return e.rondConfig } -func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req core.RondInput, userInfo types.User) (PolicyResult, error) { - if req == nil { - return PolicyResult{}, fmt.Errorf("RondInput cannot be empty") +type EvaluateOptions struct { + Logger logging.Logger +} + +func (e EvaluateOptions) GetLogger() logging.Logger { + if e.Logger == nil { + return logging.NewNoOpLogger() } + return e.Logger +} +func (e evaluator) EvaluateRequestPolicy(ctx context.Context, rondInput core.Input, options *EvaluateOptions) (PolicyResult, error) { rondConfig := e.Config() - - input, err := req.Input(userInfo, nil) - if err != nil { - return PolicyResult{}, err + if options == nil { + options = &EvaluateOptions{} } + logger := options.GetLogger() - regoInput, err := core.CreateRegoQueryInput(e.logger, input, core.RegoInputOptions{ + regoInput, err := core.CreateRegoQueryInput(logger, rondInput, core.RegoInputOptions{ EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, }) if err != nil { return PolicyResult{}, nil } + opaEvaluatorOptions := e.evaluatorOptions.opaEvaluatorOptions(logger) + var evaluatorAllowPolicy *core.OPAEvaluator if !rondConfig.RequestFlow.GenerateQuery { - evaluatorAllowPolicy, err = e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, rondConfig.RequestFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + evaluatorAllowPolicy, err = e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, rondConfig.RequestFlow.PolicyName, regoInput, opaEvaluatorOptions) if err != nil { return PolicyResult{}, err } } else { - evaluatorAllowPolicy, err = e.opaModuleConfig.CreateQueryEvaluator(ctx, e.logger, rondConfig.RequestFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + evaluatorAllowPolicy, err = e.opaModuleConfig.CreateQueryEvaluator(ctx, logger, rondConfig.RequestFlow.PolicyName, regoInput, opaEvaluatorOptions) if err != nil { return PolicyResult{}, err } } - _, query, err := evaluatorAllowPolicy.PolicyEvaluation(e.logger, e.policyEvaluationOptions) + // TODO: here if the evaluation result false, it is returned an error. This interface + // for the sdk should be improved, since it should use the PolicyResult and return error + // only if there is some error in policy evaluation. + _, query, err := evaluatorAllowPolicy.PolicyEvaluation(logger, e.policyEvaluationOptions) if err != nil { - e.logger.WithField("error", map[string]any{ + logger.WithField("error", map[string]any{ "policyName": rondConfig.RequestFlow.PolicyName, "message": err.Error(), }).Error("RBAC policy evaluation failed") @@ -114,31 +121,28 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, req core.RondInput }, nil } -func (e evaluator) EvaluateResponsePolicy(ctx context.Context, rondInput core.RondInput, userInfo types.User, decodedBody any) ([]byte, error) { - if rondInput == nil { - return nil, fmt.Errorf("RondInput cannot be empty") - } - +func (e evaluator) EvaluateResponsePolicy(ctx context.Context, rondInput core.Input, options *EvaluateOptions) ([]byte, error) { rondConfig := e.Config() - - input, err := rondInput.Input(userInfo, decodedBody) - if err != nil { - return nil, err + if options == nil { + options = &EvaluateOptions{} } + logger := options.GetLogger() - regoInput, err := core.CreateRegoQueryInput(e.logger, input, core.RegoInputOptions{ + regoInput, err := core.CreateRegoQueryInput(logger, rondInput, core.RegoInputOptions{ EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, }) if err != nil { return nil, err } - evaluator, err := e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, e.rondConfig.ResponseFlow.PolicyName, regoInput, e.opaEvaluatorOptions) + opaEvaluatorOptions := e.evaluatorOptions.opaEvaluatorOptions(logger) + + evaluator, err := e.partialResultEvaluators.GetEvaluatorFromPolicy(ctx, e.rondConfig.ResponseFlow.PolicyName, regoInput, opaEvaluatorOptions) if err != nil { return nil, err } - bodyToProxy, err := evaluator.Evaluate(e.logger, e.policyEvaluationOptions) + bodyToProxy, err := evaluator.Evaluate(logger, e.policyEvaluationOptions) if err != nil { return nil, err } diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index 413fdfb5..ec9fc71a 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -22,7 +22,8 @@ import ( "testing" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/custom_builtins" + "github.com/rond-authz/rond/custom_builtins/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/logging/test" "github.com/rond-authz/rond/metrics" @@ -34,25 +35,14 @@ import ( ) func TestEvaluateRequestPolicy(t *testing.T) { - logger := logging.NewNoOpLogger() - - t.Run("throws without RondInput", func(t *testing.T) { - sdk := getOASSdk(t, nil) - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") - require.NoError(t, err) - - _, err = evaluator.EvaluateRequestPolicy(context.Background(), nil, types.User{}) - require.EqualError(t, err, "RondInput cannot be empty") - }) - type testCase struct { method string path string opaModuleContent string oasFilePath string - user types.User + user core.InputUser reqHeaders map[string]string - mongoClient types.IMongoClient + mongoClient custom_builtins.IMongoClient expectedPolicy PolicyResult expectedErr error @@ -72,8 +62,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "with user with policy true": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", }, expectedPolicy: PolicyResult{ @@ -84,8 +74,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "not allow if not existing policy": { method: http.MethodPost, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", }, expectedPolicy: PolicyResult{}, @@ -94,8 +84,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "not allowed policy result": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", }, opaModuleContent: `package policies todo { false }`, @@ -106,8 +96,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { method: http.MethodGet, path: "/users/", oasFilePath: "../mocks/rondOasConfig.json", - user: types.User{ - UserGroups: []string{"my-group"}, + user: core.InputUser{ + Groups: []string{"my-group"}, }, reqHeaders: map[string]string{ "my-header-key": "ok", @@ -129,8 +119,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { method: http.MethodGet, path: "/users/", oasFilePath: "../mocks/rondOasConfig.json", - user: types.User{ - UserGroups: []string{"my-group"}, + user: core.InputUser{ + Groups: []string{"my-group"}, }, reqHeaders: map[string]string{ "my-header-key": "ok", @@ -149,15 +139,15 @@ func TestEvaluateRequestPolicy(t *testing.T) { "check user": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "the-user-id", - UserGroups: []string{"my-group"}, - UserRoles: []types.Role{ + user: core.InputUser{ + ID: "the-user-id", + Groups: []string{"my-group"}, + Roles: []types.Role{ { RoleID: "rid", }, }, - UserBindings: []types.Binding{ + Bindings: []types.Binding{ { Resource: &types.Resource{ ResourceType: "my-resource", @@ -184,8 +174,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "with mongo client and find_one": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", }, opaModuleContent: `package policies todo { @@ -208,8 +198,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "with mongo client and find_one with dynamic find_one query": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", Properties: map[string]any{ "field": "1234", }, @@ -237,8 +227,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { "with mongo client and find_many": { method: http.MethodGet, path: "/users/", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", }, mongoClient: &mocks.MongoClientMock{ FindManyResult: []interface{}{ @@ -264,8 +254,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { method: http.MethodGet, path: "/users/", oasFilePath: "../mocks/rondOasConfig.json", - user: types.User{ - UserGroups: []string{"my-group"}, + user: core.InputUser{ + Groups: []string{"my-group"}, }, mongoClient: &mocks.MongoClientMock{ FindOneResult: map[string]string{"myField": "1234"}, @@ -291,8 +281,8 @@ func TestEvaluateRequestPolicy(t *testing.T) { method: http.MethodGet, path: "/users/", oasFilePath: "../mocks/rondOasConfig.json", - user: types.User{ - UserID: "my-user", + user: core.InputUser{ + ID: "my-user", Properties: map[string]any{ "field": "1234", }, @@ -331,8 +321,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { metrics: testMetrics, }) - logger := test.GetLogger() - evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) + evaluate, err := sdk.FindEvaluator(testCase.method, testCase.path) require.NoError(t, err) headers := http.Header{} @@ -345,9 +334,12 @@ func TestEvaluateRequestPolicy(t *testing.T) { Headers: headers, Path: testCase.path, Method: testCase.method, - }, "") + }, "", testCase.user, nil) - actual, err := evaluate.EvaluateRequestPolicy(context.Background(), rondInput, testCase.user) + logger := test.GetLogger() + actual, err := evaluate.EvaluateRequestPolicy(context.Background(), rondInput, &EvaluateOptions{ + Logger: logger, + }) if testCase.expectedErr != nil { require.EqualError(t, err, testCase.expectedErr.Error()) } else { @@ -403,27 +395,35 @@ func TestEvaluateRequestPolicy(t *testing.T) { }) } }) -} - -func TestEvaluateResponsePolicy(t *testing.T) { - logger := logging.NewNoOpLogger() - t.Run("throws without RondInput", func(t *testing.T) { - sdk := getOASSdk(t, nil) - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + t.Run("with nil options", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + todo { true }`, + } + sdk, err := NewWithConfig(context.Background(), opaModule, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, + }, nil) require.NoError(t, err) - _, err = evaluator.EvaluateResponsePolicy(context.Background(), nil, types.User{}, nil) - require.EqualError(t, err, "RondInput cannot be empty") + result, err := sdk.EvaluateRequestPolicy(context.Background(), core.Input{}, nil) + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }, result) }) +} +func TestEvaluateResponsePolicy(t *testing.T) { type testCase struct { method string path string opaModuleContent string - user types.User + user core.InputUser reqHeaders map[string]string - mongoClient types.IMongoClient + mongoClient custom_builtins.IMongoClient decodedBody any @@ -526,7 +526,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { metrics: testMetrics, }) - evaluate, err := sdk.FindEvaluator(logger, testCase.method, testCase.path) + evaluate, err := sdk.FindEvaluator(testCase.method, testCase.path) require.NoError(t, err) req := httptest.NewRequest(testCase.method, testCase.path, nil) @@ -545,9 +545,11 @@ func TestEvaluateResponsePolicy(t *testing.T) { Headers: headers, Path: testCase.path, Method: testCase.method, - }, "") + }, "", testCase.user, testCase.decodedBody) - actual, err := evaluate.EvaluateResponsePolicy(context.Background(), rondInput, testCase.user, testCase.decodedBody) + actual, err := evaluate.EvaluateResponsePolicy(context.Background(), rondInput, &EvaluateOptions{ + Logger: logger, + }) if testCase.expectedErr != nil { require.EqualError(t, err, testCase.expectedErr.Error()) } else { @@ -597,6 +599,29 @@ func TestEvaluateResponsePolicy(t *testing.T) { }) } }) + + t.Run("with nil options", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + responsepolicy [body] { + body := input.response.body + }`, + } + sdk, err := NewWithConfig(context.Background(), opaModule, core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, + ResponseFlow: core.ResponseFlow{PolicyName: "responsepolicy"}, + }, nil) + require.NoError(t, err) + + result, err := sdk.EvaluateResponsePolicy(context.Background(), core.Input{ + Response: core.InputResponse{ + Body: map[string]string{"foo": "bar"}, + }, + }, nil) + require.NoError(t, err) + require.Equal(t, `{"foo":"bar"}`, string(result)) + }) } func BenchmarkEvaluateRequest(b *testing.B) { @@ -626,9 +651,9 @@ func BenchmarkEvaluateRequest(b *testing.B) { }, } - user := types.User{ - UserID: "user1", - UserBindings: []types.Binding{ + user := core.InputUser{ + ID: "user1", + Bindings: []types.Binding{ { BindingID: "binding1", Subjects: []string{"user1"}, @@ -660,7 +685,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { CRUDDocumentState: "PUBLIC", }, }, - UserRoles: []types.Role{ + Roles: []types.Role{ { RoleID: "company_owner", Permissions: []string{"console.company.project.view", "console.company.project.environment.view"}, @@ -696,7 +721,7 @@ func BenchmarkEvaluateRequest(b *testing.B) { } evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &EvaluatorOptions{ MongoClient: mongoClient, }, }) @@ -716,10 +741,10 @@ func BenchmarkEvaluateRequest(b *testing.B) { PathParams: map[string]string{ "projectId": "project123", }, - }, "") + }, "", user, nil) b.StartTimer() - policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, user) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, nil) b.StopTimer() require.NoError(b, err) @@ -741,9 +766,9 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { }, } - user := types.User{ - UserID: "user1", - UserBindings: []types.Binding{ + user := core.InputUser{ + ID: "user1", + Bindings: []types.Binding{ { BindingID: "binding1", Subjects: []string{"user1"}, @@ -775,7 +800,7 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { CRUDDocumentState: "PUBLIC", }, }, - UserRoles: []types.Role{ + Roles: []types.Role{ { RoleID: "company_owner", Permissions: []string{"console.company.project.view", "console.company.project.environment.view"}, @@ -805,7 +830,7 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { } evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &EvaluatorOptions{ MongoClient: mocks.MongoClientMock{}, }, }) @@ -823,10 +848,10 @@ func BenchmarkEvaluateRequestWithQueryGeneration(b *testing.B) { Headers: headers, Method: http.MethodGet, PathParams: map[string]string{}, - }, "") + }, "", user, nil) b.StartTimer() - policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, user) + policyResult, err := evaluator.EvaluateRequestPolicy(context.Background(), rondInput, nil) b.StopTimer() require.NoError(b, err) @@ -850,9 +875,9 @@ func BenchmarkEvaluateResponse(b *testing.B) { }, } - user := types.User{ - UserID: "user1", - UserBindings: []types.Binding{{ + user := core.InputUser{ + ID: "user1", + Bindings: []types.Binding{{ BindingID: "binding-env", Subjects: []string{"user1"}, Roles: []string{"env-reader"}, @@ -873,7 +898,7 @@ func BenchmarkEvaluateResponse(b *testing.B) { CRUDDocumentState: "PUBLIC", }, }, - UserRoles: []types.Role{ + Roles: []types.Role{ { RoleID: "env-reader", Permissions: []string{"console.environment.view"}, @@ -888,7 +913,7 @@ func BenchmarkEvaluateResponse(b *testing.B) { } evaluator, err := NewWithConfig(context.Background(), moduleConfig, config, &Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &EvaluatorOptions{ MongoClient: &mocks.MongoClientMock{}, }, }) @@ -901,15 +926,6 @@ func BenchmarkEvaluateResponse(b *testing.B) { headers := http.Header{} headers.Set("my-header", "value") - rondInput := getFakeInput(b, core.InputRequest{ - Path: "/projects/projectWithEnv", - Headers: headers, - Method: http.MethodGet, - PathParams: map[string]string{ - "projectId": "projectWithEnv", - }, - }, "") - decodedBody := map[string]any{ "_id": "projectWithEnv", "projectId": "my-project", @@ -927,8 +943,17 @@ func BenchmarkEvaluateResponse(b *testing.B) { }, } + rondInput := getFakeInput(b, core.InputRequest{ + Path: "/projects/projectWithEnv", + Headers: headers, + Method: http.MethodGet, + PathParams: map[string]string{ + "projectId": "projectWithEnv", + }, + }, "", user, decodedBody) + b.StartTimer() - policyResult, err := evaluator.EvaluateResponsePolicy(context.Background(), rondInput, user, decodedBody) + policyResult, err := evaluator.EvaluateResponsePolicy(context.Background(), rondInput, nil) b.StopTimer() require.NoError(b, err) @@ -980,7 +1005,7 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder { sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{ Metrics: options.metrics, - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &EvaluatorOptions{ EnablePrintStatements: true, MongoClient: options.mongoClient, }, diff --git a/sdk/inputuser/client.go b/sdk/inputuser/client.go new file mode 100644 index 00000000..e9ab8c1c --- /dev/null +++ b/sdk/inputuser/client.go @@ -0,0 +1,65 @@ +// Copyright 2023 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 inputuser + +import ( + "context" + "fmt" + "net/http" + + "github.com/rond-authz/rond/types" + + "github.com/gorilla/mux" +) + +// Client allows to retrieve information to evaluate policy, as bindings and roles +type Client interface { + Disconnect() error + + RetrieveUserBindings(ctx context.Context, user types.User) ([]types.Binding, error) + RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]types.Role, error) +} + +type clientContextKey struct{} + +// ClientInjectorMiddleware will inject into request context the +// mongo collections. +func ClientInjectorMiddleware(client Client) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := AddClientInContext(r.Context(), client) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + +func AddClientInContext(ctx context.Context, mongoClient Client) context.Context { + return context.WithValue(ctx, clientContextKey{}, mongoClient) +} + +// GetClientFromContext extracts mongo collections adapter struct from +// provided context. +func GetClientFromContext(ctx context.Context) (Client, error) { + clientInterface := ctx.Value(clientContextKey{}) + if clientInterface == nil { + return nil, nil + } + + client, ok := clientInterface.(Client) + if !ok { + return nil, fmt.Errorf("no client found in context") + } + return client, nil +} diff --git a/sdk/inputuser/client_test.go b/sdk/inputuser/client_test.go new file mode 100644 index 00000000..fe718392 --- /dev/null +++ b/sdk/inputuser/client_test.go @@ -0,0 +1,77 @@ +// Copyright 2023 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 inputuser + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/rond-authz/rond/internal/fake" + + "github.com/stretchr/testify/require" +) + +func TestClientInjectorMiddleware(t *testing.T) { + inputUserClient := &fake.InputUserClient{} + + t.Run(`context gets updated`, func(t *testing.T) { + invoked := false + next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + collection, err := GetClientFromContext(r.Context()) + require.NoError(t, err, "client not found") + require.Equal(t, inputUserClient, collection) + + w.WriteHeader(http.StatusOK) + }) + + middleware := ClientInjectorMiddleware(inputUserClient) + builtMiddleware := middleware(next) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", nil) + + builtMiddleware.ServeHTTP(w, r) + + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code") + require.True(t, invoked, "Next middleware not invoked") + }) +} + +func TestGetClientFromContext(t *testing.T) { + t.Run(`config not found in context`, func(t *testing.T) { + ctx := context.Background() + config, err := GetClientFromContext(ctx) + require.True(t, config == nil) + require.NoError(t, err, "no error expected") + }) + + t.Run(`config found in context`, func(t *testing.T) { + testClient := &fake.InputUserClient{} + ctx := AddClientInContext(context.Background(), testClient) + foundConfig, err := GetClientFromContext(ctx) + require.NoError(t, err, "unexpected error") + require.True(t, foundConfig != nil) + }) + + t.Run(`client not found in context`, func(t *testing.T) { + ctx := context.WithValue(context.Background(), clientContextKey{}, "") + foundConfig, err := GetClientFromContext(ctx) + require.EqualError(t, err, "no client found in context") + require.Nil(t, foundConfig) + }) +} diff --git a/sdk/inputuser/mongo/mongoclient.go b/sdk/inputuser/mongo/mongoclient.go new file mode 100644 index 00000000..8f73532a --- /dev/null +++ b/sdk/inputuser/mongo/mongoclient.go @@ -0,0 +1,145 @@ +// 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 mongoclient + +import ( + "context" + "fmt" + + "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/sdk/inputuser" + "github.com/rond-authz/rond/types" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +type MongoClient struct { + *mongoclient.MongoClient + bindings *mongo.Collection + roles *mongo.Collection +} + +const STATE string = "__STATE__" +const PUBLIC string = "PUBLIC" + +func (mongoClient *MongoClient) Disconnect() error { + if mongoClient != nil { + return mongoClient.MongoClient.Disconnect() + } + return nil +} + +type Config struct { + MongoDBURL string + + RolesCollectionName string + BindingsCollectionName string +} + +// NewMongoClient tries to setup a new MongoClient instance. +// The function returns a `nil` client if the environment variable `MongoDBUrl` is not specified. +func NewMongoClient(logger logging.Logger, config Config) (inputuser.Client, error) { + client, err := mongoclient.NewMongoClient(logger, config.MongoDBURL) + if err != nil { + return nil, err + } + if client == nil { + return nil, nil + } + + if config.RolesCollectionName == "" || config.BindingsCollectionName == "" { + return nil, fmt.Errorf( + `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "%s", RolesCollectionName: "%s"`, + config.BindingsCollectionName, + config.RolesCollectionName, + ) + } + + return &MongoClient{ + MongoClient: client, + roles: client.Collection(config.RolesCollectionName), + bindings: client.Collection(config.BindingsCollectionName), + }, nil +} + +func (mongoClient *MongoClient) RetrieveUserBindings(ctx context.Context, user types.User) ([]types.Binding, error) { + if mongoClient == nil { + return nil, fmt.Errorf("mongoClient is not defined") + } + if user.ID == "" { + return nil, fmt.Errorf("user id is required to fetch bindings") + } + + userBindingsOrFilter := []bson.M{ + {"subjects": bson.M{"$elemMatch": bson.M{"$eq": user.ID}}}, + } + + if user.Groups != nil { + userBindingsOrFilter = append(userBindingsOrFilter, bson.M{ + "groups": bson.M{"$elemMatch": bson.M{"$in": user.Groups}}, + }) + } + + filter := bson.M{ + "$and": []bson.M{ + { + "$or": userBindingsOrFilter, + }, + {STATE: PUBLIC}, + }, + } + + cursor, err := mongoClient.bindings.Find( + ctx, + filter, + ) + if err != nil { + return nil, err + } + bindingsResult := make([]types.Binding, 0) + if err = cursor.All(ctx, &bindingsResult); err != nil { + return nil, err + } + return bindingsResult, nil +} + +func (mongoClient *MongoClient) RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]types.Role, error) { + if mongoClient == nil { + return nil, fmt.Errorf("mongoClient is not defined") + } + + filter := bson.M{ + "$and": []bson.M{ + { + "roleId": bson.M{"$in": userRolesId}, + }, + {STATE: PUBLIC}, + }, + } + cursor, err := mongoClient.roles.Find( + ctx, + filter, + ) + if err != nil { + return nil, err + } + rolesResult := make([]types.Role, 0) + if err = cursor.All(ctx, &rolesResult); err != nil { + return nil, err + } + return rolesResult, nil +} diff --git a/sdk/inputuser/mongo/mongoclient_test.go b/sdk/inputuser/mongo/mongoclient_test.go new file mode 100644 index 00000000..654ba6d8 --- /dev/null +++ b/sdk/inputuser/mongo/mongoclient_test.go @@ -0,0 +1,317 @@ +// 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 mongoclient + +import ( + "context" + "fmt" + "os" + "reflect" + "testing" + + "github.com/rond-authz/rond/internal/testutils" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/types" + + "github.com/stretchr/testify/require" +) + +func TestSetupMongoCollection(t *testing.T) { + t.Run("if MongoDBUrl empty, returns nil", func(t *testing.T) { + log := logging.NewNoOpLogger() + adapter, _ := NewMongoClient(log, Config{}) + require.Nil(t, adapter, "MongoDBUrl is not nil") + }) + + t.Run("if RolesCollectionName empty, returns error", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/test", mongoHost), + BindingsCollectionName: "Some different name", + } + log := logging.NewNoOpLogger() + adapter, err := NewMongoClient(log, config) + require.Nil(t, adapter, "RolesCollectionName collection is not nil") + require.EqualError(t, err, `MongoDB url is not empty, required variables might be missing: BindingsCollectionName: "Some different name", RolesCollectionName: ""`) + }) + + t.Run("throws if mongo url is without protocol", func(t *testing.T) { + mongoHost := "not-valid-mongo-url" + + config := Config{ + MongoDBURL: mongoHost, + RolesCollectionName: "something new", + BindingsCollectionName: "Some different name", + } + log := logging.NewNoOpLogger() + adapter, err := NewMongoClient(log, config) + require.True(t, err != nil, "setup mongo not returns error") + require.Contains(t, err.Error(), "failed MongoDB connection string validation:") + require.True(t, adapter == nil) + }) + + t.Run("correctly returns mongodb client", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/test", mongoHost), + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + } + + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, config) + + defer mongoClient.Disconnect() + require.True(t, err == nil, "setup mongo returns error") + require.True(t, mongoClient != nil) + }) +} + +func TestMongoCollections(t *testing.T) { + t.Run("testing retrieve user bindings from mongo", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + _, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName), + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + } + + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, config) + require.NoError(t, err) + defer mongoClient.Disconnect() + + ctx := context.Background() + + testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) + + result, _ := mongoClient.RetrieveUserBindings(ctx, types.User{ID: "user1", Groups: []string{"group1", "group2"}}) + expected := []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"role1", "role2"}, + Groups: []string{"group1"}, + Permissions: []string{"permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding3", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission10", "permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding4", + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission11"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFiltering", + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "9876"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "bindingForRowFilteringFromSubject", + Subjects: []string{"filter_test"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group1"}, + Permissions: []string{"console.project.view"}, + Resource: &types.Resource{ResourceType: "custom", ResourceID: "12345"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + } + require.True(t, reflect.DeepEqual(result, expected), + "Error while getting permissions") + }) + + t.Run("testing retrieve user bindings from mongo - user without groups", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + _, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName), + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + } + + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, config) + require.NoError(t, err) + defer mongoClient.Disconnect() + + ctx := context.Background() + + testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) + + result, err := mongoClient.RetrieveUserBindings(ctx, types.User{ID: "user1"}) + expected := []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"role1", "role2"}, + Groups: []string{"group1"}, + Permissions: []string{"permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + } + require.NoError(t, err) + require.Equal(t, result, expected, "Error while getting permissions") + }) + + t.Run("testing retrieve user bindings from mongo - no userId passed", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + _, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName), + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + } + + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, config) + require.NoError(t, err) + defer mongoClient.Disconnect() + + ctx := context.Background() + + testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) + + _, err = mongoClient.RetrieveUserBindings(ctx, types.User{}) + require.EqualError(t, err, "user id is required to fetch bindings") + }) + + t.Run("retrieve all roles by id from mongo", func(t *testing.T) { + mongoHost := os.Getenv("MONGO_HOST_CI") + if mongoHost == "" { + mongoHost = testutils.LocalhostMongoDB + t.Logf("Connection to localhost MongoDB, on CI env this is a problem!") + } + + _, dbName, rolesCollection, bindingsCollection := testutils.GetAndDisposeTestClientsAndCollections(t) + + config := Config{ + MongoDBURL: fmt.Sprintf("mongodb://%s/%s", mongoHost, dbName), + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + } + log := logging.NewNoOpLogger() + mongoClient, err := NewMongoClient(log, config) + defer mongoClient.Disconnect() + require.NoError(t, err, "setup mongo returns error") + + ctx := context.Background() + + testutils.PopulateDBForTesting(t, ctx, rolesCollection, bindingsCollection) + + result, _ := mongoClient.RetrieveUserRolesByRolesID(ctx, []string{"role1", "role3", "notExistingRole"}) + expected := []types.Role{ + { + RoleID: "role1", + RoleName: "Role1", + Permissions: []string{"permission1", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role3", + RoleName: "Role3", + Permissions: []string{"permission3", "permission5", "console.project.view"}, + CRUDDocumentState: "PUBLIC", + }, + } + require.True(t, reflect.DeepEqual(result, expected), + "Error while getting permissions") + }) +} + +func TestMongoClientNil(t *testing.T) { + var mongoClient *MongoClient + + t.Run("disconnect", func(t *testing.T) { + require.Nil(t, mongoClient.Disconnect()) + }) + + t.Run("retrieve user bindings", func(t *testing.T) { + _, err := mongoClient.RetrieveUserBindings(context.Background(), types.User{}) + require.EqualError(t, err, "mongoClient is not defined") + }) + + t.Run("retrieve roles by roleIds", func(t *testing.T) { + _, err := mongoClient.RetrieveUserRolesByRolesID(context.Background(), []string{"id"}) + require.EqualError(t, err, "mongoClient is not defined") + }) +} diff --git a/sdk/inputuser/user.go b/sdk/inputuser/user.go new file mode 100644 index 00000000..861ee37f --- /dev/null +++ b/sdk/inputuser/user.go @@ -0,0 +1,67 @@ +// Copyright 2023 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 inputuser + +import ( + "context" + "fmt" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/utils" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/types" +) + +func rolesIDsFromBindings(bindings []types.Binding) []string { + rolesIds := []string{} + for _, binding := range bindings { + for _, role := range binding.Roles { + if !utils.Contains(rolesIds, role) { + rolesIds = append(rolesIds, role) + } + } + } + return rolesIds +} + +func Get(ctx context.Context, logger logging.Logger, client Client, user types.User) (core.InputUser, error) { + inputUser := core.InputUser{ + Groups: user.Groups, + ID: user.ID, + Properties: user.Properties, + } + + if client != nil && user.ID != "" { + var err error + inputUser.Bindings, err = client.RetrieveUserBindings(ctx, user) + if err != nil { + logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user bindings") + return core.InputUser{}, fmt.Errorf("error while retrieving user bindings: %s", err.Error()) + } + + userRolesIds := rolesIDsFromBindings(inputUser.Bindings) + inputUser.Roles, err = client.RetrieveUserRolesByRolesID(ctx, userRolesIds) + if err != nil { + logger.WithField("error", map[string]any{"message": err.Error()}).Error("something went wrong while retrieving user roles") + + return core.InputUser{}, fmt.Errorf("error while retrieving user Roles: %s", err.Error()) + } + logger.WithFields(map[string]any{ + "foundBindingsLength": len(inputUser.Bindings), + "foundRolesLength": len(inputUser.Roles), + }).Trace("found bindings and roles") + } + return inputUser, nil +} diff --git a/sdk/inputuser/user_test.go b/sdk/inputuser/user_test.go new file mode 100644 index 00000000..be87e0e8 --- /dev/null +++ b/sdk/inputuser/user_test.go @@ -0,0 +1,180 @@ +// Copyright 2023 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 inputuser + +import ( + "context" + "fmt" + "reflect" + "testing" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/internal/fake" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/types" + + "github.com/stretchr/testify/require" +) + +func TestRolesIDSFromBindings(t *testing.T) { + t.Run("retrieve roles ids #1", func(t *testing.T) { + result := rolesIDsFromBindings([]types.Binding{ + {Roles: []string{"a", "b"}}, + {Roles: []string{"a", "b"}}, + {Roles: []string{"c", "d"}}, + {Roles: []string{"e"}}, + }) + + require.Equal(t, []string{"a", "b", "c", "d", "e"}, result) + }) + + t.Run("retrieve roles ids #2", func(t *testing.T) { + bindings := []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"role1", "role2"}, + Groups: []string{"group1"}, + Permissions: []string{"permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding2", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group4"}, + Permissions: []string{"permission7"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding3", + Subjects: []string{"user5"}, + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission10", "permission4"}, + CRUDDocumentState: "PUBLIC", + }, + { + BindingID: "binding4", + Roles: []string{"role3", "role4"}, + Groups: []string{"group2"}, + Permissions: []string{"permission11"}, + CRUDDocumentState: "PUBLIC", + }, + + { + BindingID: "binding5", + Subjects: []string{"user1"}, + Roles: []string{"role3", "role4"}, + Permissions: []string{"permission12"}, + CRUDDocumentState: "PUBLIC", + }, + } + rolesIds := rolesIDsFromBindings(bindings) + expected := []string{"role1", "role2", "role3", "role4"} + require.True(t, reflect.DeepEqual(rolesIds, expected), "Error while getting permissions") + }) +} + +func TestRetrieveUserBindingsAndRoles(t *testing.T) { + log := logging.NewNoOpLogger() + + t.Run("extract user from request without querying MongoDB if client not passed", func(t *testing.T) { + inputUser := types.User{ + Groups: []string{"group1", "group2"}, + ID: "userId", + } + + user, err := Get(context.Background(), log, nil, inputUser) + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{"group1", "group2"}, + }, user) + }) + + t.Run("extract user with no id in headers does not perform queries", func(t *testing.T) { + mock := fake.InputUserClient{ + UserBindingsError: fmt.Errorf("some error"), + } + + _, err := Get(context.Background(), log, mock, types.User{}) + require.NoError(t, err) + }) + + t.Run("extract user but retrieve bindings fails", func(t *testing.T) { + mock := fake.InputUserClient{ + UserBindingsError: fmt.Errorf("some error"), + } + user := types.User{ + Groups: []string{"group1", "group2"}, + ID: "userId", + } + + _, err := Get(context.Background(), log, mock, user) + require.Error(t, err, "Error while retrieving user bindings: some error") + }) + + t.Run("extract user bindings but retrieve roles by role id fails", func(t *testing.T) { + mock := fake.InputUserClient{ + UserBindings: []types.Binding{ + {Roles: []string{"r1", "r2"}}, + }, + UserRolesError: fmt.Errorf("some error 2"), + } + user := types.User{ + Groups: []string{"group1", "group2"}, + ID: "userId", + } + + _, err := Get(context.Background(), log, mock, user) + require.Error(t, err, "Error while retrieving user Roles: some error 2") + }) + + t.Run("extract user bindings and roles", func(t *testing.T) { + mock := fake.InputUserClient{ + UserBindings: []types.Binding{ + {Roles: []string{"r1", "r2"}}, + {Roles: []string{"r3"}}, + }, + UserRoles: []types.Role{ + {RoleID: "r1", Permissions: []string{"p1", "p2"}}, + {RoleID: "r2", Permissions: []string{"p3", "p4"}}, + {RoleID: "r3", Permissions: []string{"p5"}}, + }, + } + user := types.User{ + Groups: []string{"group1", "group2"}, + ID: "userId", + } + + inputUser, err := Get(context.Background(), log, mock, user) + + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{"group1", "group2"}, + Bindings: []types.Binding{ + {Roles: []string{"r1", "r2"}}, + {Roles: []string{"r3"}}, + }, + Roles: []types.Role{ + {RoleID: "r1", Permissions: []string{"p1", "p2"}}, + {RoleID: "r2", Permissions: []string{"p3", "p4"}}, + {RoleID: "r3", Permissions: []string{"p5"}}, + }, + }, inputUser) + }) +} diff --git a/sdk/integration_test.go b/sdk/integration_test.go new file mode 100644 index 00000000..df90f8d3 --- /dev/null +++ b/sdk/integration_test.go @@ -0,0 +1,138 @@ +// Copyright 2023 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 sdk_test + +import ( + "context" + "net/http" + "testing" + + "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/custom_builtins/mocks" + "github.com/rond-authz/rond/logging" + "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk" + + "github.com/stretchr/testify/require" +) + +func TestUsageNewWithConfig(t *testing.T) { + opaModuleConfig, err := core.LoadRegoModule("../mocks/rego-policies") + require.NoError(t, err, "unexpected error") + + ctx := context.Background() + + rondConfig := core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "foobar", + }, + } + + evaluator, err := sdk.NewWithConfig(ctx, opaModuleConfig, rondConfig, nil) + require.NoError(t, err) + + input := core.Input{} + result, err := evaluator.EvaluateRequestPolicy(ctx, input, nil) + require.NoError(t, err) + require.Equal(t, result, sdk.PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }) +} + +func TestUsageNewWithConfigWithMongo(t *testing.T) { + opaModuleConfig, err := core.LoadRegoModule("../mocks/rego-policies-with-mongo-builtins") + require.NoError(t, err, "unexpected error") + + ctx := context.Background() + + rondConfig := core.RondConfig{ + RequestFlow: core.RequestFlow{ + PolicyName: "foobar", + }, + } + + evaluator, err := sdk.NewWithConfig(ctx, opaModuleConfig, rondConfig, &sdk.Options{ + EvaluatorOptions: &sdk.EvaluatorOptions{ + MongoClient: mocks.MongoClientMock{}, + }, + }) + require.NoError(t, err) + + input := core.Input{} + + result, err := evaluator.EvaluateRequestPolicy(ctx, input, nil) + require.NoError(t, err) + require.Equal(t, result, sdk.PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }) +} + +func TestUsageNewFromOas(t *testing.T) { + opaModuleConfig, err := core.LoadRegoModule("../mocks/rego-policies") + require.NoError(t, err, "unexpected error") + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + + ctx := context.Background() + logger := logging.NewNoOpLogger() + + oasFinder, err := sdk.NewFromOAS(ctx, opaModuleConfig, openAPISpec, nil) + require.NoError(t, err) + + evaluator, err := oasFinder.FindEvaluator(http.MethodGet, "/users/") + require.NoError(t, err) + + input := core.Input{} + result, err := evaluator.EvaluateRequestPolicy(ctx, input, &sdk.EvaluateOptions{ + Logger: logger, + }) + require.NoError(t, err) + require.Equal(t, result, sdk.PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }) +} + +func TestUsageNewFromOasWithMongo(t *testing.T) { + opaModuleConfig, err := core.LoadRegoModule("../mocks/rego-policies-with-mongo-builtins") + require.NoError(t, err, "unexpected error") + openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json") + require.NoError(t, err) + + ctx := context.Background() + logger := logging.NewNoOpLogger() + + oasFinder, err := sdk.NewFromOAS(ctx, opaModuleConfig, openAPISpec, &sdk.Options{ + EvaluatorOptions: &sdk.EvaluatorOptions{ + MongoClient: mocks.MongoClientMock{}, + }, + }) + require.NoError(t, err) + + evaluator, err := oasFinder.FindEvaluator(http.MethodGet, "/users/") + require.NoError(t, err) + + input := core.Input{} + result, err := evaluator.EvaluateRequestPolicy(ctx, input, &sdk.EvaluateOptions{ + Logger: logger, + }) + require.NoError(t, err) + require.Equal(t, result, sdk.PolicyResult{ + Allowed: true, + QueryToProxy: []byte(""), + }) +} diff --git a/sdk/openapi.go b/sdk/openapi.go index e6de670f..1dcddcae 100644 --- a/sdk/openapi.go +++ b/sdk/openapi.go @@ -16,7 +16,6 @@ package sdk import ( "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" @@ -29,22 +28,21 @@ type oasImpl struct { opaModuleConfig *core.OPAModuleConfig partialResultEvaluators core.PartialResultsEvaluators - opaEvaluatorOptions *core.OPAEvaluatorOptions + evaluatorOptions *EvaluatorOptions metrics *metrics.Metrics } -func (r oasImpl) FindEvaluator(logger logging.Logger, method, path string) (Evaluator, error) { +func (r oasImpl) FindEvaluator(method, path string) (Evaluator, error) { permission, routerInfo, err := r.oas.FindPermission(r.oasRouter, path, method) if err != nil { return nil, err } return evaluator{ rondConfig: permission, - logger: logger, opaModuleConfig: r.opaModuleConfig, partialResultEvaluators: r.partialResultEvaluators, - opaEvaluatorOptions: r.opaEvaluatorOptions, + evaluatorOptions: r.evaluatorOptions, policyEvaluationOptions: &core.PolicyEvaluationOptions{ Metrics: r.metrics, AdditionalLogFields: map[string]string{ @@ -57,5 +55,5 @@ func (r oasImpl) FindEvaluator(logger logging.Logger, method, path string) (Eval } type OASEvaluatorFinder interface { - FindEvaluator(logger logging.Logger, method, path string) (Evaluator, error) + FindEvaluator(method, path string) (Evaluator, error) } diff --git a/sdk/openapi_test.go b/sdk/openapi_test.go index fdb23c0a..88799ab3 100644 --- a/sdk/openapi_test.go +++ b/sdk/openapi_test.go @@ -48,13 +48,13 @@ func TestOasSDK(t *testing.T) { t.Run("FindEvaluator", func(t *testing.T) { t.Run("throws if path and method not found", func(t *testing.T) { - actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/not-existent/path") + actual, err := sdk.FindEvaluator(http.MethodGet, "/not-existent/path") require.ErrorContains(t, err, "not found oas definition: GET /not-existent/path") require.Nil(t, actual) }) t.Run("returns correct evaluator", func(t *testing.T) { - actual, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + actual, err := sdk.FindEvaluator(http.MethodGet, "/users/") require.NoError(t, err) evaluatorOptions := &core.PolicyEvaluationOptions{ Metrics: oas.metrics, @@ -72,8 +72,8 @@ func TestOasSDK(t *testing.T) { }, opaModuleConfig: opaModule, partialResultEvaluators: oas.partialResultEvaluators, - logger: logger, policyEvaluationOptions: evaluatorOptions, + evaluatorOptions: &EvaluatorOptions{}, }, actual) t.Run("get permissions", func(t *testing.T) { diff --git a/sdk/rondinput/http/input.go b/sdk/rondinput/http/input.go index b6fd4252..8aecf740 100644 --- a/sdk/rondinput/http/input.go +++ b/sdk/rondinput/http/input.go @@ -23,16 +23,9 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" - "github.com/rond-authz/rond/types" ) -type requestInfo struct { - *http.Request - clientTypeHeaderKey string - pathParams map[string]string -} - -func (req requestInfo) Input(user types.User, responseBody any) (core.Input, error) { +func parseRequestBody(req *http.Request) (any, error) { shouldParseJSONBody := utils.HasApplicationJSONContentType(req.Header) && req.ContentLength > 0 && (req.Method == http.MethodPatch || req.Method == http.MethodPost || req.Method == http.MethodPut || req.Method == http.MethodDelete) @@ -48,34 +41,31 @@ func (req requestInfo) Input(user types.User, responseBody any) (core.Input, err } req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) } + return requestBody, nil +} + +// TODO: before to have a stable interface, remove the usage of clientTypeHeaderKey. +// We could add to the core.Input a map[string]any to add any data passed from +// outside instead +func NewInput(req *http.Request, clientTypeHeaderKey string, pathParams map[string]string, user core.InputUser, responseBody any) (core.Input, error) { + requestBody, err := parseRequestBody(req) + if err != nil { + return core.Input{}, err + } return core.Input{ - ClientType: req.Header.Get(req.clientTypeHeaderKey), + ClientType: req.Header.Get(clientTypeHeaderKey), Request: core.InputRequest{ Method: req.Method, Path: req.URL.Path, Headers: req.Header, Query: req.URL.Query(), - PathParams: req.pathParams, + PathParams: pathParams, Body: requestBody, }, Response: core.InputResponse{ Body: responseBody, }, - User: core.InputUser{ - ID: user.UserID, - Properties: user.Properties, - Groups: user.UserGroups, - Bindings: user.UserBindings, - Roles: user.UserRoles, - }, + User: user, }, nil } - -func NewInput(req *http.Request, clientTypeHeaderKey string, pathParams map[string]string) core.RondInput { - return requestInfo{ - Request: req, - clientTypeHeaderKey: clientTypeHeaderKey, - pathParams: pathParams, - } -} diff --git a/sdk/rondinput/http/input_test.go b/sdk/rondinput/http/input_test.go index 5b1b8324..2cee6d64 100644 --- a/sdk/rondinput/http/input_test.go +++ b/sdk/rondinput/http/input_test.go @@ -21,6 +21,7 @@ import ( "net/http/httptest" "testing" + "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" @@ -28,7 +29,7 @@ import ( ) func TestRondInput(t *testing.T) { - user := types.User{} + user := core.InputUser{} clientTypeHeaderKey := "clienttypeheader" pathParams := map[string]string{} @@ -45,8 +46,7 @@ func TestRondInput(t *testing.T) { t.Run("ignored on method GET", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader(reqBodyBytes)) - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) @@ -55,8 +55,7 @@ func TestRondInput(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", nil) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) @@ -67,8 +66,7 @@ func TestRondInput(t *testing.T) { for _, method := range acceptedMethods { req := httptest.NewRequest(method, "/", bytes.NewReader(reqBodyBytes)) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") require.Equal(t, expectedRequestBody, input.Request.Body) } @@ -77,8 +75,7 @@ func TestRondInput(t *testing.T) { t.Run("added with content-type specifying charset", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBodyBytes)) req.Header.Set(utils.ContentTypeHeaderKey, "application/json;charset=UTF-8") - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") require.Equal(t, expectedRequestBody, input.Request.Body) }) @@ -86,8 +83,7 @@ func TestRondInput(t *testing.T) { t.Run("reject on method POST but with invalid body", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) req.Header.Set(utils.ContentTypeHeaderKey, "application/json") - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - _, err := rondRequest.Input(user, nil) + _, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.ErrorContains(t, err, "failed request body deserialization:") }) @@ -95,29 +91,27 @@ func TestRondInput(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte("{notajson}"))) req.Header.Set(utils.ContentTypeHeaderKey, "multipart/form-data") - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") require.Nil(t, input.Request.Body) }) }) t.Run("request userinfo remapping", func(t *testing.T) { - user := types.User{ - UserID: "UserID", - UserGroups: []string{"UserGroups"}, - UserRoles: []types.Role{}, - UserBindings: []types.Binding{}, - Properties: map[string]any{"key": "val"}, + user := core.InputUser{ + ID: "UserID", + Groups: []string{"UserGroups"}, + Roles: []types.Role{}, + Bindings: []types.Binding{}, + Properties: map[string]any{"key": "val"}, } req := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader([]byte{})) - rondRequest := NewInput(req, clientTypeHeaderKey, pathParams) - input, err := rondRequest.Input(user, nil) + input, err := NewInput(req, clientTypeHeaderKey, pathParams, user, nil) require.NoError(t, err, "Unexpected error") - require.Equal(t, user.UserID, input.User.ID) + require.Equal(t, user.ID, input.User.ID) require.EqualValues(t, user.Properties, input.User.Properties) }) } diff --git a/sdk/sdk.go b/sdk/sdk.go index 7205ab66..0e0976db 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -19,14 +19,28 @@ import ( "fmt" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" ) +type EvaluatorOptions struct { + MongoClient custom_builtins.IMongoClient + EnablePrintStatements bool +} + +func (e EvaluatorOptions) opaEvaluatorOptions(logger logging.Logger) *core.OPAEvaluatorOptions { + return &core.OPAEvaluatorOptions{ + Logger: logger, + MongoClient: e.MongoClient, + EnablePrintStatements: e.EnablePrintStatements, + } +} + type Options struct { + EvaluatorOptions *EvaluatorOptions Metrics *metrics.Metrics - EvaluatorOptions *core.OPAEvaluatorOptions Logger logging.Logger } @@ -44,12 +58,12 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas logger = logging.NewNoOpLogger() } - var evaluatorOptions *core.OPAEvaluatorOptions - if options.EvaluatorOptions != nil { - evaluatorOptions = options.EvaluatorOptions + evaluatorOptions := options.EvaluatorOptions + if evaluatorOptions == nil { + evaluatorOptions = &EvaluatorOptions{} } - evaluator, err := openapi.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions) + evaluator, err := openapi.SetupEvaluators(ctx, logger, oas, opaModuleConfig, evaluatorOptions.opaEvaluatorOptions(logger)) if err != nil { return nil, err } @@ -67,7 +81,7 @@ func NewFromOAS(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, oas opaModuleConfig: opaModuleConfig, partialResultEvaluators: evaluator, - opaEvaluatorOptions: evaluatorOptions, + evaluatorOptions: evaluatorOptions, metrics: options.Metrics, }, nil } @@ -81,18 +95,22 @@ func NewWithConfig(ctx context.Context, opaModuleConfig *core.OPAModuleConfig, r logger = logging.NewNoOpLogger() } + evaluatorOptions := options.EvaluatorOptions + if evaluatorOptions == nil { + evaluatorOptions = &EvaluatorOptions{} + } + policyEvaluators := core.PartialResultsEvaluators{} - if err := policyEvaluators.AddFromConfig(ctx, logger, opaModuleConfig, &rondConfig, options.EvaluatorOptions); err != nil { + if err := policyEvaluators.AddFromConfig(ctx, logger, opaModuleConfig, &rondConfig, evaluatorOptions.opaEvaluatorOptions(logger)); err != nil { return nil, err } return evaluator{ rondConfig: rondConfig, - logger: logger, opaModuleConfig: opaModuleConfig, partialResultEvaluators: policyEvaluators, - opaEvaluatorOptions: options.EvaluatorOptions, + evaluatorOptions: evaluatorOptions, policyEvaluationOptions: &core.PolicyEvaluationOptions{ Metrics: options.Metrics, }, diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index a79d84a3..0ed460b5 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -21,11 +21,11 @@ import ( "testing" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/mocks" + "github.com/rond-authz/rond/custom_builtins" + "github.com/rond-authz/rond/custom_builtins/mocks" "github.com/rond-authz/rond/logging" "github.com/rond-authz/rond/metrics" "github.com/rond-authz/rond/openapi" - "github.com/rond-authz/rond/types" "github.com/stretchr/testify/require" ) @@ -76,7 +76,7 @@ func TestNewFromOas(t *testing.T) { }) t.Run("passes EvaluatorOptions and set metrics correctly", func(t *testing.T) { - evalOpts := &core.OPAEvaluatorOptions{ + evalOpts := &EvaluatorOptions{ EnablePrintStatements: true, } sdk, err := NewFromOAS(ctx, opaModule, openAPISpec, &Options{ @@ -88,7 +88,7 @@ func TestNewFromOas(t *testing.T) { require.NotEmpty(t, sdk) r, ok := sdk.(oasImpl) require.True(t, ok) - require.Equal(t, evalOpts, r.opaEvaluatorOptions) + require.Equal(t, evalOpts, r.evaluatorOptions) }) t.Run("creates OAS sdk correctly", func(t *testing.T) { @@ -96,7 +96,7 @@ func TestNewFromOas(t *testing.T) { require.NoError(t, err) t.Run("and find evaluators", func(t *testing.T) { - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + evaluator, err := sdk.FindEvaluator(http.MethodGet, "/users/") require.NoError(t, err) require.NotNil(t, evaluator) }) @@ -108,7 +108,7 @@ func TestNewFromOas(t *testing.T) { require.NotNil(t, sdk) t.Run("and find evaluators", func(t *testing.T) { - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + evaluator, err := sdk.FindEvaluator(http.MethodGet, "/users/") require.NoError(t, err) require.NotNil(t, evaluator) }) @@ -120,7 +120,7 @@ func TestNewFromOas(t *testing.T) { require.NotNil(t, sdk) t.Run("and find evaluators", func(t *testing.T) { - evaluator, err := sdk.FindEvaluator(logger, http.MethodGet, "/users/") + evaluator, err := sdk.FindEvaluator(http.MethodGet, "/users/") require.NoError(t, err) require.NotNil(t, evaluator) }) @@ -165,7 +165,7 @@ func TestNewWithConfig(t *testing.T) { require.NotNil(t, evaluator) t.Run("run evaluator correctly", func(t *testing.T) { - result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ Allowed: true, @@ -180,7 +180,7 @@ func TestNewWithConfig(t *testing.T) { require.NotNil(t, evaluator) t.Run("run evaluator correctly", func(t *testing.T) { - result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ Allowed: true, @@ -190,7 +190,7 @@ func TestNewWithConfig(t *testing.T) { }) t.Run("passes EvaluatorOptions and set metrics correctly", func(t *testing.T) { - evalOpts := &core.OPAEvaluatorOptions{ + evalOpts := &EvaluatorOptions{ EnablePrintStatements: true, } eval, err := NewWithConfig(ctx, opaModule, rondConfig, &Options{ @@ -202,7 +202,7 @@ func TestNewWithConfig(t *testing.T) { require.NotEmpty(t, eval) r, ok := eval.(evaluator) require.True(t, ok) - require.Equal(t, evalOpts, r.opaEvaluatorOptions) + require.Equal(t, evalOpts, r.evaluatorOptions) }) t.Run("creates config sdk correctly", func(t *testing.T) { @@ -211,7 +211,7 @@ func TestNewWithConfig(t *testing.T) { require.NotNil(t, evaluator) t.Run("run evaluator correctly", func(t *testing.T) { - result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ Allowed: true, @@ -233,7 +233,7 @@ func TestNewWithConfig(t *testing.T) { } options := &Options{ Logger: logger, - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &EvaluatorOptions{ MongoClient: mocks.MongoClientMock{ FindOneResult: map[string]string{"myField": "1234"}, FindOneExpectation: func(collectionName string, query interface{}) { @@ -249,7 +249,7 @@ func TestNewWithConfig(t *testing.T) { require.NotNil(t, evaluator) t.Run("run evaluator correctly", func(t *testing.T) { - result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, ""), types.User{}) + result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ Allowed: true, @@ -263,7 +263,7 @@ type sdkOptions struct { opaModuleContent string oasFilePath string - mongoClient types.IMongoClient + mongoClient custom_builtins.IMongoClient metrics *metrics.Metrics } @@ -271,35 +271,17 @@ type tHelper interface { Helper() } -type FakeInput struct { - request core.InputRequest - clientType string -} +func getFakeInput(t require.TestingT, request core.InputRequest, clientType string, user core.InputUser, responseBody any) core.Input { + if h, ok := t.(tHelper); ok { + h.Helper() + } -func (i FakeInput) Input(user types.User, responseBody any) (core.Input, error) { return core.Input{ - User: core.InputUser{ - ID: user.UserID, - Properties: user.Properties, - Groups: user.UserGroups, - Bindings: user.UserBindings, - Roles: user.UserRoles, - }, - Request: i.request, + User: user, + Request: request, Response: core.InputResponse{ Body: responseBody, }, - ClientType: i.clientType, - }, nil -} - -func getFakeInput(t require.TestingT, request core.InputRequest, clientType string) core.RondInput { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - return FakeInput{ - request: request, - clientType: clientType, + ClientType: clientType, } } diff --git a/service/handler.go b/service/handler.go index 9ababeb0..de91cbfb 100644 --- a/service/handler.go +++ b/service/handler.go @@ -16,16 +16,18 @@ package service import ( "errors" + "fmt" "net/http" "net/http/httputil" + "strings" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/opatranslator" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/sdk" + "github.com/rond-authz/rond/sdk/inputuser" rondhttp "github.com/rond-authz/rond/sdk/rondinput/http" "github.com/rond-authz/rond/types" @@ -43,6 +45,7 @@ func ReverseProxyOrResponse( w http.ResponseWriter, req *http.Request, evaluatorSdk sdk.Evaluator, + inputUser core.InputUser, ) { var permission core.RondConfig if evaluatorSdk != nil { @@ -64,7 +67,7 @@ func ReverseProxyOrResponse( } return } - ReverseProxy(logger, env, w, req, &permission, evaluatorSdk) + ReverseProxy(logger, env, w, req, &permission, evaluatorSdk, inputUser) } func rbacHandler(w http.ResponseWriter, req *http.Request) { @@ -85,10 +88,17 @@ func rbacHandler(w http.ResponseWriter, req *http.Request) { return } - if err := EvaluateRequest(req, env, w, evaluatorSdk); err != nil { + rondInputUser, err := getInputUser(logger, env, req) + if err != nil { + logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed to get input user") + utils.FailResponse(w, "failed to get input user", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } - ReverseProxyOrResponse(logger, env, w, req, evaluatorSdk) + + if err := EvaluateRequest(req, env, w, evaluatorSdk, rondInputUser); err != nil { + return + } + ReverseProxyOrResponse(logger, env, w, req, evaluatorSdk, rondInputUser) } func EvaluateRequest( @@ -96,24 +106,21 @@ func EvaluateRequest( env config.EnvironmentVariables, w http.ResponseWriter, evaluatorSdk sdk.Evaluator, + rondInputUser core.InputUser, ) error { logger := glogrus.FromContext(req.Context()) permission := evaluatorSdk.Config() - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(rondlogrus.NewEntry(logger), req, types.UserHeadersKeys{ - IDHeaderKey: env.UserIdHeader, - GroupsHeaderKey: env.UserGroupsHeader, - PropertiesHeaderKey: env.UserPropertiesHeader, - }) + rondInput, err := rondhttp.NewInput(req, env.ClientTypeHeader, mux.Vars(req), rondInputUser, nil) if err != nil { - logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed user bindings and roles retrieving") - utils.FailResponseWithCode(w, http.StatusInternalServerError, "user bindings retrieval failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) + logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed to create rond input") + utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed to create rond input", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return err } - - rondInput := rondhttp.NewInput(req, env.ClientTypeHeader, mux.Vars(req)) - result, err := evaluatorSdk.EvaluateRequestPolicy(req.Context(), rondInput, userInfo) + result, err := evaluatorSdk.EvaluateRequestPolicy(req.Context(), rondInput, &sdk.EvaluateOptions{ + Logger: rondlogrus.NewEntry(logger), + }) if err != nil { if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) @@ -136,6 +143,7 @@ func EvaluateRequest( if permission.RequestFlow.QueryOptions.HeaderName != "" { queryHeaderKey = permission.RequestFlow.QueryOptions.HeaderName } + // FIXME: header is always set, also if query to proxy is empty if result.QueryToProxy != nil { req.Header.Set(queryHeaderKey, string(result.QueryToProxy)) } @@ -149,6 +157,7 @@ func ReverseProxy( req *http.Request, permission *core.RondConfig, evaluatorSdk sdk.Evaluator, + inputUser core.InputUser, ) { targetHostFromEnv := env.TargetServiceHost proxy := httputil.ReverseProxy{ @@ -175,11 +184,7 @@ func ReverseProxy( req, env.ClientTypeHeader, - types.UserHeadersKeys{ - IDHeaderKey: env.UserIdHeader, - GroupsHeaderKey: env.UserGroupsHeader, - PropertiesHeaderKey: env.UserPropertiesHeader, - }, + inputUser, evaluatorSdk, ) proxy.ServeHTTP(w, req) @@ -194,5 +199,57 @@ func alwaysProxyHandler(w http.ResponseWriter, req *http.Request) { utils.FailResponse(w, "no environment found in context", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } - ReverseProxyOrResponse(logger, env, w, req, nil) + ReverseProxyOrResponse(logger, env, w, req, nil, core.InputUser{}) +} + +type userHeadersKeys struct { + GroupsHeaderKey string + IDHeaderKey string + PropertiesHeaderKey string +} + +func getUserFromRequest(req *http.Request, userHeaders userHeadersKeys) (types.User, error) { + var user types.User + + user.Groups = split(req.Header.Get(userHeaders.GroupsHeaderKey), ",") + user.ID = req.Header.Get(userHeaders.IDHeaderKey) + + userProperties := make(map[string]interface{}) + _, err := utils.UnmarshalHeader(req.Header, userHeaders.PropertiesHeaderKey, &userProperties) + if err != nil { + return types.User{}, fmt.Errorf("user properties header is not valid: %s", err.Error()) + } + user.Properties = userProperties + + return user, nil +} + +func getInputUser(logger *logrus.Entry, env config.EnvironmentVariables, req *http.Request) (core.InputUser, error) { + user, err := getUserFromRequest(req, userHeadersKeys{ + IDHeaderKey: env.UserIdHeader, + GroupsHeaderKey: env.UserGroupsHeader, + PropertiesHeaderKey: env.UserPropertiesHeader, + }) + if err != nil { + return core.InputUser{}, fmt.Errorf("fails to get user from request: %s", err) + } + + client, err := inputuser.GetClientFromContext(req.Context()) + if err != nil { + return core.InputUser{}, err + } + + rondInputUser, err := inputuser.Get(req.Context(), rondlogrus.NewEntry(logger), client, user) + if err != nil { + return core.InputUser{}, err + } + + return rondInputUser, nil +} + +func split(str, sep string) []string { + if str == "" { + return []string{} + } + return strings.Split(str, sep) } diff --git a/service/handler_test.go b/service/handler_test.go index 6ac515aa..0358f21a 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -23,18 +23,18 @@ import ( "net/http" "net/http/httptest" "net/url" - "reflect" "strings" "testing" "github.com/rond-authz/rond/core" + cbmocks "github.com/rond-authz/rond/custom_builtins/mocks" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mocks" - "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/internal/fake" "github.com/rond-authz/rond/internal/testutils" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" + "github.com/rond-authz/rond/sdk/inputuser" "github.com/rond-authz/rond/types" "github.com/gorilla/mux" @@ -46,16 +46,6 @@ import ( "github.com/stretchr/testify/require" ) -var mockRondConfigWithQueryGen = core.RondConfig{ - RequestFlow: core.RequestFlow{ - PolicyName: "allow", - GenerateQuery: true, - QueryOptions: core.QueryOptions{ - HeaderName: "rowfilterquery", - }, - }, -} - func TestDirectProxyHandler(t *testing.T) { oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ @@ -103,12 +93,13 @@ func TestDirectProxyHandler(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -135,12 +126,13 @@ func TestDirectProxyHandler(t *testing.T) { defer server.Close() serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) @@ -172,12 +164,13 @@ func TestDirectProxyHandler(t *testing.T) { body := strings.NewReader(mockBodySting) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -218,14 +211,14 @@ func TestDirectProxyHandler(t *testing.T) { serverURL, _ := url.Parse(server.URL) - rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, rondConfig, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://www.example.com:8080/api", body) @@ -288,12 +281,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -342,12 +336,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -409,12 +404,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -465,13 +461,14 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -521,7 +518,7 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) serverURL, _ := url.Parse(server.URL) ctx := createContext(t, @@ -529,6 +526,7 @@ allow { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -570,12 +568,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -615,12 +614,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -681,12 +681,13 @@ allow { OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -725,7 +726,7 @@ allow { registry := prometheus.NewRegistry() - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", &evaluatorParams{ + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/api", &evaluatorParams{ logger: logger, registry: registry, }) @@ -734,6 +735,7 @@ allow { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + logEntry, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -801,7 +803,7 @@ allow { registry := prometheus.NewRegistry() - evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", &evaluatorParams{ + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", &evaluatorParams{ logger: logger, registry: registry, }) @@ -810,6 +812,7 @@ allow { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + logEntry, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -882,12 +885,13 @@ func TestStandaloneMode(t *testing.T) { ctx := glogger.WithLogger(context.Background(), logrus.NewEntry(log)) t.Run("ok", func(t *testing.T) { - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -929,12 +933,13 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -978,12 +983,13 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1027,12 +1033,13 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1076,12 +1083,13 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1127,12 +1135,13 @@ allow { body := strings.NewReader(mockBodySting) opaModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} - evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, mockRondConfigWithQueryGen, oasWithFilter, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModuleConfig, nil, oasWithFilter, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), env, evaluator, nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) @@ -1218,13 +1227,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { count(input.request.headers["%s"]) != 0 }`, mockHeader), } - rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, opaModule, nil, rondConfig, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1259,12 +1268,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { todo { get_header("x-backdoor", input.request.headers) == "mocked value" }`, } - evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1317,7 +1327,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }`, mockedUserProperties["my"], mockedClientType), } - evaluator := getEvaluator(t, ctx, opaModule, nil, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1328,6 +1338,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, evaluator, nil, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1397,13 +1408,13 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - rondConfig := &core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} - evaluator := getEvaluator(t, ctx, opaModule, nil, *rondConfig, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) t.Run("request respects the policy", func(t *testing.T) { @@ -1420,54 +1431,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }) }) - t.Run("Test retrieve roles ids from bindings", func(t *testing.T) { - bindings := []types.Binding{ - { - BindingID: "binding1", - Subjects: []string{"user1"}, - Roles: []string{"role1", "role2"}, - Groups: []string{"group1"}, - Permissions: []string{"permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding2", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group4"}, - Permissions: []string{"permission7"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding3", - Subjects: []string{"user5"}, - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission10", "permission4"}, - CRUDDocumentState: "PUBLIC", - }, - { - BindingID: "binding4", - Roles: []string{"role3", "role4"}, - Groups: []string{"group2"}, - Permissions: []string{"permission11"}, - CRUDDocumentState: "PUBLIC", - }, - - { - BindingID: "binding5", - Subjects: []string{"user1"}, - Roles: []string{"role3", "role4"}, - Permissions: []string{"permission12"}, - CRUDDocumentState: "PUBLIC", - }, - } - rolesIds := mongoclient.RolesIDsFromBindings(bindings) - expected := []string{"role1", "role2", "role3", "role4"} - require.True(t, reflect.DeepEqual(rolesIds, expected), "Error while getting permissions") - }) - - t.Run("TestHandlerWithUserPermissionsRetrievalFromMongoDB", func(t *testing.T) { + t.Run("TestHandlerWithUserPermissionsRetrieval", func(t *testing.T) { t.Run("return 500 if retrieveUserBindings goes bad", func(t *testing.T) { invoked := false @@ -1479,9 +1443,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("Something went wrong"), UserBindings: nil, UserRoles: nil, UserRolesError: errors.New("Something went wrong")} - - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + inputUserClientMock := &fake.InputUserClient{UserBindingsError: errors.New("Something went wrong"), UserBindings: nil, UserRoles: nil, UserRolesError: errors.New("Something went wrong")} + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1495,7 +1458,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1523,9 +1487,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - mongoclientMock := &mocks.MongoClientMock{UserBindingsError: errors.New("MongoDB Error"), UserRolesError: errors.New("MongoDB Error")} + inputUserClientMock := &fake.InputUserClient{UserBindingsError: errors.New("MongoDB Error"), UserRolesError: errors.New("MongoDB Error")} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1539,7 +1503,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1551,7 +1516,7 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) rbacHandler(w, r) - testutils.AssertResponseFullErrorMessages(t, w, http.StatusInternalServerError, "user bindings retrieval failed", utils.GENERIC_BUSINESS_ERROR_MESSAGE) + testutils.AssertResponseFullErrorMessages(t, w, http.StatusInternalServerError, "failed to get input user", utils.GENERIC_BUSINESS_ERROR_MESSAGE) require.True(t, !invoked, "Handler was not invoked.") require.Equal(t, http.StatusInternalServerError, w.Result().StatusCode, "Unexpected status code.") }) @@ -1606,9 +1571,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1622,7 +1587,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1693,9 +1659,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { } serverURL, _ := url.Parse(server.URL) - mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1709,7 +1675,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1791,10 +1758,10 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { }, } - mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1808,7 +1775,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1842,9 +1810,9 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { serverURL, _ := url.Parse(server.URL) - mongoclientMock := &mocks.MongoClientMock{UserBindings: nil} + inputUserClientMock := &fake.InputUserClient{UserBindings: nil} - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1858,7 +1826,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1899,10 +1868,10 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { userBindings := []types.Binding{} userRoles := []types.Role{} - mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, opaModule, mongoclientMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{ @@ -1916,7 +1885,8 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { BindingsCollectionName: "bindings", }, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) w := httptest.NewRecorder() @@ -1935,6 +1905,112 @@ func TestPolicyEvaluationAndUserPolicyRequirements(t *testing.T) { require.True(t, invoked, "Handler was not invoked.") require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") }) + + t.Run("policy on user on response", func(t *testing.T) { + invoked := false + responseBody := []byte(`{"msg":"ok"}`) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + require.Equal(t, string(mockedUserPropertiesStringified), r.Header.Get(userPropertiesHeaderKey), "Mocked User properties not found") + require.Equal(t, string(mockedUserGroupsHeaderValue), r.Header.Get(userGroupsHeaderKey), "Mocked User groups not found") + require.Equal(t, mockedClientType, r.Header.Get(clientTypeHeaderKey), "Mocked client type not found") + require.Equal(t, userIdHeaderKey, r.Header.Get(userIdHeaderKey), "Mocked user id not found") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(responseBody) + })) + defer server.Close() + + userBindings := []types.Binding{ + { + BindingID: "binding1", + Subjects: []string{"user1"}, + Roles: []string{"role1", "role2"}, + Groups: []string{"group1"}, + Permissions: []string{"permission4"}, + CRUDDocumentState: "PUBLIC", + }, + } + + userRoles := []types.Role{ + { + RoleID: "role3", + Permissions: []string{"permission1", "permission2", "foobar"}, + CRUDDocumentState: "PUBLIC", + }, + { + RoleID: "role4", + Permissions: []string{"permission3", "permission5"}, + CRUDDocumentState: "PUBLIC", + }, + } + + serverURL, _ := url.Parse(server.URL) + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} + + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + todo { true } + + proj_res[msg] { + count(input.user.bindings) == 1 + input.user.bindings[0].bindingId == "binding1" + count(input.user.roles) == 2 + + msg := input.response.body + } + `, + } + + oas := &openapi.OpenAPISpec{ + Paths: openapi.OpenAPIPaths{ + "/api": openapi.PathVerbs{ + "get": openapi.VerbConfig{ + PermissionV2: &core.RondConfig{ + RequestFlow: core.RequestFlow{PolicyName: "todo"}, + ResponseFlow: core.ResponseFlow{PolicyName: "proj_res"}, + }, + }, + }, + }, + } + + evaluator := getEvaluator(t, ctx, opaModule, nil, oas, http.MethodGet, "/api", nil) + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{ + TargetServiceHost: serverURL.Host, + UserPropertiesHeader: userPropertiesHeaderKey, + UserGroupsHeader: userGroupsHeaderKey, + ClientTypeHeader: clientTypeHeaderKey, + UserIdHeader: userIdHeaderKey, + MongoDBUrl: "mongodb://test", + RolesCollectionName: "roles", + BindingsCollectionName: "bindings", + }, + evaluator, + inputUserClientMock, + nil, + ) + + w := httptest.NewRecorder() + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", nil) + require.NoError(t, err, "Unexpected error") + + r.Header.Set(userPropertiesHeaderKey, string(mockedUserPropertiesStringified)) + r.Header.Set(userGroupsHeaderKey, string(mockedUserGroupsHeaderValue)) + r.Header.Set(clientTypeHeaderKey, string(mockedClientType)) + r.Header.Set(userIdHeaderKey, "miauserid") + rbacHandler(w, r) + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + + resBody, err := io.ReadAll(w.Result().Body) + require.NoError(t, err) + require.Equal(t, responseBody, resBody) + }) }) } @@ -1948,7 +2024,6 @@ project := find_one("projects", {"projectId": "1234"}) project.tenantId == "1234" }`, } - var mockXPermission = core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "todo"}} oas := &openapi.OpenAPISpec{ Paths: openapi.OpenAPIPaths{ "/api": openapi.PathVerbs{ @@ -1969,7 +2044,7 @@ project.tenantId == "1234" })) defer server.Close() - mongoMock := &mocks.MongoClientMock{ + mongoMock := &cbmocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { require.Equal(t, "projects", collectionName) require.Equal(t, map[string]interface{}{ @@ -1982,18 +2057,17 @@ project.tenantId == "1234" userBindings := []types.Binding{} userRoles := []types.Role{} - log, _ := test.NewNullLogger() - mongoclientMock := &mocks.MongoClientMock{UserBindings: userBindings, UserRoles: userRoles} + inputUserClientMock := &fake.InputUserClient{UserBindings: userBindings, UserRoles: userRoles} - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, context.Background(), mockOPAModule, mongoMock, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mongoclientMock, + inputUserClientMock, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2015,7 +2089,7 @@ project.tenantId == "1234" })) defer server.Close() - mongoMock := &mocks.MongoClientMock{ + mongoMock := &cbmocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { require.Equal(t, "projects", collectionName) require.Equal(t, map[string]interface{}{ @@ -2025,17 +2099,14 @@ project.tenantId == "1234" FindOneError: fmt.Errorf("FAILED MONGO QUERY"), } - log, _ := test.NewNullLogger() - - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, context.Background(), mockOPAModule, mongoMock, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mongoMock, + nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2057,7 +2128,7 @@ project.tenantId == "1234" })) defer server.Close() - mongoMock := &mocks.MongoClientMock{ + mongoMock := &cbmocks.MongoClientMock{ FindOneExpectation: func(collectionName string, query interface{}) { require.Equal(t, "projects", collectionName) require.Equal(t, map[string]interface{}{ @@ -2067,17 +2138,14 @@ project.tenantId == "1234" FindOneResult: nil, // not found corresponds to a nil interface. } - log, _ := test.NewNullLogger() - - ctxForPartial := glogger.WithLogger(mongoclient.WithMongoClient(context.Background(), mongoMock), logrus.NewEntry(log)) - serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctxForPartial, mockOPAModule, mongoMock, mockXPermission, oas, http.MethodGet, "/api", nil) + evaluator := getEvaluator(t, context.Background(), mockOPAModule, mongoMock, oas, http.MethodGet, "/api", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, - mongoMock, + nil, + nil, ) r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api?mockQuery=iamquery", nil) @@ -2095,9 +2163,102 @@ project.tenantId == "1234" func findLogWithMessage(logs []*logrus.Entry, message string) []*logrus.Entry { logToReturn := []*logrus.Entry{} for _, log := range logs { + fmt.Printf("LOG MESSAGE %s\n", log.Message) if log.Message == message { logToReturn = append(logToReturn, log) } } return logToReturn } + +func TestGetInputUser(t *testing.T) { + log, _ := test.NewNullLogger() + logger := logrus.NewEntry(log) + + env := config.EnvironmentVariables{ + UserIdHeader: "useridheaderkey", + UserGroupsHeader: "groupskey", + UserPropertiesHeader: "propertieskey", + } + + t.Run("without groups", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set(env.UserIdHeader, "userId") + + inputUser, err := getInputUser(logger, env, req) + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{}, + Properties: map[string]interface{}{}, + }, inputUser) + }) + + t.Run("allow empty userproperties header", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set(env.UserIdHeader, "userId") + req.Header.Set(env.UserGroupsHeader, "group1,group2") + + inputUser, err := getInputUser(logger, env, req) + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{"group1", "group2"}, + Properties: map[string]interface{}{}, + }, inputUser) + }) + + t.Run("with userproperties header", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set(env.UserIdHeader, "userId") + req.Header.Set(env.UserGroupsHeader, "group1,group2") + req.Header.Set(env.UserPropertiesHeader, `{"name":"my-name"}`) + + inputUser, err := getInputUser(logger, env, req) + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{"group1", "group2"}, + Properties: map[string]interface{}{ + "name": "my-name", + }, + }, inputUser) + }) + + t.Run("fail on invalid userproperties header value", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set(env.UserIdHeader, "userId") + req.Header.Set(env.UserPropertiesHeader, "1") + + _, err := getInputUser(logger, env, req) + require.ErrorContains(t, err, "user properties header is not valid:") + }) + + t.Run("with client and userId", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set(env.UserIdHeader, "userId") + + roles := []types.Role{ + {RoleID: "role-1"}, + } + bindings := []types.Binding{ + {BindingID: "binding-1"}, + } + ctx := inputuser.AddClientInContext(context.Background(), fake.InputUserClient{ + UserRoles: roles, + UserBindings: bindings, + }) + req = req.WithContext(ctx) + + inputUser, err := getInputUser(logger, env, req) + require.NoError(t, err) + require.Equal(t, core.InputUser{ + ID: "userId", + Groups: []string{}, + Properties: map[string]interface{}{}, + Bindings: bindings, + Roles: roles, + }, inputUser) + }) + +} diff --git a/service/opa_transport.go b/service/opa_transport.go index a299acc4..a1fb0a26 100644 --- a/service/opa_transport.go +++ b/service/opa_transport.go @@ -24,7 +24,6 @@ import ( "strconv" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/sdk" @@ -48,7 +47,7 @@ type OPATransport struct { request *http.Request clientHeaderKey string - userHeaders types.UserHeadersKeys + user core.InputUser evaluatorSDK sdk.Evaluator } @@ -58,7 +57,7 @@ func NewOPATransport( logger *logrus.Entry, req *http.Request, clientHeaderKey string, - userHeadersKeys types.UserHeadersKeys, + user core.InputUser, evaluatorSDK sdk.Evaluator, ) *OPATransport { return &OPATransport{ @@ -67,8 +66,8 @@ func NewOPATransport( logger: logger, request: req, + user: user, clientHeaderKey: clientHeaderKey, - userHeaders: userHeadersKeys, evaluatorSDK: evaluatorSDK, } } @@ -110,16 +109,16 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er return nil, fmt.Errorf("%w: %s", ErrOPATransportInvalidResponseBody, err.Error()) } - userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(rondlogrus.NewEntry(t.logger), t.request, t.userHeaders) + pathParams := mux.Vars(t.request) + input, err := rondhttp.NewInput(t.request, t.clientHeaderKey, pathParams, t.user, decodedBody) if err != nil { t.responseWithError(resp, err, http.StatusInternalServerError) return resp, nil } - pathParams := mux.Vars(t.request) - input := rondhttp.NewInput(t.request, t.clientHeaderKey, pathParams) - - responseBody, err := t.evaluatorSDK.EvaluateResponsePolicy(t.context, input, userInfo, decodedBody) + responseBody, err := t.evaluatorSDK.EvaluateResponsePolicy(t.context, input, &sdk.EvaluateOptions{ + Logger: rondlogrus.NewEntry(t.logger), + }) if err != nil { t.responseWithError(resp, err, http.StatusForbidden) return resp, nil diff --git a/service/opa_transport_test.go b/service/opa_transport_test.go index 4e0c6ff4..c5be8632 100644 --- a/service/opa_transport_test.go +++ b/service/opa_transport_test.go @@ -26,8 +26,7 @@ import ( "testing" "github.com/rond-authz/rond/core" - "github.com/rond-authz/rond/internal/mocks" - "github.com/rond-authz/rond/internal/mongoclient" + "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/utils" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" @@ -62,7 +61,7 @@ func TestRoundTripErrors(t *testing.T) { logrus.NewEntry(logger), req, "", - types.UserHeadersKeys{}, + core.InputUser{}, nil, ) @@ -99,7 +98,7 @@ func TestOPATransportResponseWithError(t *testing.T) { logrus.NewEntry(logger), req, "", - types.UserHeadersKeys{}, + core.InputUser{}, nil, ) @@ -159,11 +158,7 @@ func TestOPATransportRoundTrip(t *testing.T) { logrus.NewEntry(logger), req, "", - types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + core.InputUser{}, nil, ) @@ -183,11 +178,7 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + user: core.InputUser{}, } updatedResp, err := transport.RoundTrip(req) @@ -211,11 +202,7 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + user: core.InputUser{}, } resp, err := transport.RoundTrip(req) @@ -238,11 +225,7 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + user: core.InputUser{}, } resp, err := transport.RoundTrip(req) @@ -264,11 +247,7 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + user: core.InputUser{}, } resp, err := transport.RoundTrip(req) @@ -284,14 +263,13 @@ func TestOPATransportRoundTrip(t *testing.T) { Header: http.Header{"Content-Type": []string{"application/json"}}, } - logEntry := rondlogrus.NewLogger(logger) req = req.Clone(context.Background()) evaluator := getSdk(t, &sdkOptions{ oasFilePath: "../mocks/rondOasConfig.json", opaModuleContent: "package policies responsepolicy [resources] { resources := input.response.body }", }) - evaluatorSDK, err := evaluator.FindEvaluator(logEntry, http.MethodGet, "/users/") + evaluatorSDK, err := evaluator.FindEvaluator(http.MethodGet, "/users/") require.NoError(t, err) transport := NewOPATransport( @@ -300,11 +278,7 @@ func TestOPATransportRoundTrip(t *testing.T) { logrus.NewEntry(logger), req, "", - types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, + core.InputUser{}, evaluatorSDK, ) @@ -336,11 +310,6 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, } resp, err := transport.RoundTrip(req) @@ -363,89 +332,12 @@ func TestOPATransportRoundTrip(t *testing.T) { context: req.Context(), logger: logrus.NewEntry(logger), request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, } resp, err := transport.RoundTrip(req) require.Nil(t, resp) require.Error(t, err, "response body is not valid") }) - - t.Run("failure on get user bindings and roles", func(t *testing.T) { - db := mocks.MongoClientMock{ - UserBindingsError: fmt.Errorf("fail from mongoclient"), - } - ctx := mongoclient.WithMongoClient(req.Context(), db) - req := req.WithContext(ctx) - req.Header.Set("useridheader", "userid") - resp := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader([]byte(`{"hey":"there"}`))), - ContentLength: 0, - Header: http.Header{"Content-Type": []string{"application/json"}}, - } - transport := &OPATransport{ - RoundTripper: &MockRoundTrip{Response: resp}, - context: ctx, - logger: logrus.NewEntry(logger), - request: req, - userHeaders: types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, - } - resp, err := transport.RoundTrip(req) - require.Nil(t, err) - require.Equal(t, http.StatusInternalServerError, resp.StatusCode) - bodyBytes, err := io.ReadAll(resp.Body) - require.Nil(t, err) - require.Contains(t, string(bodyBytes), "error while retrieving user bindings") - }) - - t.Run("failure on create rego input", func(t *testing.T) { - req := req.Clone(req.Context()) - req.Header.Set("useridheader", "userid") - req.Header.Set("groupsheader", "a,b,c") - req.Header.Set("userpropertiesheader", "{}{}{}{{") - resp := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader([]byte(`{"hey":"there"}`))), - ContentLength: 0, - Header: http.Header{"Content-Type": []string{"application/json"}}, - } - evaluator := getSdk(t, &sdkOptions{ - oasFilePath: "../mocks/rondOasConfig.json", - opaModuleContent: "package policies responsepolicy [resources] { resources := input.response.body }", - }) - logEntry := rondlogrus.NewLogger(logger) - evaluatorSDK, err := evaluator.FindEvaluator(logEntry, http.MethodGet, "/users/") - require.NoError(t, err) - - transport := NewOPATransport( - &MockRoundTrip{Response: resp}, - req.Context(), - logrus.NewEntry(logger), - req, - "", - types.UserHeadersKeys{ - IDHeaderKey: "useridheader", - GroupsHeaderKey: "usergroupsheader", - PropertiesHeaderKey: "userpropertiesheader", - }, - evaluatorSDK, - ) - resp, err = transport.RoundTrip(req) - require.Nil(t, err) - require.Equal(t, http.StatusInternalServerError, resp.StatusCode) - bodyBytes, err := io.ReadAll(resp.Body) - require.Nil(t, err) - require.Contains(t, string(bodyBytes), "user properties header is not valid") - }) } type MockRoundTrip struct { @@ -475,7 +367,7 @@ type sdkOptions struct { opaModuleContent string oasFilePath string - mongoClient types.IMongoClient + mongoClient custom_builtins.IMongoClient } type tHelper interface { @@ -509,7 +401,7 @@ func getSdk(t require.TestingT, options *sdkOptions) sdk.OASEvaluatorFinder { } sdk, err := sdk.NewFromOAS(context.Background(), opaModule, openAPISpec, &sdk.Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &sdk.EvaluatorOptions{ MongoClient: options.mongoClient, EnablePrintStatements: true, }, diff --git a/service/opamiddleware.go b/service/opamiddleware.go index 34a6b1f6..e2a14667 100644 --- a/service/opamiddleware.go +++ b/service/opamiddleware.go @@ -22,7 +22,6 @@ import ( glogrus "github.com/mia-platform/glogger/v4/loggers/logrus" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/utils" - rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" @@ -56,7 +55,7 @@ func OPAMiddleware( logger := glogrus.FromContext(r.Context()) - evaluator, err := rondSDK.FindEvaluator(rondlogrus.NewEntry(logger), r.Method, path) + evaluator, err := rondSDK.FindEvaluator(r.Method, path) rondConfig := core.RondConfig{} if err == nil { rondConfig = evaluator.Config() diff --git a/service/router.go b/service/router.go index 7c9c4ee4..e06bd32d 100644 --- a/service/router.go +++ b/service/router.go @@ -24,10 +24,10 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mongoclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" + "github.com/rond-authz/rond/sdk/inputuser" "github.com/rond-authz/rond/types" swagger "github.com/davidebianchi/gswagger" @@ -102,7 +102,7 @@ func SetupRouter( opaModuleConfig *core.OPAModuleConfig, oas *openapi.OpenAPISpec, sdk sdk.OASEvaluatorFinder, - mongoClient *mongoclient.MongoClient, + inputUserClient inputuser.Client, registry *prometheus.Registry, ) (*mux.Router, error) { router := mux.NewRouter().UseEncodedPath() @@ -157,8 +157,8 @@ func SetupRouter( PathPrefixStandalone: env.PathPrefixStandalone, })) - if mongoClient != nil { - evalRouter.Use(mongoclient.MongoClientInjectorMiddleware(mongoClient)) + if inputUserClient != nil { + evalRouter.Use(inputuser.ClientInjectorMiddleware(inputUserClient)) } setupRoutes(evalRouter, oas, env) diff --git a/service/router_test.go b/service/router_test.go index b8e2c7b5..ece7bcb2 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -24,16 +24,16 @@ import ( "testing" "github.com/rond-authz/rond/core" + "github.com/rond-authz/rond/custom_builtins" "github.com/rond-authz/rond/internal/config" "github.com/rond-authz/rond/internal/fake" - "github.com/rond-authz/rond/internal/mocks" "github.com/rond-authz/rond/logging" rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/metrics" rondprometheus "github.com/rond-authz/rond/metrics/prometheus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" - "github.com/rond-authz/rond/types" + "github.com/rond-authz/rond/sdk/inputuser" "github.com/mia-platform/glogger/v4" "github.com/prometheus/client_golang/prometheus" @@ -238,7 +238,8 @@ func createContext( originalCtx context.Context, env config.EnvironmentVariables, evaluator sdk.Evaluator, - mongoClient *mocks.MongoClientMock, + inputUserClient *fake.InputUserClient, + logEntry *logrus.Entry, ) context.Context { t.Helper() @@ -247,11 +248,15 @@ func createContext( partialContext = sdk.WithEvaluator(partialContext, evaluator) - if mongoClient != nil { - partialContext = context.WithValue(partialContext, types.MongoClientContextKey{}, mongoClient) + if inputUserClient != nil { + partialContext = inputuser.AddClientInContext(partialContext, inputUserClient) } - partialContext = glogger.WithLogger(partialContext, logrus.NewEntry(logrus.New())) + logger := logEntry + if logger == nil { + logger = logrus.NewEntry(logrus.New()) + } + partialContext = glogger.WithLogger(partialContext, logger) return partialContext } @@ -272,8 +277,7 @@ func getEvaluator( t *testing.T, ctx context.Context, opaModule *core.OPAModuleConfig, - mongoClient *mocks.MongoClientMock, - rondConfig core.RondConfig, + mongoClient custom_builtins.IMongoClient, oas *openapi.OpenAPISpec, method, path string, options *evaluatorParams, @@ -296,7 +300,7 @@ func getEvaluator( } sdk, err := sdk.NewFromOAS(context.Background(), opaModule, oas, &sdk.Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ + EvaluatorOptions: &sdk.EvaluatorOptions{ MongoClient: mongoClient, }, Logger: logger, @@ -304,7 +308,7 @@ func getEvaluator( }) require.NoError(t, err, "unexpected error") - evaluator, err := sdk.FindEvaluator(logger, method, path) + evaluator, err := sdk.FindEvaluator(method, path) require.NoError(t, err) return evaluator @@ -333,13 +337,14 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/users/", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/users/", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -379,6 +384,7 @@ func TestSetupRoutesIntegration(t *testing.T) { config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/unknown/path?foo=bar", nil) @@ -404,13 +410,14 @@ func TestSetupRoutesIntegration(t *testing.T) { router := mux.NewRouter() setupRoutes(router, oas, envs) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, mockXPermission, oas, http.MethodGet, "/users/", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/users/", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/users/?foo=bar", nil) @@ -439,6 +446,7 @@ func TestSetupRoutesIntegration(t *testing.T) { config.EnvironmentVariables{LogLevel: "silent", TargetServiceHost: "targetServiceHostWillNotBeInvoked"}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/users/?foo=bar", nil) @@ -456,7 +464,6 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes the API not explicitly set in the oas file", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") - rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo"}} var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies @@ -475,12 +482,13 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas, http.MethodGet, "/foo/route-not-registered-explicitly", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/foo/route-not-registered-explicitly", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://my-service.com/foo/route-not-registered-explicitly", nil) @@ -499,7 +507,6 @@ func TestSetupRoutesIntegration(t *testing.T) { t.Run("invokes a specific API within a nested path", func(t *testing.T) { oas := prepareOASFromFile(t, "../mocks/nestedPathsConfig.json") - rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "foo"}} var mockOPAModule = &core.OPAModuleConfig{ Name: "example.rego", Content: `package policies @@ -518,12 +525,13 @@ func TestSetupRoutesIntegration(t *testing.T) { serverURL, _ := url.Parse(server.URL) - evaluator := getEvaluator(t, ctx, mockOPAModule, nil, rondConfig, oas, http.MethodGet, "/foo/bar/nested", nil) + evaluator := getEvaluator(t, ctx, mockOPAModule, nil, oas, http.MethodGet, "/foo/bar/nested", nil) ctx := createContext(t, context.Background(), config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, evaluator, nil, + nil, ) req, err := http.NewRequestWithContext(ctx, "GET", "http://crud-service/foo/bar/nested", nil) diff --git a/service/standalone_apis_test.go b/service/standalone_apis_test.go index 46edd28c..a8c08ebc 100644 --- a/service/standalone_apis_test.go +++ b/service/standalone_apis_test.go @@ -42,6 +42,7 @@ func TestRevokeHandler(t *testing.T) { config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, nil, + nil, ) t.Run("400 on missing subjects and groups", func(t *testing.T) { @@ -554,6 +555,7 @@ func TestGrantHandler(t *testing.T) { config.EnvironmentVariables{BindingsCrudServiceURL: "http://crud-service/bindings/"}, nil, nil, + nil, ) t.Run("400 on missing body fields", func(t *testing.T) { diff --git a/service/statusroutes_test.go b/service/statusroutes_test.go index 97f86537..ef3e9f69 100644 --- a/service/statusroutes_test.go +++ b/service/statusroutes_test.go @@ -24,8 +24,6 @@ import ( "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/mongoclient" - rondlogrus "github.com/rond-authz/rond/logging/logrus" "github.com/rond-authz/rond/openapi" "github.com/rond-authz/rond/sdk" @@ -110,14 +108,7 @@ test_policy { true } }, } - var mongoClient *mongoclient.MongoClient - logger, _ := test.NewNullLogger() - sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{ - EvaluatorOptions: &core.OPAEvaluatorOptions{ - MongoClient: mongoClient, - }, - Logger: rondlogrus.NewLogger(logger), - }) + sdk, err := sdk.NewFromOAS(context.Background(), opa, oas, &sdk.Options{}) require.NoError(t, err, "unexpected error") t.Run("non standalone", func(t *testing.T) { @@ -126,7 +117,7 @@ test_policy { true } TargetServiceHost: "my-service:4444", PathPrefixStandalone: "/my-prefix", } - router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) + router, err := SetupRouter(log, env, opa, oas, sdk, nil, nil) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { @@ -159,7 +150,7 @@ test_policy { true } PathPrefixStandalone: "/my-prefix", ServiceVersion: "latest", } - router, err := SetupRouter(log, env, opa, oas, sdk, mongoClient, nil) + router, err := SetupRouter(log, env, opa, oas, sdk, nil, nil) require.NoError(t, err, "unexpected error") t.Run("/-/rbac-ready", func(t *testing.T) { w := httptest.NewRecorder() diff --git a/types/rbactypes.go b/types/rbactypes.go index 69eb65bd..af2513b5 100644 --- a/types/rbactypes.go +++ b/types/rbactypes.go @@ -12,28 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// TODO: check if types should be removed from here, and set in correct packages package types -import ( - "context" -) - -type User struct { - UserID string - UserGroups []string - UserRoles []Role - UserBindings []Binding - Properties map[string]any -} - -type UserHeadersKeys struct { - GroupsHeaderKey string - IDHeaderKey string - PropertiesHeaderKey string -} - -type MongoClientContextKey struct{} - type Resource struct { ResourceType string `bson:"resourceType" json:"resourceType,omitempty"` ResourceID string `bson:"resourceId" json:"resourceId,omitempty"` @@ -65,23 +46,14 @@ type Role struct { Permissions []string `bson:"permissions" json:"permissions"` } -// MongoClientContextKey is the context key that shall be used to save -// mongo Collection reference in request contexts. -type IMongoClient interface { - Disconnect() error - - RetrieveUserBindings(ctx context.Context, user *User) ([]Binding, error) - RetrieveRoles(ctx context.Context) ([]Role, error) - RetrieveUserRolesByRolesID(ctx context.Context, userRolesId []string) ([]Role, error) - - // FindOne is only used inside the custom_builtins - FindOne(ctx context.Context, collectionName string, query map[string]interface{}) (interface{}, error) - // FindMany is only used inside the custom_builtins - FindMany(ctx context.Context, collectionName string, query map[string]interface{}) ([]interface{}, error) -} - type RequestError struct { Error string `json:"error"` Message string `json:"message"` StatusCode int `json:"statusCode"` } + +type User struct { + ID string + Groups []string + Properties map[string]any +} From 4019e2a0ebb3511526b3dae20831b2529684fde1 Mon Sep 17 00:00:00 2001 From: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:49:04 +0200 Subject: [PATCH 155/172] refactor(#229): use reverseproxy instead of director (#233) * refactor(#229): use reverseproxy instead of director * fix: version in quotes --- service/handler.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/service/handler.go b/service/handler.go index de91cbfb..8a3f9448 100644 --- a/service/handler.go +++ b/service/handler.go @@ -19,6 +19,7 @@ import ( "fmt" "net/http" "net/http/httputil" + "net/url" "strings" "github.com/rond-authz/rond/core" @@ -160,15 +161,19 @@ func ReverseProxy( inputUser core.InputUser, ) { targetHostFromEnv := env.TargetServiceHost + u, err := url.Parse(fmt.Sprintf("%s://%s", URL_SCHEME, targetHostFromEnv)) + if err != nil { + // FIXME: maybe better error handling? + // targetHostFromEnv should not arrive here if + // it's not a valid host to be put in a URL! + panic(err) + } + proxy := httputil.ReverseProxy{ FlushInterval: -1, - Director: func(req *http.Request) { - req.URL.Host = targetHostFromEnv - req.URL.Scheme = URL_SCHEME - if _, ok := req.Header["User-Agent"]; !ok { - // explicitly disable User-Agent so it's not set to default value - req.Header.Del("User-Agent") - } + Rewrite: func(r *httputil.ProxyRequest) { + r.SetURL(u) + r.SetXForwarded() }, } From f22cbbc3cb953f79a4465d63bbf8f0a8ed2f347f Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:43:40 +0200 Subject: [PATCH 156/172] feat: improve sdk request evaluator interface (#250) --- core/errors.go | 1 + core/opaevaluator.go | 2 +- core/partialevaluators_test.go | 4 ++-- internal/fake/sdk.go | 5 +++-- sdk/evaluator.go | 6 +++++- sdk/evaluator_test.go | 4 +--- service/handler.go | 5 +++++ service/router_test.go | 8 ++++++-- 8 files changed, 24 insertions(+), 11 deletions(-) diff --git a/core/errors.go b/core/errors.go index 403af248..ca13c492 100644 --- a/core/errors.go +++ b/core/errors.go @@ -27,6 +27,7 @@ var ( ErrPolicyEvalFailed = fmt.Errorf("policy evaluation failed") ErrPartialPolicyEvalFailed = fmt.Errorf("partial %w", ErrPolicyEvalFailed) ErrResponsePolicyEvalFailed = fmt.Errorf("response %w", ErrPolicyEvalFailed) + ErrPolicyNotAllowed = fmt.Errorf("policy not allowed") ErrFailedInputParse = fmt.Errorf("failed input parse") ErrFailedInputEncode = fmt.Errorf("failed input encode") diff --git a/core/opaevaluator.go b/core/opaevaluator.go index 7de43307..31c072e5 100644 --- a/core/opaevaluator.go +++ b/core/opaevaluator.go @@ -211,7 +211,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger logging.Logger, options *PolicyEv if allowed { return responseBodyOverwriter, nil } - return nil, ErrPolicyEvalFailed + return nil, ErrPolicyNotAllowed } func (evaluator *OPAEvaluator) getContext() context.Context { diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go index c03c6631..36d50761 100644 --- a/core/partialevaluators_test.go +++ b/core/partialevaluators_test.go @@ -108,7 +108,7 @@ func TestPartialResultEvaluators(t *testing.T) { evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(ctx, "column_policy", input, nil) require.NoError(t, err) _, err = evaluator.Evaluate(logger, nil) - require.EqualError(t, err, ErrPolicyEvalFailed.Error()) + require.EqualError(t, err, ErrPolicyNotAllowed.Error()) }) }) @@ -357,7 +357,7 @@ func TestPartialResultEvaluators(t *testing.T) { evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(context.Background(), "deny", input, nil) require.NoError(t, err) _, _, err = evaluator.PolicyEvaluation(logger, nil) - require.EqualError(t, err, ErrPolicyEvalFailed.Error()) + require.EqualError(t, err, ErrPolicyNotAllowed.Error()) }) }) } diff --git a/internal/fake/sdk.go b/internal/fake/sdk.go index e7e65056..3c24a2ac 100644 --- a/internal/fake/sdk.go +++ b/internal/fake/sdk.go @@ -22,7 +22,8 @@ import ( ) type RequestPolicyEvaluatorResult struct { - Err error + Err error + PolicyResult sdk.PolicyResult } type SDKEvaluator struct { @@ -49,7 +50,7 @@ func (s SDKEvaluator) EvaluateRequestPolicy(ctx context.Context, input core.Inpu if s.requestPolicyEvaluatorResult == nil { return sdk.PolicyResult{}, nil } - return sdk.PolicyResult{}, s.requestPolicyEvaluatorResult.Err + return s.requestPolicyEvaluatorResult.PolicyResult, s.requestPolicyEvaluatorResult.Err } func (e SDKEvaluator) EvaluateResponsePolicy(ctx context.Context, input core.Input, options *sdk.EvaluateOptions) ([]byte, error) { diff --git a/sdk/evaluator.go b/sdk/evaluator.go index 39be1efb..8dba5911 100644 --- a/sdk/evaluator.go +++ b/sdk/evaluator.go @@ -17,6 +17,7 @@ package sdk import ( "context" "encoding/json" + "errors" "github.com/rond-authz/rond/core" "github.com/rond-authz/rond/logging" @@ -76,7 +77,7 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, rondInput core.Inp EnableResourcePermissionsMapOptimization: rondConfig.Options.EnableResourcePermissionsMapOptimization, }) if err != nil { - return PolicyResult{}, nil + return PolicyResult{}, err } opaEvaluatorOptions := e.evaluatorOptions.opaEvaluatorOptions(logger) @@ -104,6 +105,9 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, rondInput core.Inp "policyName": rondConfig.RequestFlow.PolicyName, "message": err.Error(), }).Error("RBAC policy evaluation failed") + if errors.Is(err, core.ErrPolicyNotAllowed) { + return PolicyResult{}, nil + } return PolicyResult{}, err } diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index ec9fc71a..c71c56a7 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -79,7 +79,6 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, expectedPolicy: PolicyResult{}, - expectedErr: core.ErrPolicyEvalFailed, }, "not allowed policy result": { method: http.MethodGet, @@ -90,7 +89,6 @@ func TestEvaluateRequestPolicy(t *testing.T) { opaModuleContent: `package policies todo { false }`, expectedPolicy: PolicyResult{}, - expectedErr: core.ErrPolicyEvalFailed, }, "with empty filter query": { method: http.MethodGet, @@ -473,7 +471,7 @@ func TestEvaluateResponsePolicy(t *testing.T) { false body := input.response.body }`, - expectedErr: core.ErrPolicyEvalFailed, + expectedErr: core.ErrPolicyNotAllowed, expectedBody: "", notAllowed: true, }, diff --git a/service/handler.go b/service/handler.go index 8a3f9448..bd464000 100644 --- a/service/handler.go +++ b/service/handler.go @@ -139,6 +139,11 @@ func EvaluateRequest( utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) return err } + if !result.Allowed { + logger.Error("RBAC policy evaluation failed") + utils.FailResponseWithCode(w, http.StatusForbidden, "RBAC policy evaluation failed", utils.NO_PERMISSIONS_ERROR_MESSAGE) + return fmt.Errorf("RBAC policy evaluation failed") + } queryHeaderKey := BASE_ROW_FILTER_HEADER_KEY if permission.RequestFlow.QueryOptions.HeaderName != "" { diff --git a/service/router_test.go b/service/router_test.go index ece7bcb2..c342aa04 100644 --- a/service/router_test.go +++ b/service/router_test.go @@ -361,7 +361,7 @@ func TestSetupRoutesIntegration(t *testing.T) { require.Equal(t, http.StatusOK, w.Result().StatusCode) }) - t.Run("invokes unknown API", func(t *testing.T) { + t.Run("invokes unknown API - with mocked true evaluator", func(t *testing.T) { var invoked bool server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { invoked = true @@ -377,7 +377,11 @@ func TestSetupRoutesIntegration(t *testing.T) { eval, err := openapi.SetupEvaluators(ctx, logger, oas, mockOPAModule, nil) require.NoError(t, err) - evaluator := fake.NewSDKEvaluator(eval, mockXPermission, nil) + evaluator := fake.NewSDKEvaluator(eval, mockXPermission, &fake.RequestPolicyEvaluatorResult{ + PolicyResult: sdk.PolicyResult{ + Allowed: true, + }, + }) ctx := createContext(t, context.Background(), From 6605196c282d127e88972c39a3fe0d3066834d9c Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Thu, 31 Aug 2023 12:02:02 +0200 Subject: [PATCH 157/172] fix: send empty filter header (#251) * fix: send empty filter header * improve test message and add comment * fix * update comment * improve test --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- sdk/evaluator.go | 2 +- sdk/evaluator_test.go | 24 ++++------ sdk/integration_test.go | 12 ++--- sdk/sdk_test.go | 12 ++--- service/handler.go | 8 +++- service/handler_test.go | 100 +++++++++++++++++++++++++++++----------- 6 files changed, 98 insertions(+), 60 deletions(-) diff --git a/sdk/evaluator.go b/sdk/evaluator.go index 8dba5911..6cc9fc2a 100644 --- a/sdk/evaluator.go +++ b/sdk/evaluator.go @@ -111,7 +111,7 @@ func (e evaluator) EvaluateRequestPolicy(ctx context.Context, rondInput core.Inp return PolicyResult{}, err } - var queryToProxy = []byte{} + var queryToProxy []byte if query != nil { queryToProxy, err = json.Marshal(query) if err != nil { diff --git a/sdk/evaluator_test.go b/sdk/evaluator_test.go index c71c56a7..a3fa2077 100644 --- a/sdk/evaluator_test.go +++ b/sdk/evaluator_test.go @@ -55,8 +55,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { path: "/users/", expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte{}, + Allowed: true, }, }, "with user with policy true": { @@ -67,8 +66,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte{}, + Allowed: true, }, }, "not allow if not existing policy": { @@ -109,8 +107,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { query := data.resources[_] }`, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, }, "with filter query": { @@ -165,8 +162,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { input.user.properties.prop1 == "my-user-field" }`, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, }, "with mongo client and find_one": { @@ -189,8 +185,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { }, }, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte{}, + Allowed: true, }, }, "with mongo client and find_one with dynamic find_one query": { @@ -218,8 +213,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { } `, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte{}, + Allowed: true, }, }, "with mongo client and find_many": { @@ -244,8 +238,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { } `, expectedPolicy: PolicyResult{ - Allowed: true, - QueryToProxy: []byte{}, + Allowed: true, }, }, "with query and mongo client": { @@ -408,8 +401,7 @@ func TestEvaluateRequestPolicy(t *testing.T) { result, err := sdk.EvaluateRequestPolicy(context.Background(), core.Input{}, nil) require.NoError(t, err) require.Equal(t, PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, result) }) } diff --git a/sdk/integration_test.go b/sdk/integration_test.go index df90f8d3..8ca60ec3 100644 --- a/sdk/integration_test.go +++ b/sdk/integration_test.go @@ -47,8 +47,7 @@ func TestUsageNewWithConfig(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, input, nil) require.NoError(t, err) require.Equal(t, result, sdk.PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }) } @@ -76,8 +75,7 @@ func TestUsageNewWithConfigWithMongo(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, input, nil) require.NoError(t, err) require.Equal(t, result, sdk.PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }) } @@ -102,8 +100,7 @@ func TestUsageNewFromOas(t *testing.T) { }) require.NoError(t, err) require.Equal(t, result, sdk.PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }) } @@ -132,7 +129,6 @@ func TestUsageNewFromOasWithMongo(t *testing.T) { }) require.NoError(t, err) require.Equal(t, result, sdk.PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }) } diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 0ed460b5..0325e97b 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -168,8 +168,7 @@ func TestNewWithConfig(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, result) }) }) @@ -183,8 +182,7 @@ func TestNewWithConfig(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, result) }) }) @@ -214,8 +212,7 @@ func TestNewWithConfig(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, result) }) }) @@ -252,8 +249,7 @@ func TestNewWithConfig(t *testing.T) { result, err := evaluator.EvaluateRequestPolicy(ctx, getFakeInput(t, core.InputRequest{}, "", core.InputUser{}, nil), nil) require.NoError(t, err) require.Equal(t, PolicyResult{ - Allowed: true, - QueryToProxy: []byte(""), + Allowed: true, }, result) }) }) diff --git a/service/handler.go b/service/handler.go index bd464000..b66805bf 100644 --- a/service/handler.go +++ b/service/handler.go @@ -123,6 +123,13 @@ func EvaluateRequest( Logger: rondlogrus.NewEntry(logger), }) if err != nil { + // opatranslator.ErrEmptyQuery throws when evaluator should return a query. In case + // there is an error returning a query, and the content type is `application/json`, + // it is a list and so returns 200 with an empty list (as if all elements would be filtered out). + // + // TODO: we needs to check content-type or the accept header? Or set some configuration, + // such as `defaultResponseBodyOnEmptyQuery`, so that it does not depends on the client. + // Because the content-type in the request refers to the request body, not the response body. if errors.Is(err, opatranslator.ErrEmptyQuery) && utils.HasApplicationJSONContentType(req.Header) { w.Header().Set(utils.ContentTypeHeaderKey, utils.JSONContentTypeHeader) w.WriteHeader(http.StatusOK) @@ -149,7 +156,6 @@ func EvaluateRequest( if permission.RequestFlow.QueryOptions.HeaderName != "" { queryHeaderKey = permission.RequestFlow.QueryOptions.HeaderName } - // FIXME: header is always set, also if query to proxy is empty if result.QueryToProxy != nil { req.Header.Set(queryHeaderKey, string(result.QueryToProxy)) } diff --git a/service/handler_test.go b/service/handler_test.go index 0358f21a..a9b1f5e4 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -361,7 +361,7 @@ allow { require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") }) - t.Run("sends empty filter query", func(t *testing.T) { + t.Run("not sends empty filter query", func(t *testing.T) { policy := `package policies allow { get_header("examplekey", input.headers) == "value" @@ -390,9 +390,8 @@ allow { buf, err := io.ReadAll(r.Body) require.NoError(t, err, "Mocked backend: Unexpected error") require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") - filterQuery := r.Header.Get("rowfilterquery") - expectedQuery := `` - require.Equal(t, expectedQuery, filterQuery) + _, ok := r.Header[http.CanonicalHeaderKey("rowfilterquery")] + require.False(t, ok) w.WriteHeader(http.StatusOK) w.Write([]byte("Mocked Backend Body Example")) })) @@ -546,12 +545,10 @@ allow { }) }) - t.Run("sends empty filter query with application-json as content-type", func(t *testing.T) { + t.Run("when policy with query creation, if evaluate empty query with application/json as content-type returns empty array", func(t *testing.T) { policy := `package policies allow { false - employee := data.resources[_] - employee.name == "name_test" } ` @@ -591,20 +588,17 @@ allow { require.Equal(t, "[]", string(buf), "Unexpected body response") }) - t.Run("sends empty filter query with text/plain as content-type", func(t *testing.T) { + t.Run("403 when policy with query creation, if evaluate empty query with text/plain as content-type", func(t *testing.T) { policy := `package policies allow { false - employee := data.resources[_] - employee.name == "name_test" } ` - invoked := false mockBodySting := "I am a body" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - invoked = true + t.Fail() })) defer server.Close() @@ -630,7 +624,6 @@ allow { rbacHandler(w, r) - require.True(t, !invoked, "Handler was not invoked.") require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") }) @@ -658,20 +651,10 @@ allow { } ` - invoked := false mockBodySting := "I am a body" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - invoked = true - defer r.Body.Close() - buf, err := io.ReadAll(r.Body) - require.NoError(t, err, "Mocked backend: Unexpected error") - require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") - filterQuery := r.Header.Get("rowfilterquery") - expectedQuery := `` - require.Equal(t, expectedQuery, filterQuery) - w.WriteHeader(http.StatusOK) - w.Write([]byte("Mocked Backend Body Example")) + t.Fail() })) defer server.Close() @@ -699,7 +682,6 @@ allow { rbacHandler(w, r) - require.True(t, !invoked, "Handler was not invoked.") require.Equal(t, http.StatusForbidden, w.Result().StatusCode, "Unexpected status code.") require.Equal(t, utils.JSONContentTypeHeader, w.Result().Header.Get(utils.ContentTypeHeaderKey), "Unexpected content type.") }) @@ -847,6 +829,73 @@ allow { }) }) }) + + t.Run("not send filter query if empty - config without query generation", func(t *testing.T) { + policy := `package policies +todo { + get_header("examplekey", input.headers) == "value" + input.request.method == "GET" + employee := data.resources[_] +} + +todo { + input.request.method == "GET" + + employee := data.resources[_] +} + +todo { + input.request.method == "GET" + input.request.path == "/api" +} +` + + invoked := false + mockBodySting := "I am a body" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + invoked = true + defer r.Body.Close() + buf, err := io.ReadAll(r.Body) + require.NoError(t, err, "Mocked backend: Unexpected error") + require.Equal(t, mockBodySting, string(buf), "Mocked backend: Unexpected Body received") + _, ok := r.Header[http.CanonicalHeaderKey(BASE_ROW_FILTER_HEADER_KEY)] + require.False(t, ok) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Mocked Backend Body Example")) + })) + defer server.Close() + + body := strings.NewReader(mockBodySting) + + serverURL, _ := url.Parse(server.URL) + + OPAModuleConfig := &core.OPAModuleConfig{Name: "mypolicy.rego", Content: policy} + + evaluator := getEvaluator(t, ctx, OPAModuleConfig, nil, oas, http.MethodGet, "/api", nil) + ctx := createContext(t, + context.Background(), + config.EnvironmentVariables{TargetServiceHost: serverURL.Host}, + evaluator, + nil, + nil, + ) + + r, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com:8080/api", body) + require.NoError(t, err, "Unexpected error") + r.Header.Set("miauserproperties", `{"name":"gianni"}`) + r.Header.Set("examplekey", "value") + r.Header.Set(utils.ContentTypeHeaderKey, "text/plain") + w := httptest.NewRecorder() + + rbacHandler(w, r) + + require.True(t, invoked, "Handler was not invoked.") + require.Equal(t, http.StatusOK, w.Result().StatusCode, "Unexpected status code.") + buf, err := io.ReadAll(w.Body) + require.NoError(t, err, "Unexpected error to read body response") + require.Equal(t, "Mocked Backend Body Example", string(buf), "Unexpected body response") + }) } func TestStandaloneMode(t *testing.T) { @@ -2260,5 +2309,4 @@ func TestGetInputUser(t *testing.T) { Roles: roles, }, inputUser) }) - } From f5edce051de6e4b3a7d03de502cb86ebdc9a3973 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:59:39 +0200 Subject: [PATCH 158/172] chore(deps): bump github.com/open-policy-agent/opa from 0.55.0 to 0.56.0 (#253) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.55.0 to 0.56.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.55.0...v0.56.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 9 +++++---- go.sum | 26 +++++++++++++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 813cd9af..bb390f5d 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mia-platform/configlib v1.0.0 github.com/mia-platform/glogger/v4 v4.0.0 github.com/mia-platform/go-crud-service-client v0.11.0 - github.com/open-policy-agent/opa v0.55.0 + github.com/open-policy-agent/opa v0.56.0 github.com/prometheus/client_golang v1.16.0 github.com/samber/lo v1.38.1 github.com/sirupsen/logrus v1.9.3 @@ -80,13 +80,14 @@ require ( go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/sdk v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect - golang.org/x/crypto v0.11.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index be72a9c0..df9ce2a9 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/open-policy-agent/opa v0.55.0 h1:s7Vm4ph6zDqqP/KzvUSw9fsKVsm9lhbTZhYGxxTK7mo= -github.com/open-policy-agent/opa v0.55.0/go.mod h1:2Vh8fj/bXCqSwGMbBiHGrw+O8yrho6T/fdaHt5ROmaQ= +github.com/open-policy-agent/opa v0.56.0 h1:FUSb6MyckjuffOMshEG8P+HGnckxkJ8ENZJHEzAddhk= +github.com/open-policy-agent/opa v0.56.0/go.mod h1:un01L10fkolr00KJMDSqGb2FXCjVyVQOybLtHOfSEfY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -571,8 +571,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 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= @@ -651,7 +651,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= 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= @@ -738,8 +738,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -754,8 +754,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -881,7 +881,9 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -903,7 +905,7 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= 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= @@ -959,3 +961,5 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 2f3f5b91e683f18c12d92833d497445ceb2b19d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 15:31:22 +0200 Subject: [PATCH 159/172] chore(deps): bump github.com/getkin/kin-openapi from 0.118.0 to 0.119.0 (#252) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.118.0 to 0.119.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.118.0...v0.119.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 24 +++++++++++------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index bb390f5d..b1bfd1ac 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/davidebianchi/gswagger v0.9.0 - github.com/getkin/kin-openapi v0.118.0 + github.com/getkin/kin-openapi v0.119.0 github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 @@ -32,8 +32,8 @@ require ( github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index df9ce2a9..fa593f72 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -126,8 +127,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.119.0 h1:FIn6hYX3huTFV28GS1hotceX8MAsGGf/c+jFta5XRaE= +github.com/getkin/kin-openapi v0.119.0/go.mod h1:Kc71dQTNCxy/6sBdhR5dbKoSHwzLCcgygpCzizUy4XA= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -147,15 +148,14 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -289,7 +289,6 @@ github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6 github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -322,15 +321,15 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -494,11 +493,9 @@ github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/Tp4c= github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -926,6 +923,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= From ad20d7439513c778be2581becdf70cefca9c6f30 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:58:34 +0200 Subject: [PATCH 160/172] add custom metadata to rond input (#254) * feat: add custom metadata to rond input * Apply suggestions from code review --------- Co-authored-by: Federico Maggi <7142570+fredmaggiowski@users.noreply.github.com> --- core/input.go | 9 ++++---- core/partialevaluators_test.go | 40 ++++++++++++++++++++++++++++++++++ sdk/sdk_test.go | 27 +++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/core/input.go b/core/input.go index dc1ecce0..6267449e 100644 --- a/core/input.go +++ b/core/input.go @@ -26,10 +26,11 @@ import ( ) type Input struct { - Request InputRequest `json:"request"` - Response InputResponse `json:"response"` - ClientType string `json:"clientType,omitempty"` - User InputUser `json:"user"` + Request InputRequest `json:"request"` + Response InputResponse `json:"response"` + ClientType string `json:"clientType,omitempty"` + User InputUser `json:"user"` + CustomMetadata map[string]any `json:"metadata,omitempty"` } type InputRequest struct { diff --git a/core/partialevaluators_test.go b/core/partialevaluators_test.go index 36d50761..0d9b97f2 100644 --- a/core/partialevaluators_test.go +++ b/core/partialevaluators_test.go @@ -360,4 +360,44 @@ func TestPartialResultEvaluators(t *testing.T) { require.EqualError(t, err, ErrPolicyNotAllowed.Error()) }) }) + + t.Run("evaluate policy with custom metadata", func(t *testing.T) { + partialEvaluators := PartialResultsEvaluators{} + rondConfig := &RondConfig{ + RequestFlow: RequestFlow{ + PolicyName: "check_metadata", + }, + } + + evalOpts := OPAEvaluatorOptions{ + Logger: logger, + } + + opaModule := &OPAModuleConfig{ + Content: `package policies + check_metadata { + input.metadata.field == "ok" + }`, + Name: "policies", + } + + err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts) + require.NoError(t, err) + require.NotNil(t, partialEvaluators["allow"]) + + rondInput := Input{ + CustomMetadata: map[string]any{ + "field": "ok", + }, + } + + input, err := CreateRegoQueryInput(logger, rondInput, RegoInputOptions{}) + require.NoError(t, err) + evaluator, err := partialEvaluators.GetEvaluatorFromPolicy(context.Background(), "check_metadata", input, &evalOpts) + require.NoError(t, err) + res, query, err := evaluator.PolicyEvaluation(logger, nil) + require.NoError(t, err) + require.Empty(t, query) + require.Empty(t, res) + }) } diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 0325e97b..5c2adbbe 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -253,6 +253,33 @@ func TestNewWithConfig(t *testing.T) { }, result) }) }) + + t.Run("with custom metadata", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + check_metadata { + input.metadata.field1 == "pass" + } + `, + } + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "check_metadata"}} + + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + + res, err := evaluator.EvaluateRequestPolicy(context.Background(), core.Input{ + CustomMetadata: map[string]any{ + "field1": "pass", + }, + }, &EvaluateOptions{Logger: logger}) + + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + }, res) + }) } type sdkOptions struct { From b4c94a2d3cdb51fbeb97b21e0b76637b341fa1f5 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:29:58 +0200 Subject: [PATCH 161/172] Custom metadata accepts any (#255) * feat: custom metadata accepts any * add test example --- core/input.go | 10 ++++---- sdk/sdk_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/core/input.go b/core/input.go index 6267449e..7918d685 100644 --- a/core/input.go +++ b/core/input.go @@ -26,11 +26,11 @@ import ( ) type Input struct { - Request InputRequest `json:"request"` - Response InputResponse `json:"response"` - ClientType string `json:"clientType,omitempty"` - User InputUser `json:"user"` - CustomMetadata map[string]any `json:"metadata,omitempty"` + Request InputRequest `json:"request"` + Response InputResponse `json:"response"` + ClientType string `json:"clientType,omitempty"` + User InputUser `json:"user"` + CustomMetadata any `json:"metadata,omitempty"` } type InputRequest struct { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 5c2adbbe..479ddeda 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -280,6 +280,67 @@ func TestNewWithConfig(t *testing.T) { Allowed: true, }, res) }) + + t.Run("with custom metadata as struct", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + check_metadata { + input.metadata.field1 == "pass" + input.metadata.nested.foo == "bar" + } + `, + } + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "check_metadata"}} + + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + + type Nested struct { + Foo string `json:"foo"` + } + type Metadata struct { + Field string `json:"field1"` + Nested Nested `json:"nested"` + } + + res, err := evaluator.EvaluateRequestPolicy(context.Background(), core.Input{ + CustomMetadata: Metadata{Field: "pass", Nested: Nested{Foo: "bar"}}, + }, &EvaluateOptions{Logger: logger}) + + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + }, res) + }) + + t.Run("with custom metadata as slice", func(t *testing.T) { + opaModule := &core.OPAModuleConfig{ + Name: "example.rego", + Content: `package policies + check_metadata { + input.metadata[0] == "pass" + } + `, + } + rondConfig := core.RondConfig{RequestFlow: core.RequestFlow{PolicyName: "check_metadata"}} + + evaluator, err := NewWithConfig(ctx, opaModule, rondConfig, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + + type Metadata []string + + res, err := evaluator.EvaluateRequestPolicy(context.Background(), core.Input{ + CustomMetadata: Metadata{"pass"}, + }, &EvaluateOptions{Logger: logger}) + + require.NoError(t, err) + require.Equal(t, PolicyResult{ + Allowed: true, + }, res) + }) } type sdkOptions struct { From 13883540e999f928d08375ae741f7fd7f29b747d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:11:00 +0200 Subject: [PATCH 162/172] chore(deps): bump actions/checkout from 3 to 4 (#256) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/security.yml | 2 +- .github/workflows/test.yml | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e5bfebdc..e92f4148 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a703861..436584dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Release uses: softprops/action-gh-release@v1 with: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 8a792606..f250a844 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run Gosec Security Scanner uses: securego/gosec@master with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 082f6c64..d3e5f26d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use golang ${{ matrix.go_version }} uses: actions/setup-go@v4 with: @@ -37,7 +37,7 @@ jobs: runs-on: ${{ matrix.os }} continue-on-error: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use golang ${{ matrix.go_version }} uses: actions/setup-go@v4 with: @@ -61,7 +61,7 @@ jobs: go_version: ['1.20'] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go_version }} @@ -106,7 +106,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure docker metadata id: meta From b6f1326946ae7cf164e4aee39d72c1a06f2aa0c5 Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Thu, 7 Sep 2023 11:14:42 +0200 Subject: [PATCH 163/172] v1.10.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 03f02ca4..a406fd66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ LABEL maintainer="rond@rond-authz.io" \ LABEL org.opencontainers.image.description "Rönd is a lightweight container that distributes security policy enforcement throughout your application." -ENV SERVICE_VERSION="1.9.0" +ENV SERVICE_VERSION="1.10.0" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From ab1f78cdd004b61779783e705269374d92ac2bb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:54:32 +0200 Subject: [PATCH 164/172] chore(deps): bump golang from 1.21.0 to 1.21.1 (#257) Bumps golang from 1.21.0 to 1.21.1. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a406fd66..c24e6ca9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################ # STEP 1 build executable binary ############################ -FROM golang:1.21.0 AS builder +FROM golang:1.21.1 AS builder WORKDIR /app From 1d6135e5d72ed575f3f848685d26b38b93cd6df1 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:43:04 +0200 Subject: [PATCH 165/172] upgrade glogrus (#258) --- go.mod | 4 ++-- go.sum | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index b1bfd1ac..ca8d9365 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 - github.com/mia-platform/glogger/v4 v4.0.0 + github.com/mia-platform/glogger/v4 v4.0.1 github.com/mia-platform/go-crud-service-client v0.11.0 github.com/open-policy-agent/opa v0.56.0 github.com/prometheus/client_golang v1.16.0 @@ -42,7 +42,7 @@ require ( github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/knadh/koanf v1.4.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index fa593f72..a21f5215 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,7 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -156,6 +157,7 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -302,6 +304,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -312,6 +315,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/knadh/koanf v0.6.0/go.mod h1:HGb//qrjEExYwYqqoxit9S1rvFU+YCx1jz3GtcdZEy8= github.com/knadh/koanf v1.4.3 h1:rSJcSH5LSFhvzBRsAYfT3k7eLP0I4UxeZqjtAatk+wc= github.com/knadh/koanf v1.4.3/go.mod h1:5FAkuykKXZvLqhAbP4peWgM5CTcZmn7L1d27k/a+kfg= @@ -347,6 +352,8 @@ github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjK github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= github.com/mia-platform/glogger/v4 v4.0.0 h1:vap3r+0RRHsZkefVqanBz6H381FiD3rtg/j+xuNcRac= github.com/mia-platform/glogger/v4 v4.0.0/go.mod h1:TqVXSfa4wuAaLoe0ye8D6hsPksClgs7p3Y1dC2oRxoQ= +github.com/mia-platform/glogger/v4 v4.0.1 h1:bxWUG+DYvHGGLYfISkm18XfxphEeMTHuxdmK+S7o/+M= +github.com/mia-platform/glogger/v4 v4.0.1/go.mod h1:Sr1Ydsal05PRoEehM+mr7pcBD5tmGD35uCTnGytGMPw= github.com/mia-platform/go-crud-service-client v0.11.0 h1:6/4OFe2N2z2gQuVrAocTrKBPgttb244f74JzZ1ZNz4g= github.com/mia-platform/go-crud-service-client v0.11.0/go.mod h1:Sj3BEGMu0RTdeye/fPBVwRoojOjKuhWl5j+psY3s40s= github.com/mia-platform/jsonschema v0.1.0 h1:tjQf7TaYROsAqk7SXTL+44TrfKk3bSEvhRGPS51IA5Y= @@ -374,6 +381,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -496,6 +504,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/Tp4c= github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= From 90af1fd4cee91f15a5a1da9c85350b638c127e5c Mon Sep 17 00:00:00 2001 From: Federico Maggi Date: Thu, 7 Sep 2023 17:43:45 +0200 Subject: [PATCH 166/172] v1.10.1 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c24e6ca9..821025f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ LABEL maintainer="rond@rond-authz.io" \ LABEL org.opencontainers.image.description "Rönd is a lightweight container that distributes security policy enforcement throughout your application." -ENV SERVICE_VERSION="1.10.0" +ENV SERVICE_VERSION="1.10.1" # Import the user and group files from the builder. COPY --from=builder /etc/passwd /etc/passwd From f7ca4f1ab09b2180f4a839f0604bb63b7ccfc04d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:22:19 +0200 Subject: [PATCH 167/172] chore(deps): bump github.com/getkin/kin-openapi from 0.119.0 to 0.120.0 (#259) Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.119.0 to 0.120.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.119.0...v0.120.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 17 ++++------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index ca8d9365..ee5416fa 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/davidebianchi/gswagger v0.9.0 - github.com/getkin/kin-openapi v0.119.0 + github.com/getkin/kin-openapi v0.120.0 github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 github.com/mia-platform/configlib v1.0.0 @@ -55,7 +55,7 @@ require ( github.com/montanaflynn/stats v0.6.6 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect diff --git a/go.sum b/go.sum index a21f5215..9c20f3b3 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.119.0 h1:FIn6hYX3huTFV28GS1hotceX8MAsGGf/c+jFta5XRaE= -github.com/getkin/kin-openapi v0.119.0/go.mod h1:Kc71dQTNCxy/6sBdhR5dbKoSHwzLCcgygpCzizUy4XA= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -143,7 +143,6 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -157,7 +156,6 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -304,7 +302,6 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -313,8 +310,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/knadh/koanf v0.6.0/go.mod h1:HGb//qrjEExYwYqqoxit9S1rvFU+YCx1jz3GtcdZEy8= @@ -350,8 +345,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mia-platform/configlib v1.0.0 h1:8sh40jZlCxrtGBq87nbjKa4zisgccuXxBrjKD5OWJ1s= github.com/mia-platform/configlib v1.0.0/go.mod h1:oyELirRsp1AzPaSF7GzcMe/R3Rf7uRvw3FclA9Q4fFQ= -github.com/mia-platform/glogger/v4 v4.0.0 h1:vap3r+0RRHsZkefVqanBz6H381FiD3rtg/j+xuNcRac= -github.com/mia-platform/glogger/v4 v4.0.0/go.mod h1:TqVXSfa4wuAaLoe0ye8D6hsPksClgs7p3Y1dC2oRxoQ= github.com/mia-platform/glogger/v4 v4.0.1 h1:bxWUG+DYvHGGLYfISkm18XfxphEeMTHuxdmK+S7o/+M= github.com/mia-platform/glogger/v4 v4.0.1/go.mod h1:Sr1Ydsal05PRoEehM+mr7pcBD5tmGD35uCTnGytGMPw= github.com/mia-platform/go-crud-service-client v0.11.0 h1:6/4OFe2N2z2gQuVrAocTrKBPgttb244f74JzZ1ZNz4g= @@ -381,7 +374,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -405,8 +397,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= 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= @@ -504,7 +496,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/Tp4c= github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= From 630793c3b470eaf9f7a668162bd15850a31b91ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:38:13 +0200 Subject: [PATCH 168/172] chore(deps): bump docker/metadata-action from 4 to 5 (#264) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d3e5f26d..dea6b088 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,7 +110,7 @@ jobs: - name: Configure docker metadata id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: | ghcr.io/rond-authz/rond From 2c3f9807a3b1feec26776e06f03330154bab780b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:38:37 +0200 Subject: [PATCH 169/172] chore(deps): bump docker/login-action from 2 to 3 (#263) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dea6b088..92473929 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -131,14 +131,14 @@ jobs: uses: docker/setup-qemu-action@v2 - name: Docker Login to ghcr.io - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Docker Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.BOT_DOCKER_USERNAME }} password: ${{ secrets.BOT_DOCKER_TOKEN }} From 23c5dfb02571717e14f4790255e1a8538d73f6ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:39:27 +0200 Subject: [PATCH 170/172] chore(deps): bump docker/setup-buildx-action from 2 to 3 (#262) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92473929..8f33373d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -125,7 +125,7 @@ jobs: org.opencontainers.image.vendor=rond authz - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v2 From bb9ed365b905082cdc1fa9b07ea2d646db77082c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:39:39 +0200 Subject: [PATCH 171/172] chore(deps): bump docker/build-push-action from 4 to 5 (#261) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f33373d..ff064bb6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -152,7 +152,7 @@ jobs: ${{ runner.os }}-buildx- - name: Build and push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 From b28d54b6902d8aeef6728b933ed9c15c83bba47a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:47:48 +0200 Subject: [PATCH 172/172] chore(deps): bump docker/setup-qemu-action from 2 to 3 (#260) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ff064bb6..45387129 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -128,7 +128,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Docker Login to ghcr.io uses: docker/login-action@v3