mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Add default decision configuration
Previously, OPA would serve POST requests with an empty URL path by querying data.system.main and returning the generated value. In some cases, it's useful to be able to reconfigure OPA to use a different document to serve these kinds of requests. The same goes for the OPA authorization policy. These changes make the default decision and default authorization decision paths configurable. Signed-off-by: Torin Sandall <torinsandall@gmail.com>
This commit is contained in:
@@ -7,9 +7,8 @@ package authorizer
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
@@ -19,16 +18,13 @@ import (
|
||||
"github.com/open-policy-agent/opa/storage"
|
||||
)
|
||||
|
||||
// SystemAuthzPath is the path of the document that defines auth/z decisions for
|
||||
// OPA itself.
|
||||
const SystemAuthzPath = "data.system.authz.allow"
|
||||
|
||||
// Basic provides policy-based authorization over incoming requests.
|
||||
type Basic struct {
|
||||
inner http.Handler
|
||||
compiler func() *ast.Compiler
|
||||
store storage.Store
|
||||
runtime *ast.Term
|
||||
decision string
|
||||
}
|
||||
|
||||
// Runtime returns an argument that sets the runtime on the authorizer.
|
||||
@@ -38,6 +34,14 @@ func Runtime(term *ast.Term) func(*Basic) {
|
||||
}
|
||||
}
|
||||
|
||||
// Decision returns an argument that sets the path of the authorization decision
|
||||
// to query.
|
||||
func Decision(ref ast.Ref) func(*Basic) {
|
||||
return func(b *Basic) {
|
||||
b.decision = ref.String()
|
||||
}
|
||||
}
|
||||
|
||||
// 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{
|
||||
@@ -62,7 +66,7 @@ func (h *Basic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
rego := rego.New(
|
||||
rego.Query(SystemAuthzPath),
|
||||
rego.Query(h.decision),
|
||||
rego.Compiler(h.compiler()),
|
||||
rego.Store(h.store),
|
||||
rego.Input(input),
|
||||
|
||||
@@ -163,7 +163,7 @@ func TestBasic(t *testing.T) {
|
||||
req = identifier.SetIdentity(req, tc.identity)
|
||||
}
|
||||
|
||||
NewBasic(&mockHandler{}, compiler, store).ServeHTTP(recorder, req)
|
||||
NewBasic(&mockHandler{}, compiler, store, Decision(ast.MustParseRef("data.system.authz.allow"))).ServeHTTP(recorder, req)
|
||||
|
||||
if recorder.Code != tc.expectedStatus {
|
||||
t.Fatalf("Expected status code %v but got: %v", tc.expectedStatus, recorder)
|
||||
|
||||
+40
-22
@@ -16,14 +16,13 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"net/url"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/metrics"
|
||||
@@ -74,8 +73,6 @@ const (
|
||||
PromHandlerCatch = "catchall"
|
||||
)
|
||||
|
||||
var systemMainPath = ast.MustParseRef("data.system.main")
|
||||
|
||||
// map of unsafe buitins
|
||||
var unsafeBuiltinsMap = map[string]bool{ast.HTTPSend.Name: true}
|
||||
|
||||
@@ -83,22 +80,24 @@ var unsafeBuiltinsMap = map[string]bool{ast.HTTPSend.Name: true}
|
||||
type Server struct {
|
||||
Handler http.Handler
|
||||
|
||||
addrs []string
|
||||
insecureAddr string
|
||||
authentication AuthenticationScheme
|
||||
authorization AuthorizationScheme
|
||||
cert *tls.Certificate
|
||||
mtx sync.RWMutex
|
||||
partials map[string]rego.PartialResult
|
||||
store storage.Store
|
||||
manager *plugins.Manager
|
||||
watcher *watch.Watcher
|
||||
decisionIDFactory func() string
|
||||
diagnostics Buffer
|
||||
revision string
|
||||
logger func(context.Context, *Info)
|
||||
errLimit int
|
||||
runtime *ast.Term
|
||||
addrs []string
|
||||
insecureAddr string
|
||||
authentication AuthenticationScheme
|
||||
authorization AuthorizationScheme
|
||||
cert *tls.Certificate
|
||||
mtx sync.RWMutex
|
||||
partials map[string]rego.PartialResult
|
||||
store storage.Store
|
||||
manager *plugins.Manager
|
||||
watcher *watch.Watcher
|
||||
decisionIDFactory func() string
|
||||
diagnostics Buffer
|
||||
revision string
|
||||
logger func(context.Context, *Info)
|
||||
errLimit int
|
||||
runtime *ast.Term
|
||||
defaultDecision ast.Ref
|
||||
defaultAuthorizationDecision ast.Ref
|
||||
}
|
||||
|
||||
// Loop will contain all the calls from the server that we'll be listening on.
|
||||
@@ -187,7 +186,12 @@ func (s *Server) Init(ctx context.Context) (*Server, error) {
|
||||
// so that the latter can run first.
|
||||
switch s.authorization {
|
||||
case AuthorizationBasic:
|
||||
s.Handler = authorizer.NewBasic(s.Handler, s.getCompiler, s.store, authorizer.Runtime(s.runtime))
|
||||
s.Handler = authorizer.NewBasic(
|
||||
s.Handler,
|
||||
s.getCompiler,
|
||||
s.store,
|
||||
authorizer.Runtime(s.runtime),
|
||||
authorizer.Decision(s.defaultAuthorizationDecision))
|
||||
}
|
||||
|
||||
switch s.authentication {
|
||||
@@ -295,6 +299,20 @@ func (s *Server) WithRuntime(term *ast.Term) *Server {
|
||||
return s
|
||||
}
|
||||
|
||||
// WithDefaultDecision sets path of the policy decision to query to serve
|
||||
// requests with an empty URL path.
|
||||
func (s *Server) WithDefaultDecision(ref ast.Ref) *Server {
|
||||
s.defaultDecision = ref
|
||||
return s
|
||||
}
|
||||
|
||||
// WithDefaultAuthorizationDecision sets path of the policy decision to query to
|
||||
// authorize requests to OPA itself.
|
||||
func (s *Server) WithDefaultAuthorizationDecision(ref ast.Ref) *Server {
|
||||
s.defaultAuthorizationDecision = ref
|
||||
return s
|
||||
}
|
||||
|
||||
// Listeners returns functions that listen and serve connections.
|
||||
func (s *Server) Listeners() ([]Loop, error) {
|
||||
loops := []Loop{}
|
||||
@@ -511,7 +529,7 @@ func (s *Server) migrateWatcher(txn storage.Transaction) {
|
||||
}
|
||||
|
||||
func (s *Server) unversionedPost(w http.ResponseWriter, r *http.Request) {
|
||||
s.v0QueryPath(w, r, systemMainPath)
|
||||
s.v0QueryPath(w, r, s.defaultDecision)
|
||||
}
|
||||
|
||||
func (s *Server) v0DataPost(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1787,6 +1787,8 @@ func TestDiagnostics(t *testing.T) {
|
||||
WithStore(f.server.store).
|
||||
WithManager(f.server.manager).
|
||||
WithDiagnosticsBuffer(NewBoundedBuffer(8)).
|
||||
WithDefaultDecision(ast.MustParseRef("data.system.main")).
|
||||
WithDefaultAuthorizationDecision(ast.MustParseRef("data.system.authz.allow")).
|
||||
Init(context.Background())
|
||||
|
||||
queriesOnly := `package system.diagnostics
|
||||
@@ -2427,6 +2429,8 @@ func TestAuthorization(t *testing.T) {
|
||||
WithStore(store).
|
||||
WithManager(m).
|
||||
WithAuthorization(AuthorizationBasic).
|
||||
WithDefaultDecision(ast.MustParseRef("data.system.main")).
|
||||
WithDefaultAuthorizationDecision(ast.MustParseRef("data.system.authz.allow")).
|
||||
Init(ctx)
|
||||
|
||||
if err != nil {
|
||||
@@ -2617,6 +2621,8 @@ func newFixture(t *testing.T) *fixture {
|
||||
WithAddresses([]string{":8182"}).
|
||||
WithStore(store).
|
||||
WithManager(m).
|
||||
WithDefaultDecision(ast.MustParseRef("data.system.main")).
|
||||
WithDefaultAuthorizationDecision(ast.MustParseRef("data.system.authz.allow")).
|
||||
Init(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
Reference in New Issue
Block a user