diff --git a/README.md b/README.md index 0eee1a63bcd04015519165af84c263d72d261a9d..726d353b326938a2808893b17857fb689b1af406 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 5c179f2bb045d6c3db4e04ae2ebe80e8719cc836..57e1842dcbe8c805bdd9cf8d88f1d5f4e54e8b0a 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 6f4e1ce3413aec9a59c16a4073552c858155694f..d87e84ef2cad3b82ad166c2e62934d0aa9058f62 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 e76c0b6aa8f993255eb1eace345d6ef640f7a413..a881198f52ead1cb2cf489da7136e4007cfeb0f2 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 1185c2caa4dabd112d757f1e7643b59649021bce..40ef11257fd3cda407e08315bd3a743cc8d16eb3 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"}, }, }, {