Skip to content

Commit

Permalink
Doc reference changed & using type assertion for performance issue
Browse files Browse the repository at this point in the history
  • Loading branch information
jthann committed Nov 21, 2022
1 parent 13235fc commit 73dbbab
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 56 deletions.
20 changes: 10 additions & 10 deletions doc/reference/filters.md
Expand Up @@ -1066,9 +1066,9 @@ After OIDCAdaptor handled, following OIDC related information can be obtained fr
## OPAFilter
The [Open Policy Agent (OPA)](https://www.openpolicyagent.org/docs/latest/) is an open source,
general-purpose policy engine that unifies policy enforcement across the stack. It provides a
high-level declarative language, we can specify policy as code to enforce policies in
Easegress API Gateway. There are 185 builtin operators including functions we can use
like examples `net.cidr_contains` and `contains`.
high-level declarative language, which can be used to define and enforce policies in
Easegress API Gateway. Currently, there are 160+ built-in operators and functions we can use,
for examples `net.cidr_contains` and `contains`.

```yaml
name: demo-pipeline
Expand All @@ -1093,7 +1093,7 @@ filters:
}
```

The following table input request fields can be used in OPA policy to help make policy decision enforcement
The following table lists input request fields that can be used in an OPA policy to help enforce it.

| Name | Type | Description | Example |
|--------------------------|--------|-----------------------------------------------------------------------|--------------------------------------|
Expand All @@ -1110,12 +1110,12 @@ The following table input request fields can be used in OPA policy to help make

### Configuration

| Name | Type | Description | Required |
|------------------|--------|------------------------------------------------------------------------------------------------------------------------|----------|
| defaultStatus | int | The default http status code when request is denied by OPA policy decision | No |
| readBody | bool | Whether to read request body as OPA policy data on condition than body can be cast to string | No |
| includedHeaders | string | Used to configure which HTTP headers should be included in `input.request.headers`, <br/>comma-separated header names | No |
| policy | string | The OPA policy written using Rego declarative language | Yes |
| Name | Type | Description | Required |
|------------------|--------|--------------------------------------------------------------------------------------|----------|
| defaultStatus | int | The default HTTP status code when request is denied by the OPA policy decision | No |
| readBody | bool | Whether to read request body as OPA policy data on condition | No |
| includedHeaders | string | Names of the HTTP headers to be included in `input.request.headers`, comma-separated | No |
| policy | string | The OPA policy written in the Rego declarative language | Yes |

### Results
| Value | Description |
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -213,7 +213,7 @@ require (
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spaolacci/murmur3 v1.1.0
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cast v1.5.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
Expand Down
84 changes: 39 additions & 45 deletions pkg/filters/opafilter/opafilter.go
Expand Up @@ -18,16 +18,15 @@
package opafilter

import (
"bytes"
stdctx "context"
"encoding/json"
"errors"
"io"
"fmt"
"net/textproto"
"strings"
"time"

"github.com/open-policy-agent/opa/rego"
"github.com/spf13/cast"

"github.com/megaease/easegress/pkg/context"
"github.com/megaease/easegress/pkg/filters"
Expand All @@ -48,13 +47,6 @@ var (
errOpaInvalidResultType = errors.New("got an invalid type from repo policy. Only a boolean or map is valid")
)

// RegoResult is the expected result from rego policy.
type RegoResult struct {
Allow bool `json:"allow"`
AdditionalHeaders map[string]string `json:"additional_headers,omitempty"`
StatusCode int `json:"status_code,omitempty"`
}

var kind = &filters.Kind{
Name: kindName,
Description: "OPAFilter implement OpenPolicyAgent function",
Expand Down Expand Up @@ -120,7 +112,7 @@ func (o *OPAFilter) Init() {
).PrepareForEval(ctx)
defer cancelFunc()
if err != nil {
logger.Errorf("create PrepareForEval rego query error: %s", err)
panic(fmt.Sprintf("cannot create PrepareForEval rego query: %s", err))
} else {
o.regoQuery = &query
}
Expand Down Expand Up @@ -159,10 +151,12 @@ func (o *OPAFilter) evalRequest(r *httpprot.Request, w *httpprot.Response) strin

var body string
if o.spec.ReadBody {
buf, _ := io.ReadAll(r.Body)
body = string(buf)
// Put the body back in the request
r.Body = io.NopCloser(bytes.NewBuffer(buf))
_ = r.FetchPayload(0)
if !r.IsStream() {
body = string(r.RawPayload())
} else {
logger.Errorf("body is stream, cannot set as input request body")
}
}
pathParts := strings.Split(strings.Trim(r.Path(), "/"), "/")
input := map[string]any{
Expand Down Expand Up @@ -190,47 +184,47 @@ func (o *OPAFilter) evalRequest(r *httpprot.Request, w *httpprot.Response) strin
if err != nil {
return o.opaError(w, err)
}
if *bp {
if bp {
return ""
}
return resultFiltered
}

func boolP(val bool) *bool {
return &val
}

func (o *OPAFilter) handleRegoResult(w *httpprot.Response, result any) (*bool, error) {
func (o *OPAFilter) handleRegoResult(w *httpprot.Response, result any) (bool, error) {
if allowed, ok := result.(bool); ok {
if !allowed {
w.SetStatusCode(o.spec.DefaultStatus)
return boolP(false), nil
return false, nil
}
return true, nil
}
var resMap map[string]any
resMap, ok := result.(map[string]any)
if !ok {
return false, errOpaInvalidResultType
}
var allow bool
if a, exists := resMap["allow"]; exists {
allow = cast.ToBool(a)
}
if _, exists := resMap["additional_headers"]; exists {
if aHeaders, ok := resMap["additional_headers"].(map[string]any); ok {
for key, value := range aHeaders {
w.Header().Set(key, value)
}
}
return boolP(true), nil
}
if _, ok := result.(map[string]any); !ok {
return nil, errOpaInvalidResultType
}
marshaled, err := json.Marshal(result)
if err != nil {
return nil, errOpaInvalidResultType
}
regoResult := RegoResult{
// By default, a non-allowed request with return a 403 response.
StatusCode: o.spec.DefaultStatus,
AdditionalHeaders: make(map[string]string),
}
if err = json.Unmarshal(marshaled, &regoResult); err != nil {
return nil, errOpaInvalidResultType
}
// Set the headers on the ongoing request (overriding as necessary)
for key, value := range regoResult.AdditionalHeaders {
w.Header().Set(key, value)
}
if !regoResult.Allow {
w.SetStatusCode(regoResult.StatusCode)
if sc, exists := resMap["status_code"]; exists {
isc, err := cast.ToIntE(sc)
if err != nil {
logger.Errorf("cannot cast status_code to int: %s", err)
} else {
if !allow {
w.SetStatusCode(isc)
}
}
}
return boolP(regoResult.Allow), nil
return allow, nil
}

func (o *OPAFilter) opaError(resp *httpprot.Response, err error) string {
Expand Down

0 comments on commit 73dbbab

Please sign in to comment.