From c0a9763b0909a0ae6d6b4572c8c6e062cef1da36 Mon Sep 17 00:00:00 2001
From: Karan Kajla <kkajla12@users.noreply.github.com>
Date: Sun, 31 Mar 2024 13:17:49 -0700
Subject: [PATCH] Add policy support to query endpoint (#315)

---
 pkg/authz/query/handlers.go  |   20 +-
 pkg/authz/query/parser.go    |    1 -
 pkg/authz/query/resultset.go |   44 +-
 pkg/authz/query/service.go   |  156 +++-
 pkg/authz/query/spec.go      |   32 +-
 tests/v2/query-policy.json   | 1370 ++++++++++++++++++++++++++++++++++
 6 files changed, 1553 insertions(+), 70 deletions(-)
 create mode 100644 tests/v2/query-policy.json

diff --git a/pkg/authz/query/handlers.go b/pkg/authz/query/handlers.go
index 54a6ef1..be02aa7 100644
--- a/pkg/authz/query/handlers.go
+++ b/pkg/authz/query/handlers.go
@@ -42,12 +42,20 @@ func (svc QueryService) Routes() ([]service.Route, error) {
 }
 
 func queryV1(svc QueryService, w http.ResponseWriter, r *http.Request) error {
-	queryString := r.URL.Query().Get("q")
+	queryParams := r.URL.Query()
+	queryString := queryParams.Get("q")
 	query, err := NewQueryFromString(queryString)
 	if err != nil {
 		return err
 	}
 
+	if queryParams.Has("context") {
+		err = query.WithContext(queryParams.Get("context"))
+		if err != nil {
+			return service.NewInvalidParameterError("context", "invalid")
+		}
+	}
+
 	listParams := service.GetListParamsFromContext[QueryListParamParser](r.Context())
 	// create next cursor from lastId or afterId param
 	if r.URL.Query().Has("lastId") {
@@ -88,12 +96,20 @@ func queryV1(svc QueryService, w http.ResponseWriter, r *http.Request) error {
 }
 
 func queryV2(svc QueryService, w http.ResponseWriter, r *http.Request) error {
-	queryString := r.URL.Query().Get("q")
+	queryParams := r.URL.Query()
+	queryString := queryParams.Get("q")
 	query, err := NewQueryFromString(queryString)
 	if err != nil {
 		return err
 	}
 
+	if queryParams.Has("context") {
+		err = query.WithContext(queryParams.Get("context"))
+		if err != nil {
+			return service.NewInvalidParameterError("context", "invalid")
+		}
+	}
+
 	listParams := service.GetListParamsFromContext[QueryListParamParser](r.Context())
 	results, prevCursor, nextCursor, err := svc.Query(r.Context(), query, listParams)
 	if err != nil {
diff --git a/pkg/authz/query/parser.go b/pkg/authz/query/parser.go
index b508c5c..2bcc885 100644
--- a/pkg/authz/query/parser.go
+++ b/pkg/authz/query/parser.go
@@ -176,6 +176,5 @@ func NewQueryFromString(queryString string) (Query, error) {
 		}
 	}
 
-	query.rawString = queryString
 	return query, nil
 }
diff --git a/pkg/authz/query/resultset.go b/pkg/authz/query/resultset.go
index 36e4a05..39ba7ac 100644
--- a/pkg/authz/query/resultset.go
+++ b/pkg/authz/query/resultset.go
@@ -26,6 +26,7 @@ type ResultSetNode struct {
 	ObjectId   string
 	Relation   string
 	Warrant    warrant.WarrantSpec
+	Policy     warrant.Policy
 	IsImplicit bool
 	next       *ResultSetNode
 }
@@ -48,7 +49,7 @@ func (rs *ResultSet) List() *ResultSetNode {
 	return rs.head
 }
 
-func (rs *ResultSet) Add(objectType string, objectId string, relation string, warrant warrant.WarrantSpec, isImplicit bool) {
+func (rs *ResultSet) Add(objectType string, objectId string, relation string, warrant warrant.WarrantSpec, policy warrant.Policy, isImplicit bool) {
 	existingRes, exists := rs.m[key(objectType, objectId, relation)]
 	if !exists {
 		newNode := ResultSetNode{
@@ -56,6 +57,7 @@ func (rs *ResultSet) Add(objectType string, objectId string, relation string, wa
 			ObjectId:   objectId,
 			Relation:   relation,
 			Warrant:    warrant,
+			Policy:     policy,
 			IsImplicit: isImplicit,
 			next:       nil,
 		}
@@ -73,9 +75,15 @@ func (rs *ResultSet) Add(objectType string, objectId string, relation string, wa
 
 		// Add result node to map for O(1) lookups
 		rs.m[key(objectType, objectId, relation)] = &newNode
-	} else if existingRes.IsImplicit && !isImplicit { // favor explicit results
-		existingRes.IsImplicit = isImplicit
-		existingRes.Warrant = warrant
+	} else {
+		// favor explicit results
+		if existingRes.IsImplicit && !isImplicit {
+			existingRes.IsImplicit = isImplicit
+			existingRes.Warrant = warrant
+			existingRes.Policy = policy
+		}
+
+		existingRes.Policy = existingRes.Policy.Or(policy)
 	}
 }
 
@@ -95,13 +103,11 @@ func (rs *ResultSet) Has(objectType string, objectId string, relation string) bo
 func (rs *ResultSet) Union(other *ResultSet) *ResultSet {
 	resultSet := NewResultSet()
 	for iter := rs.List(); iter != nil; iter = iter.Next() {
-		resultSet.Add(iter.ObjectType, iter.ObjectId, iter.Relation, iter.Warrant, iter.IsImplicit)
+		resultSet.Add(iter.ObjectType, iter.ObjectId, iter.Relation, iter.Warrant, iter.Policy, iter.IsImplicit)
 	}
 
 	for iter := other.List(); iter != nil; iter = iter.Next() {
-		if !resultSet.Has(iter.ObjectType, iter.ObjectId, iter.Relation) || !iter.IsImplicit {
-			resultSet.Add(iter.ObjectType, iter.ObjectId, iter.Relation, iter.Warrant, iter.IsImplicit)
-		}
+		resultSet.Add(iter.ObjectType, iter.ObjectId, iter.Relation, iter.Warrant, iter.Policy, iter.IsImplicit)
 	}
 
 	return resultSet
@@ -121,11 +127,14 @@ func (rs *ResultSet) Intersect(other *ResultSet) *ResultSet {
 	for iter := a.List(); iter != nil; iter = iter.Next() {
 		if b.Has(iter.ObjectType, iter.ObjectId, iter.Relation) {
 			bRes := b.Get(iter.ObjectType, iter.ObjectId, iter.Relation)
-			if !bRes.IsImplicit {
-				result.Add(bRes.ObjectType, bRes.ObjectId, bRes.Relation, bRes.Warrant, bRes.IsImplicit)
-			} else {
-				result.Add(iter.ObjectType, iter.ObjectId, iter.Relation, iter.Warrant, iter.IsImplicit)
-			}
+			result.Add(
+				iter.ObjectType,
+				iter.ObjectId,
+				iter.Relation,
+				iter.Warrant,
+				iter.Policy.And(bRes.Policy),
+				bRes.IsImplicit || iter.IsImplicit,
+			)
 		}
 	}
 
@@ -135,11 +144,14 @@ func (rs *ResultSet) Intersect(other *ResultSet) *ResultSet {
 func (rs *ResultSet) String() string {
 	var strs []string
 	for iter := rs.List(); iter != nil; iter = iter.Next() {
+		resStr := fmt.Sprintf("%s => %s", key(iter.ObjectType, iter.ObjectId, iter.Relation), iter.Warrant.String())
+		if iter.Policy != "" {
+			resStr += fmt.Sprintf("[%s]", iter.Policy)
+		}
 		if iter.IsImplicit {
-			strs = append(strs, fmt.Sprintf("%s => %s [implicit]", key(iter.ObjectType, iter.ObjectId, iter.Relation), iter.Warrant.String()))
-		} else {
-			strs = append(strs, fmt.Sprintf("%s => %s", key(iter.ObjectType, iter.ObjectId, iter.Relation), iter.Warrant.String()))
+			resStr += "[implicit]"
 		}
+		strs = append(strs, resStr)
 	}
 
 	return strings.Join(strs, ", ")
diff --git a/pkg/authz/query/service.go b/pkg/authz/query/service.go
index d3d2837..75d0c50 100644
--- a/pkg/authz/query/service.go
+++ b/pkg/authz/query/service.go
@@ -114,7 +114,7 @@ func (svc QueryService) Query(ctx context.Context, query Query, listParams servi
 				}
 
 				for res := queryResult.List(); res != nil; res = res.Next() {
-					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit)
+					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit)
 				}
 			}
 		}
@@ -175,7 +175,7 @@ func (svc QueryService) Query(ctx context.Context, query Query, listParams servi
 				}
 
 				for res := queryResult.List(); res != nil; res = res.Next() {
-					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit)
+					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit)
 				}
 			}
 		}
@@ -184,13 +184,24 @@ func (svc QueryService) Query(ctx context.Context, query Query, listParams servi
 	}
 
 	for res := resultSet.List(); res != nil; res = res.Next() {
-		queryResults = append(queryResults, QueryResult{
-			ObjectType: res.ObjectType,
-			ObjectId:   res.ObjectId,
-			Relation:   res.Relation,
-			Warrant:    res.Warrant,
-			IsImplicit: res.IsImplicit,
-		})
+		var err error
+		addResult := true
+		if res.Policy != "" {
+			addResult, err = res.Policy.Eval(query.Context)
+			if err != nil {
+				return nil, nil, nil, err
+			}
+		}
+
+		if addResult {
+			queryResults = append(queryResults, QueryResult{
+				ObjectType: res.ObjectType,
+				ObjectId:   res.ObjectId,
+				Relation:   res.Relation,
+				Warrant:    res.Warrant,
+				IsImplicit: res.IsImplicit,
+			})
+		}
 	}
 
 	// handle sorting and pagination
@@ -357,27 +368,35 @@ func (svc QueryService) query(ctx context.Context, query Query, level int) (*Res
 						continue
 					}
 
+					policy := matchedWarrant.Policy.And(sub.Policy)
 					if matchedWarrant.ObjectId == warrant.Wildcard {
-						expandedWildcardWarrants, err := svc.listWarrants(ctx, warrant.FilterParams{
-							ObjectType: matchedWarrant.ObjectType,
-						})
+						expandedObjects, err := svc.listObjectsByType(ctx, matchedWarrant.ObjectType)
 						if err != nil {
 							return nil, err
 						}
 
-						for _, w := range expandedWildcardWarrants {
-							if w.ObjectId != warrant.Wildcard {
-								resultSet.Add(w.ObjectType, w.ObjectId, relation, matchedWarrant, sub.IsImplicit || level > 0)
-							}
+						for _, obj := range expandedObjects {
+							resultSet.Add(obj.ObjectType, obj.ObjectId, relation, matchedWarrant, policy, sub.IsImplicit || level > 0)
 						}
 					} else {
-						resultSet.Add(matchedWarrant.ObjectType, matchedWarrant.ObjectId, relation, matchedWarrant, sub.IsImplicit || level > 0)
+						resultSet.Add(matchedWarrant.ObjectType, matchedWarrant.ObjectId, relation, matchedWarrant, policy, sub.IsImplicit || level > 0)
 					}
 				}
 			} else if query.SelectObjects.WhereSubject == nil ||
 				(matchedWarrant.Subject.ObjectType == query.SelectObjects.WhereSubject.Type &&
 					matchedWarrant.Subject.ObjectId == query.SelectObjects.WhereSubject.Id) {
-				resultSet.Add(matchedWarrant.ObjectType, matchedWarrant.ObjectId, relation, matchedWarrant, false)
+				if matchedWarrant.ObjectId == warrant.Wildcard {
+					expandedObjects, err := svc.listObjectsByType(ctx, matchedWarrant.ObjectType)
+					if err != nil {
+						return nil, err
+					}
+
+					for _, obj := range expandedObjects {
+						resultSet.Add(obj.ObjectType, obj.ObjectId, relation, matchedWarrant, matchedWarrant.Policy, false)
+					}
+				} else {
+					resultSet.Add(matchedWarrant.ObjectType, matchedWarrant.ObjectId, relation, matchedWarrant, matchedWarrant.Policy, false)
+				}
 			}
 		}
 
