mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Add a new inter-query value cache to cache data across queries
This commit adds a new inter-query value cache that built-in functions can use to cache information across queries. For example, the `regex` and `glob` builtins can use this to cache compiled regex and glob match patterns respectively. The number of entries in the cache can be configured via the OPA config. By default there is no limit. Fixes: #6908 Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ type Basic struct {
|
||||
printHook print.Hook
|
||||
enablePrintStatements bool
|
||||
interQueryCache cache.InterQueryCache
|
||||
interQueryValueCache cache.InterQueryValueCache
|
||||
}
|
||||
|
||||
// Runtime returns an argument that sets the runtime on the authorizer.
|
||||
@@ -73,6 +74,13 @@ func InterQueryCache(interQueryCache cache.InterQueryCache) func(*Basic) {
|
||||
}
|
||||
}
|
||||
|
||||
// InterQueryValueCache enables the inter-query value cache on the authorizer
|
||||
func InterQueryValueCache(interQueryValueCache cache.InterQueryValueCache) func(*Basic) {
|
||||
return func(b *Basic) {
|
||||
b.interQueryValueCache = interQueryValueCache
|
||||
}
|
||||
}
|
||||
|
||||
// NewBasic returns a new Basic object.
|
||||
func NewBasic(inner http.Handler, compiler func() *ast.Compiler, store storage.Store, opts ...func(*Basic)) http.Handler {
|
||||
b := &Basic{
|
||||
@@ -107,6 +115,7 @@ func (h *Basic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
rego.EnablePrintStatements(h.enablePrintStatements),
|
||||
rego.PrintHook(h.printHook),
|
||||
rego.InterQueryBuiltinCache(h.interQueryCache),
|
||||
rego.InterQueryBuiltinValueCache(h.interQueryValueCache),
|
||||
)
|
||||
|
||||
rs, err := rego.Eval(r.Context())
|
||||
|
||||
@@ -6,6 +6,7 @@ package authorizer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -516,6 +517,43 @@ func TestInterQueryCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterQueryValueCache(t *testing.T) {
|
||||
|
||||
compiler := func() *ast.Compiler {
|
||||
module := `
|
||||
package system.authz
|
||||
import rego.v1
|
||||
|
||||
allow if {
|
||||
regex.match("foo.*", "foobar")
|
||||
}`
|
||||
c := ast.NewCompiler()
|
||||
c.Compile(map[string]*ast.Module{
|
||||
"test.rego": ast.MustParseModule(module),
|
||||
})
|
||||
if c.Failed() {
|
||||
t.Fatalf("Unexpected error compiling test module: %v", c.Errors)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:8181/v1/data", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, _ := cache.ParseCachingConfig(nil)
|
||||
interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config)
|
||||
|
||||
basic := NewBasic(&mockHandler{}, compiler, inmem.New(), InterQueryValueCache(interQueryValueCache), Decision(func() ast.Ref {
|
||||
return ast.MustParseRef("data.system.authz.allow")
|
||||
}))
|
||||
|
||||
// Execute the policy
|
||||
basic.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
func Equal(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
|
||||
+51
-38
@@ -111,42 +111,43 @@ type Server struct {
|
||||
Handler http.Handler
|
||||
DiagnosticHandler http.Handler
|
||||
|
||||
router *mux.Router
|
||||
addrs []string
|
||||
diagAddrs []string
|
||||
h2cEnabled bool
|
||||
authentication AuthenticationScheme
|
||||
authorization AuthorizationScheme
|
||||
cert *tls.Certificate
|
||||
tlsConfigMtx sync.RWMutex
|
||||
certFile string
|
||||
certFileHash []byte
|
||||
certKeyFile string
|
||||
certKeyFileHash []byte
|
||||
certRefresh time.Duration
|
||||
certPool *x509.CertPool
|
||||
certPoolFile string
|
||||
certPoolFileHash []byte
|
||||
minTLSVersion uint16
|
||||
mtx sync.RWMutex
|
||||
partials map[string]rego.PartialResult
|
||||
preparedEvalQueries *cache
|
||||
store storage.Store
|
||||
manager *plugins.Manager
|
||||
decisionIDFactory func() string
|
||||
logger func(context.Context, *Info) error
|
||||
errLimit int
|
||||
pprofEnabled bool
|
||||
runtime *ast.Term
|
||||
httpListeners []httpListener
|
||||
metrics Metrics
|
||||
defaultDecisionPath string
|
||||
interQueryBuiltinCache iCache.InterQueryCache
|
||||
allPluginsOkOnce bool
|
||||
distributedTracingOpts tracing.Options
|
||||
ndbCacheEnabled bool
|
||||
unixSocketPerm *string
|
||||
cipherSuites *[]uint16
|
||||
router *mux.Router
|
||||
addrs []string
|
||||
diagAddrs []string
|
||||
h2cEnabled bool
|
||||
authentication AuthenticationScheme
|
||||
authorization AuthorizationScheme
|
||||
cert *tls.Certificate
|
||||
tlsConfigMtx sync.RWMutex
|
||||
certFile string
|
||||
certFileHash []byte
|
||||
certKeyFile string
|
||||
certKeyFileHash []byte
|
||||
certRefresh time.Duration
|
||||
certPool *x509.CertPool
|
||||
certPoolFile string
|
||||
certPoolFileHash []byte
|
||||
minTLSVersion uint16
|
||||
mtx sync.RWMutex
|
||||
partials map[string]rego.PartialResult
|
||||
preparedEvalQueries *cache
|
||||
store storage.Store
|
||||
manager *plugins.Manager
|
||||
decisionIDFactory func() string
|
||||
logger func(context.Context, *Info) error
|
||||
errLimit int
|
||||
pprofEnabled bool
|
||||
runtime *ast.Term
|
||||
httpListeners []httpListener
|
||||
metrics Metrics
|
||||
defaultDecisionPath string
|
||||
interQueryBuiltinCache iCache.InterQueryCache
|
||||
interQueryBuiltinValueCache iCache.InterQueryValueCache
|
||||
allPluginsOkOnce bool
|
||||
distributedTracingOpts tracing.Options
|
||||
ndbCacheEnabled bool
|
||||
unixSocketPerm *string
|
||||
cipherSuites *[]uint16
|
||||
}
|
||||
|
||||
// Metrics defines the interface that the server requires for recording HTTP
|
||||
@@ -748,7 +749,8 @@ func (s *Server) initHandlerAuthz(handler http.Handler) http.Handler {
|
||||
authorizer.Decision(s.manager.Config.DefaultAuthorizationDecisionRef),
|
||||
authorizer.PrintHook(s.manager.PrintHook()),
|
||||
authorizer.EnablePrintStatements(s.manager.EnablePrintStatements()),
|
||||
authorizer.InterQueryCache(s.interQueryBuiltinCache))
|
||||
authorizer.InterQueryCache(s.interQueryBuiltinCache),
|
||||
authorizer.InterQueryValueCache(s.interQueryBuiltinValueCache))
|
||||
|
||||
if s.metrics != nil {
|
||||
handler = s.instrumentHandler(handler.ServeHTTP, PromHandlerAPIAuthz)
|
||||
@@ -800,7 +802,12 @@ func (s *Server) initRouters(ctx context.Context) {
|
||||
diagRouter := mux.NewRouter()
|
||||
|
||||
// authorizer, if configured, needs the iCache to be set up already
|
||||
s.interQueryBuiltinCache = iCache.NewInterQueryCacheWithContext(ctx, s.manager.InterQueryBuiltinCacheConfig())
|
||||
|
||||
cacheConfig := s.manager.InterQueryBuiltinCacheConfig()
|
||||
|
||||
s.interQueryBuiltinCache = iCache.NewInterQueryCacheWithContext(ctx, cacheConfig)
|
||||
s.interQueryBuiltinValueCache = iCache.NewInterQueryValueCache(ctx, cacheConfig)
|
||||
|
||||
s.manager.RegisterCacheTrigger(s.updateCacheConfig)
|
||||
|
||||
// Add authorization handler. This must come BEFORE authentication handler
|
||||
@@ -933,6 +940,7 @@ func (s *Server) execQuery(ctx context.Context, br bundleRevisions, txn storage.
|
||||
rego.Runtime(s.runtime),
|
||||
rego.UnsafeBuiltins(unsafeBuiltinsMap),
|
||||
rego.InterQueryBuiltinCache(s.interQueryBuiltinCache),
|
||||
rego.InterQueryBuiltinValueCache(s.interQueryBuiltinValueCache),
|
||||
rego.PrintHook(s.manager.PrintHook()),
|
||||
rego.EnablePrintStatements(s.manager.EnablePrintStatements()),
|
||||
rego.DistributedTracingOpts(s.distributedTracingOpts),
|
||||
@@ -1121,6 +1129,7 @@ func (s *Server) v0QueryPath(w http.ResponseWriter, r *http.Request, urlPath str
|
||||
rego.EvalParsedInput(input),
|
||||
rego.EvalMetrics(m),
|
||||
rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache),
|
||||
rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache),
|
||||
rego.EvalNDBuiltinCache(ndbCache),
|
||||
}
|
||||
|
||||
@@ -1402,6 +1411,7 @@ func (s *Server) v1CompilePost(w http.ResponseWriter, r *http.Request) {
|
||||
rego.Runtime(s.runtime),
|
||||
rego.UnsafeBuiltins(unsafeBuiltinsMap),
|
||||
rego.InterQueryBuiltinCache(s.interQueryBuiltinCache),
|
||||
rego.InterQueryBuiltinValueCache(s.interQueryBuiltinValueCache),
|
||||
rego.PrintHook(s.manager.PrintHook()),
|
||||
)
|
||||
|
||||
@@ -1541,6 +1551,7 @@ func (s *Server) v1DataGet(w http.ResponseWriter, r *http.Request) {
|
||||
rego.EvalMetrics(m),
|
||||
rego.EvalQueryTracer(buf),
|
||||
rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache),
|
||||
rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache),
|
||||
rego.EvalInstrument(includeInstrumentation),
|
||||
rego.EvalNDBuiltinCache(ndbCache),
|
||||
}
|
||||
@@ -1760,6 +1771,7 @@ func (s *Server) v1DataPost(w http.ResponseWriter, r *http.Request) {
|
||||
rego.EvalMetrics(m),
|
||||
rego.EvalQueryTracer(buf),
|
||||
rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache),
|
||||
rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache),
|
||||
rego.EvalInstrument(includeInstrumentation),
|
||||
rego.EvalNDBuiltinCache(ndbCache),
|
||||
}
|
||||
@@ -2655,6 +2667,7 @@ func isPathOwned(path, root []string) bool {
|
||||
|
||||
func (s *Server) updateCacheConfig(cacheConfig *iCache.Config) {
|
||||
s.interQueryBuiltinCache.UpdateConfig(cacheConfig)
|
||||
s.interQueryBuiltinValueCache.UpdateConfig(cacheConfig)
|
||||
}
|
||||
|
||||
func (s *Server) updateNDCache(enabled bool) {
|
||||
|
||||
Reference in New Issue
Block a user