mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
plugins/logs: Include http request context in decision logs
It would be useful if users had the ability to enhance the decision log with info from the incoming HTTP request such as headers. This change allows users to configure headers whose values if present in the incoming HTTP request would be surfaced via the decision log. This can be extended in the future to include more context from the request. Fixes: #6693 Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
This commit is contained in:
+21
-19
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/logging"
|
||||
"github.com/open-policy-agent/opa/metrics"
|
||||
"github.com/open-policy-agent/opa/storage"
|
||||
"github.com/open-policy-agent/opa/topdown"
|
||||
@@ -15,25 +16,26 @@ import (
|
||||
|
||||
// Info contains information describing a policy decision.
|
||||
type Info struct {
|
||||
Txn storage.Transaction
|
||||
Revision string // Deprecated: Use `Bundles` instead
|
||||
Bundles map[string]BundleInfo
|
||||
DecisionID string
|
||||
TraceID string
|
||||
SpanID string
|
||||
RemoteAddr string
|
||||
Query string
|
||||
Path string
|
||||
Timestamp time.Time
|
||||
Input *interface{}
|
||||
InputAST ast.Value
|
||||
Results *interface{}
|
||||
MappedResults *interface{}
|
||||
NDBuiltinCache *interface{}
|
||||
Error error
|
||||
Metrics metrics.Metrics
|
||||
Trace []*topdown.Event
|
||||
RequestID uint64
|
||||
Txn storage.Transaction
|
||||
Revision string // Deprecated: Use `Bundles` instead
|
||||
Bundles map[string]BundleInfo
|
||||
DecisionID string
|
||||
TraceID string
|
||||
SpanID string
|
||||
RemoteAddr string
|
||||
HTTPRequestContext logging.HTTPRequestContext
|
||||
Query string
|
||||
Path string
|
||||
Timestamp time.Time
|
||||
Input *interface{}
|
||||
InputAST ast.Value
|
||||
Results *interface{}
|
||||
MappedResults *interface{}
|
||||
NDBuiltinCache *interface{}
|
||||
Error error
|
||||
Metrics metrics.Metrics
|
||||
Trace []*topdown.Event
|
||||
RequestID uint64
|
||||
}
|
||||
|
||||
// BundleInfo contains information describing a bundle.
|
||||
|
||||
+15
-14
@@ -2958,20 +2958,21 @@ func (l decisionLogger) Log(ctx context.Context, txn storage.Transaction, path s
|
||||
decisionID, _ := logging.DecisionIDFromContext(ctx)
|
||||
|
||||
info := &Info{
|
||||
Txn: txn,
|
||||
Revision: l.revision,
|
||||
Bundles: bundles,
|
||||
Timestamp: time.Now().UTC(),
|
||||
DecisionID: decisionID,
|
||||
RemoteAddr: rctx.ClientAddr,
|
||||
Path: path,
|
||||
Query: query,
|
||||
Input: goInput,
|
||||
InputAST: astInput,
|
||||
Results: goResults,
|
||||
Error: err,
|
||||
Metrics: m,
|
||||
RequestID: rctx.ReqID,
|
||||
Txn: txn,
|
||||
Revision: l.revision,
|
||||
Bundles: bundles,
|
||||
Timestamp: time.Now().UTC(),
|
||||
DecisionID: decisionID,
|
||||
RemoteAddr: rctx.ClientAddr,
|
||||
HTTPRequestContext: rctx.HTTPRequestContext,
|
||||
Path: path,
|
||||
Query: query,
|
||||
Input: goInput,
|
||||
InputAST: astInput,
|
||||
Results: goResults,
|
||||
Error: err,
|
||||
Metrics: m,
|
||||
RequestID: rctx.ReqID,
|
||||
}
|
||||
|
||||
if ndbCache != nil {
|
||||
|
||||
@@ -3491,6 +3491,51 @@ func TestDecisionIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecisionLoggingWithHTTPRequestContext(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
|
||||
decisions := []*Info{}
|
||||
|
||||
var nextID int
|
||||
|
||||
f.server = f.server.WithDecisionIDFactory(func() string {
|
||||
nextID++
|
||||
return fmt.Sprint(nextID)
|
||||
}).WithDecisionLoggerWithErr(func(_ context.Context, info *Info) error {
|
||||
decisions = append(decisions, info)
|
||||
return nil
|
||||
})
|
||||
|
||||
req := newReqV1("POST", "/data/nonexistent", `{"input": {"foo": 1}}`)
|
||||
req.Header.Set("foo", "bar")
|
||||
req.Header.Set("foo2", "bar2")
|
||||
req.Header.Add("foo2", "bar3")
|
||||
|
||||
var rctx logging.RequestContext
|
||||
rctx.HTTPRequestContext = logging.HTTPRequestContext{Header: req.Header.Clone()}
|
||||
|
||||
req = req.WithContext(logging.NewContext(req.Context(), &rctx))
|
||||
|
||||
if err := f.executeRequest(req, http.StatusOK, `{"decision_id": "1"}`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(decisions) != 1 {
|
||||
t.Fatalf("Expected exactly 1 decision but got: %d", len(decisions))
|
||||
}
|
||||
|
||||
expHeaders := http.Header{}
|
||||
expHeaders.Set("foo", "bar")
|
||||
expHeaders.Add("foo2", "bar2")
|
||||
expHeaders.Add("foo2", "bar3")
|
||||
|
||||
exp := logging.HTTPRequestContext{Header: expHeaders}
|
||||
|
||||
if !reflect.DeepEqual(decisions[0].HTTPRequestContext, exp) {
|
||||
t.Fatalf("Expected HTTP request context %v but got: %v", exp, decisions[0].HTTPRequestContext)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecisionLogging(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user