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:
Torin Sandall
2018-10-17 08:56:39 -07:00
parent 986d82fc4d
commit b95227688b
7 changed files with 145 additions and 51 deletions
+11 -7
View File
@@ -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),
+1 -1
View File
@@ -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
View File
@@ -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) {
+6
View File
@@ -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)