From e194f6556ee5ba1cdc603996c8246bdb2c687f5b Mon Sep 17 00:00:00 2001 From: Yordan Kinkov <yordan.kinkov@vereign.com> Date: Fri, 21 Oct 2022 17:24:22 +0300 Subject: [PATCH] Return string when accessing a header in Rego --- README.md | 21 ++++++++++++++++++++- internal/header/header.go | 14 +++++++++++--- internal/header/header_test.go | 4 ++-- internal/service/policy/service.go | 2 +- internal/service/policy/service_test.go | 4 ++-- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0eee1a63..726d353b 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,26 @@ endpoints for working with arbitrary dynamically uploaded policies. ### Access HTTP Headers inside a policy HTTP Request Headers are passed to the evaluation runtime on each request. One could access any header by name within -the Rego source code using `input.header.name` or `input.header["name"]`. +the Rego source code using `input.headers.Name` or `input.headers["Name"]`. +##### **Important** +Header names are passed to the Rego runtime in a canonical format. This means that the first character and any characters following a +hyphen are uppercase and the rest are lowercase. + +Example: +The policy service receives a request with these headers: +``` +accept-encoding: gzip, deflate +Accept-Language: en-us +fOO: Bar +x-loCATion: Baz +``` +Inside a policy these headers could be accessed as follows: +``` +input.headers["Accept-Encoding"] +input.headers["Accept-Language"] +input.headers["Foo"] +input.headers["X-Location"] +``` ### Policy Extensions Functions diff --git a/internal/header/header.go b/internal/header/header.go index 5c179f2b..57e1842d 100644 --- a/internal/header/header.go +++ b/internal/header/header.go @@ -23,10 +23,18 @@ func Middleware() func(http.Handler) http.Handler { } func ToContext(ctx context.Context, r *http.Request) context.Context { - return context.WithValue(ctx, headerKey, r.Header) + headers := make(map[string]string, len(r.Header)+1) + for name := range r.Header { + headers[name] = r.Header.Get(name) + } + + // add Host header to headers map + headers["Host"] = r.Host + + return context.WithValue(ctx, headerKey, headers) } -func FromContext(ctx context.Context) (http.Header, bool) { - header, ok := ctx.Value(headerKey).(http.Header) +func FromContext(ctx context.Context) (map[string]string, bool) { + header, ok := ctx.Value(headerKey).(map[string]string) return header, ok } diff --git a/internal/header/header_test.go b/internal/header/header_test.go index 6f4e1ce3..d87e84ef 100644 --- a/internal/header/header_test.go +++ b/internal/header/header_test.go @@ -10,10 +10,10 @@ import ( ) func TestMiddleware(t *testing.T) { - expected := http.Header{"Authorization": []string{"my-token"}} + expected := map[string]string{"Authorization": "my-token", "Host": "example.com"} req := httptest.NewRequest("POST", "/example", nil) - req.Header = expected + req.Header = http.Header{"Authorization": []string{"my-token"}} nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { value, ok := header.FromContext(r.Context()) diff --git a/internal/service/policy/service.go b/internal/service/policy/service.go index e76c0b6a..a881198f 100644 --- a/internal/service/policy/service.go +++ b/internal/service/policy/service.go @@ -21,7 +21,7 @@ import ( //go:generate counterfeiter . Storage //go:generate counterfeiter . RegoCache -const HeaderKey = "header" +const HeaderKey = "headers" type Cache interface { Set(ctx context.Context, key, namespace, scope string, value []byte, ttl int) error diff --git a/internal/service/policy/service_test.go b/internal/service/policy/service_test.go index 1185c2ca..40ef1125 100644 --- a/internal/service/policy/service_test.go +++ b/internal/service/policy/service_test.go @@ -37,7 +37,7 @@ func TestService_Evaluate(t *testing.T) { testPolicyWithStaticData := `package testgroup.example default allow = false allow { data.msg == "hello world" }` // prepare test policy accessing headers during evaluation - testPolicyAccessingHeaders := `package testgroup.example token := input.header["Authorization"]` + testPolicyAccessingHeaders := `package testgroup.example token := input.headers["Authorization"]` // prepare test query that can be retrieved from rego queryCache testQuery, err := rego.New( @@ -347,7 +347,7 @@ func TestService_Evaluate(t *testing.T) { }, }, res: &goapolicy.EvaluateResult{ - Result: map[string]interface{}{"token": []interface{}{"my-token"}}, + Result: map[string]interface{}{"token": "my-token"}, }, }, { -- GitLab