@@ -388,7 +407,7 @@ func (svc QueryService) query(ctx context.Context, query Query, level int) (*Res
 			}
 
 			for res := implicitResultSet.List(); res != nil; res = res.Next() {
-				resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+				resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit || level > 0)
 			}
 		}
 
@@ -436,10 +455,10 @@ func (svc QueryService) query(ctx context.Context, query Query, level int) (*Res
 				}
 
 				for sub := subset.List(); sub != nil; sub = sub.Next() {
-					resultSet.Add(sub.ObjectType, sub.ObjectId, relation, matchedWarrant, sub.IsImplicit || level > 0)
+					resultSet.Add(sub.ObjectType, sub.ObjectId, relation, matchedWarrant, matchedWarrant.Policy.And(sub.Policy), sub.IsImplicit || level > 0)
 				}
 			} else if query.SelectSubjects.SubjectTypes[0] == matchedWarrant.Subject.ObjectType {
-				resultSet.Add(matchedWarrant.Subject.ObjectType, matchedWarrant.Subject.ObjectId, relation, matchedWarrant, false)
+				resultSet.Add(matchedWarrant.Subject.ObjectType, matchedWarrant.Subject.ObjectId, relation, matchedWarrant, matchedWarrant.Policy, false)
 			}
 		}
 
