Add path conflict checks

Previously there were no checks in place to ensure that base and
virtual documents do not overlap. As a result, if users loaded raw
JSON and rules into OPA that overlapped, the evaluation results were
not well defined. With these changes, we can detect the overlap and
reject updates (to policies or data) that would cause inconsistent
results.

Fixes #1207

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
This commit is contained in:
Torin Sandall
2019-02-13 15:28:46 -08:00
parent b6d95b1cad
commit bb80fd7f74
14 changed files with 323 additions and 43 deletions
+13 -1
View File
@@ -973,6 +973,12 @@ func (s *Server) v1DataPatch(w http.ResponseWriter, r *http.Request) {
}
}
if err := ast.CheckPathConflicts(s.getCompiler(), storage.NonEmpty(ctx, s.store, txn)); len(err) > 0 {
s.store.Abort(ctx, txn)
writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err)
return
}
if err := s.store.Commit(ctx, txn); err != nil {
writer.ErrorAuto(w, err)
} else {
@@ -1141,6 +1147,12 @@ func (s *Server) v1DataPut(w http.ResponseWriter, r *http.Request) {
return
}
if err := ast.CheckPathConflicts(s.getCompiler(), storage.NonEmpty(ctx, s.store, txn)); len(err) > 0 {
s.store.Abort(ctx, txn)
writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err)
return
}
if err := s.store.Commit(ctx, txn); err != nil {
writer.ErrorAuto(w, err)
} else {
@@ -1359,7 +1371,7 @@ func (s *Server) v1PoliciesPut(w http.ResponseWriter, r *http.Request) {
modules[path] = parsedMod
c := ast.NewCompiler().SetErrorLimit(s.errLimit)
c := ast.NewCompiler().SetErrorLimit(s.errLimit).WithPathConflictsCheck(storage.NonEmpty(ctx, s.store, txn))
m.Timer(metrics.RegoModuleCompile).Start()
+27
View File
@@ -549,6 +549,33 @@ p = true { false }`
"message": "storage_write_conflict_error: /a/b"
}`},
}},
{"put base/virtual conflict", []tr{
{http.MethodPut, "/policies/testmod", "package x.y\np = 1\nq = 2", 200, ""},
{http.MethodPut, "/data/x", `{"y": {"p": "xxx"}}`, 400, `{
"code": "invalid_parameter",
"message": "1 error occurred: testmod:2: rego_compile_error: conflicting rule for data path x/y/p found"
}`},
{http.MethodPut, "/data/x/y", `{"p": "xxx"}`, 400, ``},
{http.MethodPut, "/data/x/y/p", `"xxx"`, 400, ``},
{http.MethodPut, "/data/x/y/p/a", `1`, 400, ``},
{http.MethodDelete, "/policies/testmod", "", 200, ""},
{http.MethodPut, "/data/x/y/p/a", `1`, 204, ``},
{http.MethodPut, "/policies/testmod", "package x.y\np = 1\nq = 2", 400, `{
"code": "invalid_parameter",
"message": "error(s) occurred while compiling module(s)",
"errors": [
{
"code": "rego_compile_error",
"message": "conflicting rule for data path x/y/p found",
"location": {
"file": "testmod",
"row": 2,
"col": 5
}
}
]
}`},
}},
{"get virtual", []tr{
{http.MethodPut, "/policies/test", testMod1, 200, ""},
{http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": {"y": [1,2,3,4], "z": [3,4,5,6]}}]`, 204, ""},