@@ -450,7 +469,7 @@ func (svc QueryService) query(ctx context.Context, query Query, level int) (*Res
 			}
 
 			for res := implicitResultSet.List(); res != nil; res = res.Next() {
-				resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+				resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit || level > 0)
 			}
 		}
 
@@ -517,7 +536,7 @@ func (svc QueryService) queryRule(ctx context.Context, query Query, level int, r
 
 				resultSet := NewResultSet()
 				for res := results.List(); res != nil; res = res.Next() {
-					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit || level > 0)
 				}
 
 				return resultSet, nil
@@ -538,24 +557,55 @@ func (svc QueryService) queryRule(ctx context.Context, query Query, level int, r
 						continue
 					}
 
-					inheritedResults, err := svc.query(ctx, Query{
-						Expand: query.Expand,
-						SelectObjects: &SelectObjects{
-							ObjectTypes: query.SelectObjects.ObjectTypes,
-							WhereSubject: &Resource{
-								Type: indirectWarrant.ObjectType,
-								Id:   indirectWarrant.ObjectId,
+					if indirectWarrant.ObjectId == warrant.Wildcard {
+						expandedObjects, err := svc.listObjectsByType(ctx, indirectWarrant.ObjectType)
+						if err != nil {
+							return nil, err
+						}
+
+						for _, obj := range expandedObjects {
+							if obj.ObjectId != indirectWarrant.Subject.ObjectId {
+								inheritedResults, err := svc.query(ctx, Query{
+									Expand: query.Expand,
+									SelectObjects: &SelectObjects{
+										ObjectTypes: query.SelectObjects.ObjectTypes,
+										WhereSubject: &Resource{
+											Type: obj.ObjectType,
+											Id:   obj.ObjectId,
+										},
+										Relations: []string{rule.WithRelation},
+									},
+									Context: query.Context,
+								}, 0)
+								if err != nil {
+									return nil, err
+								}
+
+								for res := inheritedResults.List(); res != nil; res = res.Next() {
+									resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, indirectWarrant.Policy.Or(res.Policy), res.IsImplicit || level > 0)
+								}
+							}
+						}
+					} else {
+						inheritedResults, err := svc.query(ctx, Query{
+							Expand: query.Expand,
+							SelectObjects: &SelectObjects{
+								ObjectTypes: query.SelectObjects.ObjectTypes,
+								WhereSubject: &Resource{
+									Type: indirectWarrant.ObjectType,
+									Id:   indirectWarrant.ObjectId,
+								},
+								Relations: []string{rule.WithRelation},
 							},
-							Relations: []string{rule.WithRelation},
-						},
-						Context: query.Context,
-					}, 0)
-					if err != nil {
-						return nil, err
-					}
+							Context: query.Context,
+						}, 0)
+						if err != nil {
+							return nil, err
+						}
 
-					for res := inheritedResults.List(); res != nil; res = res.Next() {
-						resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+						for res := inheritedResults.List(); res != nil; res = res.Next() {
+							resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, indirectWarrant.Policy.Or(res.Policy), res.IsImplicit || level > 0)
+						}
 					}
 				}
 
@@ -578,7 +628,7 @@ func (svc QueryService) queryRule(ctx context.Context, query Query, level int, r
 
 				resultSet := NewResultSet()
 				for res := results.List(); res != nil; res = res.Next() {
-					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+					resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.Policy, res.IsImplicit || level > 0)
 				}
 
 				return resultSet, nil
@@ -616,7 +666,7 @@ func (svc QueryService) queryRule(ctx context.Context, query Query, level int, r
 					}
 
 					for res := subset.List(); res != nil; res = res.Next() {
-						resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, res.IsImplicit || level > 0)
+						resultSet.Add(res.ObjectType, res.ObjectId, relation, res.Warrant, w.Policy.Or(res.Policy), res.IsImplicit || level > 0)
 					}
 				}
 
@@ -652,6 +702,30 @@ func (svc QueryService) listWarrants(ctx context.Context, filterParams warrant.F
 	}
 }
 
+func (svc QueryService) listObjectsByType(ctx context.Context, objectType string) ([]object.ObjectSpec, error) {
+	var result []object.ObjectSpec
+	listParams := service.DefaultListParams(object.ObjectListParamParser{})
+	listParams.WithLimit(MaxEdges)
+	for {
+		objectSpecs, _, nextCursor, err := svc.objectSvc.List(
+			ctx,
+			&object.FilterOptions{ObjectType: objectType},
+			listParams,
+		)
+		if err != nil {
+			return nil, err
+		}
+
+		result = append(result, objectSpecs...)
+
+		if nextCursor == nil {
+			return result, nil
+		}
+
+		listParams.NextCursor = nextCursor
+	}
+}
+
 func objectKey(objectType string, objectId string) string {
 	return fmt.Sprintf("%s:%s", objectType, objectId)
 }
diff --git a/pkg/authz/query/spec.go b/pkg/authz/query/spec.go
index c0ac820..145be5a 100644
--- a/pkg/authz/query/spec.go
+++ b/pkg/authz/query/spec.go
@@ -15,10 +15,12 @@
 package authz
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 
-	baseWarrant "github.com/warrant-dev/warrant/pkg/authz/warrant"
+	"github.com/pkg/errors"
+	warrant "github.com/warrant-dev/warrant/pkg/authz/warrant"
 	"github.com/warrant-dev/warrant/pkg/service"
 )
 
@@ -26,11 +28,21 @@ type Query struct {
 	Expand         bool
 	SelectSubjects *SelectSubjects
 	SelectObjects  *SelectObjects
-	Context        baseWarrant.PolicyContext
-	rawString      string
+	Context        warrant.PolicyContext
 }
 
-func (q Query) String() string {
+func (q *Query) WithContext(contextString string) error {
+	var context warrant.PolicyContext
+	err := json.Unmarshal([]byte(contextString), &context)
+	if err != nil {
+		return errors.Wrap(err, "query: error parsing query context")
+	}
+
+	q.Context = context
+	return nil
+}
+
+func (q *Query) String() string {
 	var str string
 	if q.Expand {
 		str = "select"
@@ -95,12 +107,12 @@ type QueryHaving struct {
 }
 
 type QueryResult struct {
-	ObjectType string                  `json:"objectType"`
-	ObjectId   string                  `json:"objectId"`
-	Relation   string                  `json:"relation"`
-	Warrant    baseWarrant.WarrantSpec `json:"warrant"`
-	IsImplicit bool                    `json:"isImplicit"`
-	Meta       map[string]interface{}  `json:"meta,omitempty"`
+	ObjectType string                 `json:"objectType"`
+	ObjectId   string                 `json:"objectId"`
+	Relation   string                 `json:"relation"`
+	Warrant    warrant.WarrantSpec    `json:"warrant"`
+	IsImplicit bool                   `json:"isImplicit"`
+	Meta       map[string]interface{} `json:"meta,omitempty"`
 }
 
 type QueryResponseV1 struct {
diff --git a/tests/v2/query-policy.json b/tests/v2/query-policy.json
new file mode 100644
index 0000000..6d2fdff
--- /dev/null
+++ b/tests/v2/query-policy.json
@@ -0,0 +1,1370 @@
+{
+    "ignoredFields": [
+        "createdAt"
+    ],
+    "tests": [
+        {
+            "name": "assignRoleAdminMemberOfAllRoles",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "*",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "admin"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "*",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "admin"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleAdminMemberOfPermissionManageRoles",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "manage-roles",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "admin"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "manage-roles",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "admin"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleManagerMemberOfPermissionCreateItem",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "create-item",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "create-item",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleManagerMemberOfPermissionDeleteItem",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "delete-item",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "delete-item",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleManagerMemberOfRoleAccountant",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "accountant",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "accountant",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "manager"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleAccountantMemberOfPermissionViewBalanceSheet",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "view-balance-sheet",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "accountant"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "view-balance-sheet",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "accountant"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleAccountantMemberOfRoleReadOnly",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "accountant"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "accountant"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignRoleReadOnlyMemberOfPermissionViewItems",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "view-items",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "read-only"
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "permission",
+                    "objectId": "view-items",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "role",
+                        "objectId": "read-only"
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignUserJohnMemberOfRoleAdminOnTeam1And2",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "headers": {
+                    "Warrant-Token": "latest"
+                },
+                "body": {
+                    "objectType": "role",
+                    "objectId": "admin",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "john"
+                    },
+                    "policy": "team in ['team-1', 'team-2']"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "admin",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "john"
+                    },
+                    "policy": "team in ['team-1', 'team-2']"
+                }
+            }
+        },
+        {
+            "name": "assignUserJohnMemberOfRoleAdminOnTeam3",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "headers": {
+                    "Warrant-Token": "latest"
+                },
+                "body": {
+                    "objectType": "role",
+                    "objectId": "manager",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "john"
+                    },
+                    "policy": "team == 'team-3'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "manager",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "john"
+                    },
+                    "policy": "team == 'team-3'"
+                }
+            }
+        },
+        {
+            "name": "assignUserJaneMemberOfRoleAccountantOnTeam1And3",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "accountant",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "jane"
+                    },
+                    "policy": "team in ['team-1', 'team-3']"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "accountant",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "jane"
+                    },
+                    "policy": "team in ['team-1', 'team-3']"
+                }
+            }
+        },
+        {
+            "name": "assignUserJaneMemberOfRoleReadOnlyOnTeam2",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "jane"
+                    },
+                    "policy": "team == 'team-2'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "jane"
+                    },
+                    "policy": "team == 'team-2'"
+                }
+            }
+        },
+        {
+            "name": "assignUserJamesMemberOfRoleAdminOnTeam1",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "admin",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-1'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "admin",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-1'"
+                }
+            }
+        },
+        {
+            "name": "assignUserJamesMemberOfRoleManagerOnTeam2",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "manager",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-2'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "manager",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-2'"
+                }
+            }
+        },
+        {
+            "name": "assignUserJamesMemberOfRoleReadOnlyOnTeam3",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-3'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "role",
+                    "objectId": "read-only",
+                    "relation": "member",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "james"
+                    },
+                    "policy": "team == 'team-3'"
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJohnIsMemberInTeam1",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:john%20is%20member&context=%7B%22team%22%3A%22team-1%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "create-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "create-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "delete-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "delete-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "manage-roles",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "manage-roles",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "admin"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJohnIsMemberInTeam2",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:john%20is%20member&context=%7B%22team%22%3A%22team-2%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "create-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "create-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "delete-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "delete-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "manage-roles",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "manage-roles",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "admin"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJohnIsMemberInTeam3",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:john%20is%20member&context=%7B%22team%22%3A%22team-3%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "create-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "create-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "delete-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "delete-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJaneIsMemberInTeam1",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:jane%20is%20member&context=%7B%22team%22%3A%22team-1%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJaneIsMemberInTeam2",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:jane%20is%20member&context=%7B%22team%22%3A%22team-2%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJaneIsMemberInTeam3",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:jane%20is%20member&context=%7B%22team%22%3A%22team-3%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJamesIsMemberInTeam1",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:james%20is%20member&context=%7B%22team%22%3A%22team-1%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "create-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "create-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "delete-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "delete-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "manage-roles",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "manage-roles",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "admin"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJamesIsMemberInTeam2",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:james%20is%20member&context=%7B%22team%22%3A%22team-2%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "create-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "create-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "delete-item",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "delete-item",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "manager"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-balance-sheet",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-balance-sheet",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "accountant"
+                                }
+                            },
+                            "isImplicit": true
+                        },
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectPermissionsWhereUserJamesIsMemberInTeam3",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20permission%20where%20user:james%20is%20member&context=%7B%22team%22%3A%22team-3%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "permission",
+                            "objectId": "view-items",
+                            "relation": "member",
+                            "warrant": {
+                                "objectType": "permission",
+                                "objectId": "view-items",
+                                "relation": "member",
+                                "subject": {
+                                    "objectType": "role",
+                                    "objectId": "read-only"
+                                }
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "deleteUserJohn",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/user/john"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteUserJane",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/user/jane"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteUserJames",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/user/james"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteRoleAdmin",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/role/admin"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteRoleManager",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/role/manager"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteRoleAccountant",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/role/accountant"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteRoleReadOnly",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/role/read-only"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deletePermissionManageRoles",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/permission/manage-roles"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deletePermissionCreateItem",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/permission/create-item"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deletePermissionDeleteItem",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/permission/delete-item"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deletePermissionViewItems",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/permission/view-items"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deletePermissionViewBalanceSheet",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/permission/view-balance-sheet"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "createDocumentObjectType",
+            "request": {
+                "method": "POST",
+                "url": "/v2/object-types",
+                "body": {
+                    "type": "document",
+                    "relations": {
+                        "owner": {},
+                        "editor": {
+                            "inheritIf": "owner"
+                        },
+                        "viewer": {
+                            "inheritIf": "editor"
+                        },
+                        "editor-viewer": {
+                            "inheritIf": "allOf",
+                            "rules": [
+                                {
+                                    "inheritIf": "editor"
+                                },
+                                {
+                                    "inheritIf": "viewer"
+                                }
+                            ]
+                        }
+                    }
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "type": "document",
+                    "relations": {
+                        "owner": {},
+                        "editor": {
+                            "inheritIf": "owner"
+                        },
+                        "viewer": {
+                            "inheritIf": "editor"
+                        },
+                        "editor-viewer": {
+                            "inheritIf": "allOf",
+                            "rules": [
+                                {
+                                    "inheritIf": "editor"
+                                },
+                                {
+                                    "inheritIf": "viewer"
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "assignUserU1OwnerOfDocumentD1InTeamT1",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "document",
+                    "objectId": "D1",
+                    "relation": "owner",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "U1"
+                    },
+                    "policy": "team == 'T1'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "document",
+                    "objectId": "D1",
+                    "relation": "owner",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "U1"
+                    },
+                    "policy": "team == 'T1'"
+                }
+            }
+        },
+        {
+            "name": "assignUserU2ViewerOfDocumentD1InTeamT2",
+            "request": {
+                "method": "POST",
+                "url": "/v2/warrants",
+                "body": {
+                    "objectType": "document",
+                    "objectId": "D1",
+                    "relation": "viewer",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "U2"
+                    },
+                    "policy": "team == 'T2'"
+                }
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "objectType": "document",
+                    "objectId": "D1",
+                    "relation": "viewer",
+                    "subject": {
+                        "objectType": "user",
+                        "objectId": "U2"
+                    },
+                    "policy": "team == 'T2'"
+                }
+            }
+        },
+        {
+            "name": "selectDocumentsWhereUserU1IsEditorViewerInTeamT1",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20document%20where%20user:U1%20is%20editor-viewer&context=%7B%22team%22%3A%22T1%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "document",
+                            "objectId": "D1",
+                            "relation": "editor-viewer",
+                            "warrant": {
+                                "objectType": "document",
+                                "objectId": "D1",
+                                "relation": "owner",
+                                "subject": {
+                                    "objectType": "user",
+                                    "objectId": "U1"
+                                },
+                                "policy": "team == 'T1'"
+                            },
+                            "isImplicit": true
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "selectDocumentsWhereUserU1IsEditorViewerInTeamT2",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20document%20where%20user:U1%20is%20editor-viewer&context=%7B%22team%22%3A%22T2%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": []
+                }
+            }
+        },
+        {
+            "name": "selectDocumentsWhereUserU2IsEditorViewerInTeamT1",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20document%20where%20user:U2%20is%20editor-viewer&context=%7B%22team%22%3A%22T1%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": []
+                }
+            }
+        },
+        {
+            "name": "selectDocumentsWhereUserU2IsViewerInTeamT2",
+            "request": {
+                "method": "GET",
+                "url": "/v2/query?q=select%20document%20where%20user:U2%20is%20viewer&context=%7B%22team%22%3A%22T2%22%7D"
+            },
+            "expectedResponse": {
+                "statusCode": 200,
+                "body": {
+                    "results": [
+                        {
+                            "objectType": "document",
+                            "objectId": "D1",
+                            "relation": "viewer",
+                            "warrant": {
+                                "objectType": "document",
+                                "objectId": "D1",
+                                "relation": "viewer",
+                                "subject": {
+                                    "objectType": "user",
+                                    "objectId": "U2"
+                                },
+                                "policy": "team == 'T2'"
+                            },
+                            "isImplicit": false
+                        }
+                    ]
+                }
+            }
+        },
+        {
+            "name": "deleteDocumentD1",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/document/D1"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteUser1",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/user/U1"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteUser2",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/objects/user/U2"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        },
+        {
+            "name": "deleteObjectTypeDocument",
+            "request": {
+                "method": "DELETE",
+                "url": "/v2/object-types/document"
+            },
+            "expectedResponse": {
+                "statusCode": 200
+            }
+        }
+    ]
+}