diff --git a/cmd/expand/root.go b/cmd/expand/root.go index 3fae19e8c..63f0a0971 100644 --- a/cmd/expand/root.go +++ b/cmd/expand/root.go @@ -45,9 +45,9 @@ func NewExpandCmd() *cobra.Command { return cmdx.FailSilently(cmd) } - var tree *ketoapi.ExpandTree + var tree *ketoapi.Tree[*ketoapi.RelationTuple] if resp.Tree != nil { - tree = (&ketoapi.ExpandTree{}).FromProto(resp.Tree) + tree = ketoapi.TreeFromProto[*ketoapi.RelationTuple](resp.Tree) } cmdx.PrintJSONAble(cmd, tree) diff --git a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/main.go b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/main.go index 8655dea82..43a038bbe 100644 --- a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/main.go +++ b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/main.go @@ -28,7 +28,7 @@ func main() { panic(err) } - tree := (&ketoapi.ExpandTree{}).FromProto(res.Tree) + tree := ketoapi.TreeFromProto[*ketoapi.RelationTuple](res.Tree) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") diff --git a/go.sum b/go.sum index 213a35cc1..2d6b5425a 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs= +github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= diff --git a/internal/check/binop.go b/internal/check/binop.go index 65db2a7af..53e79c6b6 100644 --- a/internal/check/binop.go +++ b/internal/check/binop.go @@ -6,7 +6,8 @@ import ( "github.com/pkg/errors" "github.com/ory/keto/internal/check/checkgroup" - "github.com/ory/keto/internal/expand" + "github.com/ory/keto/internal/relationtuple" + "github.com/ory/keto/ketoapi" ) type binaryOperator = func(ctx context.Context, checks []checkgroup.CheckFunc) checkgroup.Result @@ -52,9 +53,9 @@ func and(ctx context.Context, checks []checkgroup.CheckFunc) checkgroup.Result { go check(childCtx, resultCh) } - tree := &expand.Tree{ - Type: expand.Intersection, - Children: []*expand.Tree{}, + tree := &ketoapi.Tree[*relationtuple.RelationTuple]{ + Type: ketoapi.TreeNodeIntersection, + Children: []*ketoapi.Tree[*relationtuple.RelationTuple]{}, } for i := 0; i < len(checks); i++ { diff --git a/internal/check/checkgroup/definitions.go b/internal/check/checkgroup/definitions.go index 3ab00707f..1d2474676 100644 --- a/internal/check/checkgroup/definitions.go +++ b/internal/check/checkgroup/definitions.go @@ -5,8 +5,8 @@ import ( "github.com/pkg/errors" - "github.com/ory/keto/internal/expand" "github.com/ory/keto/internal/relationtuple" + "github.com/ory/keto/ketoapi" ) type ( @@ -24,18 +24,20 @@ type ( Result struct { Membership Membership - Tree *expand.Tree + Tree *ketoapi.Tree[*relationtuple.RelationTuple] Err error } Edge struct { - Tuple relationtuple.InternalRelationTuple - Type expand.NodeType + Tuple relationtuple.RelationTuple + Type ketoapi.TreeNodeType } Transformation int Membership int + + tree = ketoapi.Tree[*relationtuple.RelationTuple] ) //go:generate stringer -type Membership @@ -82,15 +84,15 @@ func WithEdge(e Edge, f CheckFunc) CheckFunc { select { case result := <-childCh: if result.Tree == nil { - result.Tree = &expand.Tree{ - Type: expand.Leaf, + result.Tree = &ketoapi.Tree[*relationtuple.RelationTuple]{ + Type: ketoapi.TreeNodeLeaf, Tuple: &e.Tuple, } } else { - result.Tree = &expand.Tree{ + result.Tree = &ketoapi.Tree[*relationtuple.RelationTuple]{ Type: e.Type, Tuple: &e.Tuple, - Children: []*expand.Tree{result.Tree}, + Children: []*tree{result.Tree}, } } resultCh <- result diff --git a/internal/check/engine.go b/internal/check/engine.go index 24a862b82..a27296edc 100644 --- a/internal/check/engine.go +++ b/internal/check/engine.go @@ -8,12 +8,12 @@ import ( "github.com/ory/keto/internal/check/checkgroup" "github.com/ory/keto/internal/driver/config" - "github.com/ory/keto/internal/expand" "github.com/ory/keto/internal/namespace" "github.com/ory/keto/internal/namespace/ast" "github.com/ory/keto/internal/relationtuple" "github.com/ory/keto/internal/x" "github.com/ory/keto/internal/x/graph" + "github.com/ory/keto/ketoapi" ) type ( @@ -81,7 +81,7 @@ func (e *Engine) CheckRelationTuple(ctx context.Context, r *RelationTuple, restD func (e *Engine) checkExpandSubject(ctx context.Context, r *RelationTuple, restDepth int) checkgroup.CheckFunc { if restDepth < 0 { e.d.Logger(). - WithFields(r.ToLoggerFields()). + WithField("request", r.String()). Debug("reached max-depth, therefore this query will not be further expanded") return checkgroup.UnknownMemberFunc } @@ -98,7 +98,7 @@ func (e *Engine) checkExpandSubject(ctx context.Context, r *RelationTuple, restD err error visited bool innerCtx = graph.InitVisited(ctx) - query = &Query{Namespace: r.Namespace, Object: r.Object, Relation: r.Relation} + query = &Query{Namespace: &r.Namespace, Object: &r.Object, Relation: &r.Relation} ) for { subjects, pageToken, err = e.d.RelationTupleManager().GetRelationTuples(innerCtx, query, x.WithToken(pageToken)) @@ -114,15 +114,16 @@ func (e *Engine) checkExpandSubject(ctx context.Context, r *RelationTuple, restD if visited { continue } - if s.Subject.SubjectSet() == nil || s.Subject.SubjectSet().Relation == WildcardRelation { + subjectSet, ok := s.Subject.(*relationtuple.SubjectSet) + if !ok || subjectSet.Relation == WildcardRelation { continue } g.Add(e.checkIsAllowed( innerCtx, &RelationTuple{ - Namespace: s.Subject.SubjectSet().Namespace, - Object: s.Subject.SubjectSet().Object, - Relation: s.Subject.SubjectSet().Relation, + Namespace: subjectSet.Namespace, + Object: subjectSet.Object, + Relation: subjectSet.Relation, Subject: r.Subject, }, restDepth-1, @@ -152,8 +153,8 @@ func (e *Engine) checkDirect(ctx context.Context, r *RelationTuple, restDepth in if rels, _, err := e.d.RelationTupleManager().GetRelationTuples(ctx, r.ToQuery()); err == nil && len(rels) > 0 { resultCh <- checkgroup.Result{ Membership: checkgroup.IsMember, - Tree: &expand.Tree{ - Type: expand.Leaf, + Tree: &ketoapi.Tree[*relationtuple.RelationTuple]{ + Type: ketoapi.TreeNodeLeaf, Tuple: r, }, } diff --git a/internal/check/engine_test.go b/internal/check/engine_test.go index be0b1d9fe..63b7701cf 100644 --- a/internal/check/engine_test.go +++ b/internal/check/engine_test.go @@ -15,6 +15,7 @@ import ( "github.com/ory/keto/internal/namespace" "github.com/ory/keto/internal/relationtuple" "github.com/ory/keto/internal/x" + "github.com/ory/keto/ketoapi" ) type configProvider = config.Provider @@ -39,6 +40,33 @@ func newDepsProvider(t *testing.T, namespaces []*namespace.Namespace, pageOpts . } } +func toUUID(s string) uuid.UUID { + return uuid.NewV5(uuid.Nil, s) +} + +func tupleFromString(t *testing.T, s string) *relationtuple.RelationTuple { + rt, err := ketoapi.FromString(s) + require.NoError(t, err) + result := &relationtuple.RelationTuple{ + Namespace: rt.Namespace, + Object: toUUID(rt.Object), + Relation: rt.Relation, + } + switch { + case rt.SubjectID != nil: + result.Subject = &relationtuple.SubjectID{ID: toUUID(*rt.SubjectID)} + case rt.SubjectSet != nil: + result.Subject = &relationtuple.SubjectSet{ + Namespace: rt.SubjectSet.Namespace, + Object: toUUID(rt.SubjectSet.Object), + Relation: rt.SubjectSet.Relation, + } + default: + t.Fatal("invalid tuple") + } + return result +} + func TestEngine(t *testing.T) { ctx := context.Background() @@ -60,8 +88,7 @@ func TestEngine(t *testing.T) { e := check.NewEngine(reg) - userHasAccess, err := relationtuple.InternalFromString("test:object#access@user") - require.NoError(t, err) + userHasAccess := tupleFromString(t, "test:object#access@user") // global max-depth defaults to 5 assert.Equal(t, reg.Config(ctx).MaxReadDepth(), 5) @@ -423,7 +450,7 @@ func TestEngine(t *testing.T) { e := check.NewEngine(reg) for i, user := range users { - t.Run("user="+user, func(t *testing.T) { + t.Run("user="+user.String(), func(t *testing.T) { t.Skip() // TODO pagination allowed, err := e.CheckIsMember(ctx, &relationtuple.RelationTuple{ Namespace: namesp, @@ -530,7 +557,7 @@ func TestEngine(t *testing.T) { e := check.NewEngine(reg) - stations := []string{sendlingerTor, odeonsplatz, centralStation} + stations := []uuid.UUID{sendlingerTor, odeonsplatz, centralStation} res, err := e.CheckIsMember(ctx, &relationtuple.RelationTuple{ Namespace: namesp, Object: stations[0], diff --git a/internal/check/rewrites.go b/internal/check/rewrites.go index af90308bd..b596a4e4d 100644 --- a/internal/check/rewrites.go +++ b/internal/check/rewrites.go @@ -6,23 +6,24 @@ import ( "github.com/pkg/errors" "github.com/ory/keto/internal/check/checkgroup" - "github.com/ory/keto/internal/expand" "github.com/ory/keto/internal/namespace/ast" + "github.com/ory/keto/internal/relationtuple" "github.com/ory/keto/internal/x" + "github.com/ory/keto/ketoapi" ) func checkNotImplemented(_ context.Context, resultCh chan<- checkgroup.Result) { resultCh <- checkgroup.Result{Err: errors.WithStack(errors.New("not implemented"))} } -func toExpandNodeType(op ast.Operator) expand.NodeType { +func toTreeNodeType(op ast.Operator) ketoapi.TreeNodeType { switch op { case ast.OperatorOr: - return expand.Union + return ketoapi.TreeNodeUnion case ast.OperatorAnd: - return expand.Intersection + return ketoapi.TreeNodeIntersection default: - return expand.Union + return ketoapi.TreeNodeUnion } } @@ -60,25 +61,25 @@ func (e *Engine) checkUsersetRewrite( case *ast.TupleToUserset: checks = append(checks, checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.TupeToUserset, + Type: ketoapi.TreeNodeTupeToUserset, }, e.checkTupleToUserset(tuple, c, restDepth))) case *ast.ComputedUserset: checks = append(checks, checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.ComputedUserset, + Type: ketoapi.TreeNodeComputedUserset, }, e.checkComputedUserset(ctx, tuple, c, restDepth))) case *ast.UsersetRewrite: checks = append(checks, checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: toExpandNodeType(c.Operation), + Type: toTreeNodeType(c.Operation), }, e.checkUsersetRewrite(ctx, tuple, c, restDepth))) case *ast.InvertResult: checks = append(checks, checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.Not, + Type: ketoapi.TreeNodeNot, }, e.checkInverted(ctx, tuple, c, restDepth))) default: @@ -113,25 +114,25 @@ func (e *Engine) checkInverted( case *ast.TupleToUserset: check = checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.TupeToUserset, + Type: ketoapi.TreeNodeTupeToUserset, }, e.checkTupleToUserset(tuple, c, restDepth)) case *ast.ComputedUserset: check = checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.ComputedUserset, + Type: ketoapi.TreeNodeComputedUserset, }, e.checkComputedUserset(ctx, tuple, c, restDepth)) case *ast.UsersetRewrite: check = checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: toExpandNodeType(c.Operation), + Type: toTreeNodeType(c.Operation), }, e.checkUsersetRewrite(ctx, tuple, c, restDepth)) case *ast.InvertResult: check = checkgroup.WithEdge(checkgroup.Edge{ Tuple: *tuple, - Type: expand.Not, + Type: ketoapi.TreeNodeNot, }, e.checkInverted(ctx, tuple, c, restDepth)) default: @@ -228,9 +229,9 @@ func (e *Engine) checkTupleToUserset( tuples, nextPage, err = e.d.RelationTupleManager().GetRelationTuples( ctx, &Query{ - Namespace: tuple.Namespace, - Object: tuple.Object, - Relation: userset.Relation, + Namespace: &tuple.Namespace, + Object: &tuple.Object, + Relation: &userset.Relation, }, x.WithToken(prevPage)) if err != nil { @@ -239,19 +240,19 @@ func (e *Engine) checkTupleToUserset( } for _, t := range tuples { - if t.Subject.SubjectSet() == nil { - continue + if subjectSet, ok := t.Subject.(*relationtuple.SubjectSet); ok { + g.Add(e.checkIsAllowed( + ctx, + &RelationTuple{ + Namespace: subjectSet.Namespace, + Object: subjectSet.Object, + Relation: userset.ComputedUsersetRelation, + Subject: tuple.Subject, + }, + restDepth-1, + )) + } - g.Add(e.checkIsAllowed( - ctx, - &RelationTuple{ - Namespace: t.Subject.SubjectSet().Namespace, - Object: t.Subject.SubjectSet().Object, - Relation: userset.ComputedUsersetRelation, - Subject: tuple.Subject, - }, - restDepth-1, - )) } } resultCh <- g.Result() diff --git a/internal/check/userset_rewrites_test.go b/internal/check/userset_rewrites_test.go index f83fdbb42..a74b7e97d 100644 --- a/internal/check/userset_rewrites_test.go +++ b/internal/check/userset_rewrites_test.go @@ -11,10 +11,10 @@ import ( "github.com/ory/keto/internal/check" "github.com/ory/keto/internal/check/checkgroup" - "github.com/ory/keto/internal/expand" "github.com/ory/keto/internal/namespace" "github.com/ory/keto/internal/namespace/ast" "github.com/ory/keto/internal/relationtuple" + "github.com/ory/keto/ketoapi" ) var namespaces = []*namespace.Namespace{ @@ -92,10 +92,10 @@ var namespaces = []*namespace.Namespace{ func insertFixtures(t *testing.T, m relationtuple.Manager, tuples []string) { t.Helper() - relationTuples := make([]*relationtuple.InternalRelationTuple, len(tuples)) + relationTuples := make([]*relationtuple.RelationTuple, len(tuples)) var err error for i, tuple := range tuples { - relationTuples[i], err = relationtuple.InternalFromString(tuple) + relationTuples[i] = tupleFromString(t, tuple) require.NoError(t, err) } require.NoError(t, m.WriteRelationTuples(context.Background(), relationTuples...)) @@ -227,8 +227,7 @@ func TestUsersetRewrites(t *testing.T) { t.Run(tc.query, func(t *testing.T) { defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - rt, err := relationtuple.InternalFromString(tc.query) - require.NoError(t, err) + rt := tupleFromString(t, tc.query) res := e.CheckRelationTuple(ctx, rt, 100) require.NoError(t, res.Err) @@ -245,18 +244,22 @@ func TestUsersetRewrites(t *testing.T) { } // assertPath asserts that the given path can be found in the tree. -func assertPath(t *testing.T, path path, tree *expand.Tree) { +func assertPath(t *testing.T, path path, tree *ketoapi.Tree[*relationtuple.RelationTuple]) { require.NotNil(t, tree) - assert.True(t, hasPath(path, tree), "could not find path %s in tree:\n%s", path, tree) + assert.True(t, hasPath(t, path, tree), "could not find path %s in tree:\n%s", path, tree) } -func hasPath(path path, tree *expand.Tree) bool { +func hasPath(t *testing.T, path path, tree *ketoapi.Tree[*relationtuple.RelationTuple]) bool { if len(path) == 0 { return true } treeLabel := tree.Label() - if path[0] != "*" && path[0] != treeLabel { - return false + if path[0] != "*" { + // use tupleFromString to compare against paths with UUIDs. + tuple := tupleFromString(t, path[0]) + if tuple.String() != treeLabel { + return false + } } if len(path) == 1 { @@ -264,7 +267,7 @@ func hasPath(path path, tree *expand.Tree) bool { } for _, child := range tree.Children { - if hasPath(path[1:], child) { + if hasPath(t, path[1:], child) { return true } } diff --git a/internal/e2e/cases_test.go b/internal/e2e/cases_test.go index 0f2349dee..2befa69ef 100644 --- a/internal/e2e/cases_test.go +++ b/internal/e2e/cases_test.go @@ -57,36 +57,40 @@ func runCases(c client, m *namespaceTestManager) func(*testing.T) { rel := "expand" subjects := []string{"s1", "s2"} - expectedTree := &ketoapi.ExpandTree{ - Type: ketoapi.ExpandNodeUnion, - SubjectSet: &ketoapi.SubjectSet{ - Namespace: n.Name, - Object: obj, - Relation: rel, + expectedTree := &ketoapi.Tree[*ketoapi.RelationTuple]{ + Type: ketoapi.TreeNodeUnion, + Tuple: &ketoapi.RelationTuple{ + SubjectSet: &ketoapi.SubjectSet{ + Namespace: n.Name, + Object: obj, + Relation: rel, + }, }, - Children: make([]*ketoapi.ExpandTree, len(subjects)), + Children: make([]*ketoapi.Tree[*ketoapi.RelationTuple], len(subjects)), } for i, subjectID := range subjects { + subjectID := subjectID c.createTuple(t, &ketoapi.RelationTuple{ Namespace: n.Name, Object: obj, Relation: rel, SubjectID: &subjectID, }) - expectedTree.Children[i] = &ketoapi.ExpandTree{ - Type: ketoapi.ExpandNodeLeaf, - SubjectID: &subjectID, + expectedTree.Children[i] = &ketoapi.Tree[*ketoapi.RelationTuple]{ + Type: ketoapi.TreeNodeLeaf, + Tuple: &ketoapi.RelationTuple{ + SubjectID: &subjectID, + }, } } - actualTree := c.expand(t, expectedTree.SubjectSet, 100) + actualTree := c.expand(t, expectedTree.Tuple.SubjectSet, 100) assert.Equal(t, expectedTree.Type, actualTree.Type) - assert.Equal(t, expectedTree.SubjectSet, actualTree.SubjectSet) - assert.Equal(t, expectedTree.SubjectID, actualTree.SubjectID) + assert.Equalf(t, expectedTree.Tuple, actualTree.Tuple, + "want:\t%s\ngot:\t%s", expectedTree.Tuple, actualTree.Tuple) assert.Equal(t, len(expectedTree.Children), len(actualTree.Children), "expected: %+v; actual: %+v", expectedTree.Children, actualTree.Children) - expand.AssertExternalTreesAreEqual(t, expectedTree, actualTree) }) diff --git a/internal/e2e/cli_client_test.go b/internal/e2e/cli_client_test.go index 8d5570df3..ea9bdbb6c 100644 --- a/internal/e2e/cli_client_test.go +++ b/internal/e2e/cli_client_test.go @@ -99,9 +99,9 @@ func (g *cliClient) check(t require.TestingT, r *ketoapi.RelationTuple) bool { return res.Allowed } -func (g *cliClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.ExpandTree { +func (g *cliClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] { out := g.c.ExecNoErr(t, "expand", r.Relation, r.Namespace, r.Object, "--"+cliexpand.FlagMaxDepth, fmt.Sprintf("%d", depth), "--"+cmdx.FlagFormat, string(cmdx.FormatJSON)) - res := ketoapi.ExpandTree{} + res := ketoapi.Tree[*ketoapi.RelationTuple]{} require.NoError(t, json.Unmarshal([]byte(out), &res)) return &res } diff --git a/internal/e2e/full_suit_test.go b/internal/e2e/full_suit_test.go index a1a99b61f..c795ef440 100644 --- a/internal/e2e/full_suit_test.go +++ b/internal/e2e/full_suit_test.go @@ -35,7 +35,7 @@ type ( queryTuple(t require.TestingT, q *ketoapi.RelationQuery, opts ...x.PaginationOptionSetter) *ketoapi.GetResponse queryTupleErr(t require.TestingT, expected herodot.DefaultError, q *ketoapi.RelationQuery, opts ...x.PaginationOptionSetter) check(t require.TestingT, r *ketoapi.RelationTuple) bool - expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.ExpandTree + expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] waitUntilLive(t require.TestingT) } ) diff --git a/internal/e2e/grpc_client_test.go b/internal/e2e/grpc_client_test.go index 92d3c66f6..75e47687c 100644 --- a/internal/e2e/grpc_client_test.go +++ b/internal/e2e/grpc_client_test.go @@ -129,7 +129,7 @@ func (g *grpcClient) check(t require.TestingT, r *ketoapi.RelationTuple) bool { return resp.Allowed } -func (g *grpcClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.ExpandTree { +func (g *grpcClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] { c := rts.NewExpandServiceClient(g.readConn(t)) resp, err := c.Expand(g.ctx, &rts.ExpandRequest{ @@ -138,7 +138,7 @@ func (g *grpcClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int }) require.NoError(t, err) - return (&ketoapi.ExpandTree{}).FromProto(resp.Tree) + return ketoapi.TreeFromProto[*ketoapi.RelationTuple](resp.Tree) } func (g *grpcClient) waitUntilLive(t require.TestingT) { diff --git a/internal/e2e/helpers.go b/internal/e2e/helpers.go index e48772f32..f0170cd83 100644 --- a/internal/e2e/helpers.go +++ b/internal/e2e/helpers.go @@ -27,13 +27,10 @@ type namespaceTestManager struct { reg driver.Registry ctx context.Context nspaces []*namespace.Namespace - nextID int32 } func (m *namespaceTestManager) add(t *testing.T, nn ...*namespace.Namespace) { - for _, n := range nn { - m.nspaces = append(m.nspaces, n) - } + m.nspaces = append(m.nspaces, nn...) require.NoError(t, m.reg.Config(m.ctx).Set(config.KeyNamespaces, m.nspaces)) diff --git a/internal/e2e/rest_client_test.go b/internal/e2e/rest_client_test.go index 4c9ad415b..e0f93c171 100644 --- a/internal/e2e/rest_client_test.go +++ b/internal/e2e/rest_client_test.go @@ -140,14 +140,14 @@ func (rc *restClient) check(t require.TestingT, r *ketoapi.RelationTuple) bool { return false } -func (rc *restClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.ExpandTree { +func (rc *restClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] { query := r.ToURLQuery() query.Set("max-depth", fmt.Sprintf("%d", depth)) body, code := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", expand.RouteBase, query.Encode()), "", false) require.Equal(t, http.StatusOK, code, body) - tree := &ketoapi.ExpandTree{} + tree := &ketoapi.Tree[*ketoapi.RelationTuple]{} require.NoError(t, json.Unmarshal([]byte(body), tree)) return tree diff --git a/internal/e2e/sdk_client_test.go b/internal/e2e/sdk_client_test.go index c816307b2..8424d4d6c 100644 --- a/internal/e2e/sdk_client_test.go +++ b/internal/e2e/sdk_client_test.go @@ -199,30 +199,34 @@ func (c *sdkClient) check(t require.TestingT, r *ketoapi.RelationTuple) bool { return *resp.Payload.Allowed } -func buildTree(t require.TestingT, mt *models.ExpandTree) *ketoapi.ExpandTree { - et := &ketoapi.ExpandTree{ - Type: ketoapi.ExpandNodeType(*mt.Type), - } - if mt.SubjectSet != nil { - et.SubjectSet = &ketoapi.SubjectSet{ - Namespace: *mt.SubjectSet.Namespace, - Object: *mt.SubjectSet.Object, - Relation: *mt.SubjectSet.Relation, +func buildTree(t require.TestingT, mt *models.ExpandTree) *ketoapi.Tree[*ketoapi.RelationTuple] { + result := &ketoapi.Tree[*ketoapi.RelationTuple]{ + Type: ketoapi.TreeNodeType(*mt.Type), + } + if mt.Tuple.SubjectSet != nil { + result.Tuple = &ketoapi.RelationTuple{ + SubjectSet: &ketoapi.SubjectSet{ + Namespace: *mt.Tuple.SubjectSet.Namespace, + Object: *mt.Tuple.SubjectSet.Object, + Relation: *mt.Tuple.SubjectSet.Relation, + }, } } else { - et.SubjectID = &mt.SubjectID + result.Tuple = &ketoapi.RelationTuple{ + SubjectID: &mt.Tuple.SubjectID, + } } - if et.Type != ketoapi.ExpandNodeLeaf && len(mt.Children) != 0 { - et.Children = make([]*ketoapi.ExpandTree, len(mt.Children)) + if result.Type != ketoapi.TreeNodeLeaf && len(mt.Children) != 0 { + result.Children = make([]*ketoapi.Tree[*ketoapi.RelationTuple], len(mt.Children)) for i, c := range mt.Children { - et.Children[i] = buildTree(t, c) + result.Children[i] = buildTree(t, c) } } - return et + return result } -func (c *sdkClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.ExpandTree { +func (c *sdkClient) expand(t require.TestingT, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] { resp, err := c.getReadClient().Read.GetExpand( read.NewGetExpandParamsWithTimeout(requestTimeout). WithNamespace(r.Namespace). diff --git a/internal/expand/engine.go b/internal/expand/engine.go index e82e6c2c4..d71275895 100644 --- a/internal/expand/engine.go +++ b/internal/expand/engine.go @@ -39,13 +39,13 @@ func (e *Engine) BuildTree(ctx context.Context, subject relationtuple.Subject, r } if us, isUserSet := subject.(*relationtuple.SubjectSet); isUserSet { - ctx, wasAlreadyVisited := graph.CheckAndAddVisited(ctx, subject.UniqueID()) + ctx, wasAlreadyVisited := graph.CheckAndAddVisited(ctx, subject) if wasAlreadyVisited { return nil, nil } subTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: subject, } @@ -72,7 +72,7 @@ func (e *Engine) BuildTree(ctx context.Context, subject relationtuple.Subject, r } if restDepth <= 1 { - subTree.Type = ketoapi.ExpandNodeLeaf + subTree.Type = ketoapi.TreeNodeLeaf return subTree, nil } @@ -84,7 +84,7 @@ func (e *Engine) BuildTree(ctx context.Context, subject relationtuple.Subject, r } if child == nil { child = &relationtuple.Tree{ - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: r.Subject, } } @@ -98,7 +98,7 @@ func (e *Engine) BuildTree(ctx context.Context, subject relationtuple.Subject, r // is SubjectID return &relationtuple.Tree{ - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: subject, }, nil } diff --git a/internal/expand/engine_test.go b/internal/expand/engine_test.go index 8918098b1..2ec2d860d 100644 --- a/internal/expand/engine_test.go +++ b/internal/expand/engine_test.go @@ -54,7 +54,7 @@ func TestEngine(t *testing.T) { tree, err := e.BuildTree(context.Background(), user, 100) require.NoError(t, err) assert.Equal(t, &relationtuple.Tree{ - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: user, }, tree) }) @@ -86,15 +86,15 @@ func TestEngine(t *testing.T) { tree, err := e.BuildTree(context.Background(), bouldererUserSet, 100) require.NoError(t, err) expand.AssertInternalTreesAreEqual(t, &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: bouldererUserSet, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: paul, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: tommy, }, }, @@ -103,50 +103,50 @@ func TestEngine(t *testing.T) { t.Run("case=expands two levels", func(t *testing.T) { expectedTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: uuid.Must(uuid.NewV4()), Relation: "transitive member", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: uuid.Must(uuid.NewV4()), Relation: "member", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, }, }, { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: uuid.Must(uuid.NewV4()), Relation: "member", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}, }, }, @@ -196,28 +196,28 @@ func TestEngine(t *testing.T) { } expectedTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: ids[0], Relation: "child", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: ids[1], Relation: "child", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: ids[2], Relation: "child", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectSet{ Object: ids[3], Relation: "child", @@ -242,7 +242,7 @@ func TestEngine(t *testing.T) { root := uuid.Must(uuid.NewV4()) users := x.UUIDs(4) expectedTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{Object: root, Relation: "access"}, } @@ -253,7 +253,7 @@ func TestEngine(t *testing.T) { Subject: &relationtuple.SubjectID{ID: user}, })) expectedTree.Children = append(expectedTree.Children, &relationtuple.Tree{ - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: user}, }) } @@ -272,14 +272,14 @@ func TestEngine(t *testing.T) { reg, e := newTestEngine(t, []*namespace.Namespace{{}}) expectedTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Object: uuid.Must(uuid.NewV4()), Relation: "rel", }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectSet{ Object: uuid.Must(uuid.NewV4()), Relation: "sr", @@ -319,19 +319,19 @@ func TestEngine(t *testing.T) { reg, e := newTestEngine(t, []*namespace.Namespace{{Name: namesp}}) expectedTree := &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: sendlingerTorSS, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: odeonsplatzSS, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: centralStationSS, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: sendlingerTorSS, Children: nil, }, diff --git a/internal/expand/handler_test.go b/internal/expand/handler_test.go index 1273b908d..01a464a60 100644 --- a/internal/expand/handler_test.go +++ b/internal/expand/handler_test.go @@ -67,17 +67,23 @@ func TestRESTHandler(t *testing.T) { Object: "root", Relation: "parent of", } - expectedTree := &ketoapi.ExpandTree{ - Type: ketoapi.ExpandNodeUnion, - SubjectSet: rootSub, - Children: []*ketoapi.ExpandTree{ + expectedTree := &ketoapi.Tree[*ketoapi.RelationTuple]{ + Type: ketoapi.TreeNodeUnion, + Tuple: &ketoapi.RelationTuple{ + SubjectSet: rootSub, + }, + Children: []*ketoapi.Tree[*ketoapi.RelationTuple]{ { - Type: ketoapi.ExpandNodeLeaf, - SubjectID: x.Ptr("child0"), + Type: ketoapi.TreeNodeLeaf, + Tuple: &ketoapi.RelationTuple{ + SubjectID: x.Ptr("child0"), + }, }, { - Type: ketoapi.ExpandNodeLeaf, - SubjectID: x.Ptr("child1"), + Type: ketoapi.TreeNodeLeaf, + Tuple: &ketoapi.RelationTuple{ + SubjectID: x.Ptr("child1"), + }, }, }, } @@ -87,13 +93,13 @@ func TestRESTHandler(t *testing.T) { Namespace: nspace.Name, Object: rootSub.Object, Relation: rootSub.Relation, - SubjectID: expectedTree.Children[0].SubjectID, + SubjectID: expectedTree.Children[0].Tuple.SubjectID, }, &ketoapi.RelationTuple{ Namespace: nspace.Name, Object: rootSub.Object, Relation: rootSub.Relation, - SubjectID: expectedTree.Children[1].SubjectID, + SubjectID: expectedTree.Children[1].Tuple.SubjectID, }, ) @@ -104,7 +110,7 @@ func TestRESTHandler(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) - actualTree := ketoapi.ExpandTree{} + actualTree := ketoapi.Tree[*ketoapi.RelationTuple]{} body, err := io.ReadAll(resp.Body) require.NoError(t, err) t.Logf("body: %s", string(body)) diff --git a/internal/expand/testhelper.go b/internal/expand/testhelper.go index 951737f9d..845566bcc 100644 --- a/internal/expand/testhelper.go +++ b/internal/expand/testhelper.go @@ -10,13 +10,14 @@ import ( "github.com/ory/keto/ketoapi" ) -func AssertExternalTreesAreEqual(t *testing.T, expected, actual *ketoapi.ExpandTree) { +func AssertExternalTreesAreEqual(t *testing.T, expected, actual *ketoapi.Tree[*ketoapi.RelationTuple]) { t.Helper() assert.Truef(t, treesAreEqual(t, expected, actual), "expected:\n%+v\n\nactual:\n%+v", expected, actual) } -func treesAreEqual(t *testing.T, expected, actual *ketoapi.ExpandTree) bool { +// TODO(hperl): Refactor to generic tree equality helper. +func treesAreEqual(t *testing.T, expected, actual *ketoapi.Tree[*ketoapi.RelationTuple]) bool { if expected == nil || actual == nil { return expected == actual } @@ -25,8 +26,8 @@ func treesAreEqual(t *testing.T, expected, actual *ketoapi.ExpandTree) bool { t.Logf("expected type %q, actual type %q", expected.Type, actual.Type) return false } - if !assert.ObjectsAreEqual(expected.SubjectID, actual.SubjectID) || !assert.ObjectsAreEqual(expected.SubjectSet, actual.SubjectSet) { - t.Logf("expected subject: %+v %+v, actual subject: %+v %+v", expected.SubjectID, expected.SubjectSet, actual.SubjectID, actual.SubjectSet) + if !assert.ObjectsAreEqual(expected.Tuple.SubjectID, actual.Tuple.SubjectID) || !assert.ObjectsAreEqual(expected.Tuple.SubjectSet, actual.Tuple.SubjectSet) { + t.Logf("expected subject: %+v %+v, actual subject: %+v %+v", expected.Tuple.SubjectID, expected.Tuple.SubjectSet, actual.Tuple.SubjectID, actual.Tuple.SubjectSet) return false } if len(expected.Children) != len(actual.Children) { diff --git a/internal/expand/tree.go b/internal/expand/tree.go deleted file mode 100644 index 8ba6dc8f4..000000000 --- a/internal/expand/tree.go +++ /dev/null @@ -1,283 +0,0 @@ -package expand - -import ( - "encoding/json" - "fmt" - "strings" - - rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" - - "github.com/pkg/errors" - - "github.com/ory/keto/internal/relationtuple" -) - -// swagger:enum NodeType -type NodeType string - -const ( - Union NodeType = "union" - Exclusion NodeType = "exclusion" - Intersection NodeType = "intersection" - TupeToUserset NodeType = "tuple_to_userset" - ComputedUserset NodeType = "computed_userset" - Leaf NodeType = "leaf" - Not NodeType = "not" -) - -// swagger:ignore -type Tree struct { - Type NodeType - Subject relationtuple.Subject - Tuple *relationtuple.InternalRelationTuple - Children []*Tree -} - -func (t *Tree) GetSubject() relationtuple.Subject { - if t.Subject != nil { - return t.Subject - } - return t.Tuple.Subject -} - -var ( - ErrUnknownNodeType = errors.New("unknown node type") -) - -func (t NodeType) String() string { - return string(t) -} - -func (t *NodeType) UnmarshalJSON(v []byte) error { - switch string(v) { - case `"union"`: - *t = Union - case `"exclusion"`: - *t = Exclusion - case `"intersection"`: - *t = Intersection - case `"leaf"`: - *t = Leaf - default: - return ErrUnknownNodeType - } - return nil -} - -func (t NodeType) ToProto() rts.NodeType { - switch t { - case Leaf: - return rts.NodeType_NODE_TYPE_LEAF - case Union: - return rts.NodeType_NODE_TYPE_UNION - case Exclusion: - return rts.NodeType_NODE_TYPE_EXCLUSION - case Intersection: - return rts.NodeType_NODE_TYPE_INTERSECTION - } - return rts.NodeType_NODE_TYPE_UNSPECIFIED -} - -func NodeTypeFromProto(t rts.NodeType) NodeType { - switch t { - case rts.NodeType_NODE_TYPE_LEAF: - return Leaf - case rts.NodeType_NODE_TYPE_UNION: - return Union - case rts.NodeType_NODE_TYPE_EXCLUSION: - return Exclusion - case rts.NodeType_NODE_TYPE_INTERSECTION: - return Intersection - } - return Leaf -} - -// swagger:model expandTree -type node struct { - // required: true - Type NodeType `json:"type"` - Children []*node `json:"children,omitempty"` - SubjectID *string `json:"subject_id,omitempty"` - SubjectSet *relationtuple.SubjectSet `json:"subject_set,omitempty"` -} - -func (n *node) toTree() (*Tree, error) { - t := &Tree{} - if n.SubjectID == nil && n.SubjectSet == nil { - return nil, errors.WithStack(relationtuple.ErrNilSubject) - } else if n.SubjectID != nil && n.SubjectSet != nil { - return nil, errors.WithStack(relationtuple.ErrDuplicateSubject) - } - - if n.SubjectID != nil { - t.Subject = &relationtuple.SubjectID{ID: *n.SubjectID} - } else { - t.Subject = n.SubjectSet - } - - t.Type = n.Type - - if n.Children != nil { - t.Children = make([]*Tree, len(n.Children)) - for i := range n.Children { - var err error - t.Children[i], err = n.Children[i].toTree() - if err != nil { - return nil, err - } - } - } - - return t, nil -} - -func (n *node) fromTree(t *Tree) error { - n.Type = t.Type - n.SubjectID = t.GetSubject().SubjectID() - n.SubjectSet = t.GetSubject().SubjectSet() - - if t.Children != nil { - n.Children = make([]*node, len(t.Children)) - for i := range t.Children { - n.Children[i] = &node{} - if err := n.Children[i].fromTree(t.Children[i]); err != nil { - return err - } - } - } - - return nil -} - -func (t *Tree) UnmarshalJSON(v []byte) error { - var n node - if err := json.Unmarshal(v, &n); err != nil { - return errors.WithStack(err) - } - - tt, err := (&n).toTree() - if err != nil { - return err - } - - *t = *tt - return nil -} - -func (t *Tree) MarshalJSON() ([]byte, error) { - var n node - if err := n.fromTree(t); err != nil { - return nil, err - } - return json.Marshal(n) -} - -// swagger:ignore -func (t *Tree) ToProto() *rts.SubjectTree { - if t == nil { - return nil - } - - if t.Type == Leaf { - return &rts.SubjectTree{ - NodeType: rts.NodeType_NODE_TYPE_LEAF, - Subject: t.GetSubject().ToProto(), - } - } - - children := make([]*rts.SubjectTree, len(t.Children)) - for i, c := range t.Children { - children[i] = c.ToProto() - } - - return &rts.SubjectTree{ - NodeType: t.Type.ToProto(), - Subject: t.GetSubject().ToProto(), - Children: children, - } -} - -// swagger:ignore -func TreeFromProto(t *rts.SubjectTree) (*Tree, error) { - if t == nil { - return nil, nil - } - - sub, err := relationtuple.SubjectFromProto(t.Subject) - if err != nil { - return nil, err - } - self := &Tree{ - Type: NodeTypeFromProto(t.NodeType), - Subject: sub, - } - - if t.NodeType != rts.NodeType_NODE_TYPE_LEAF { - self.Children = make([]*Tree, len(t.Children)) - for i, c := range t.Children { - var err error - self.Children[i], err = TreeFromProto(c) - if err != nil { - return nil, err - } - } - } - - return self, nil -} - -func (t *Tree) Label() string { - if t == nil { - return "" - } - - if t.Subject != nil { - return t.Subject.String() - } else if t.Tuple != nil { - return t.Tuple.String() - } - return "" -} - -func (t *Tree) String() string { - if t == nil { - return "" - } - - nodeLabel := t.Label() - - if t.Type == Leaf { - return fmt.Sprintf("∋ %s️", nodeLabel) - } - - children := make([]string, len(t.Children)) - for i, c := range t.Children { - var indent string - if i == len(t.Children)-1 { - indent = " " - } else { - indent = "│ " - } - children[i] = strings.Join(strings.Split(c.String(), "\n"), "\n"+indent) - } - - setOperation := "" - switch t.Type { - case Intersection: - setOperation = "⋂" - case Union: - setOperation = "⋃" - case Exclusion: - setOperation = `\` - case TupeToUserset: - setOperation = "┐ tuple to userset" - case ComputedUserset: - setOperation = "┐ computed userset" - } - - boxSymbol := "├" - if len(children) == 1 { - boxSymbol = "└" - } - return fmt.Sprintf("%s %s\n%s──%s", setOperation, nodeLabel, boxSymbol, strings.Join(children, "\n└──")) -} diff --git a/internal/httpclient-next/api/openapi.yaml b/internal/httpclient-next/api/openapi.yaml index 95a6fe688..dae1b641f 100644 --- a/internal/httpclient-next/api/openapi.yaml +++ b/internal/httpclient-next/api/openapi.yaml @@ -73,7 +73,8 @@ paths: style: form responses: "204": - description: An empty response + description: Empty responses are sent when, for example, resources are deleted. + The HTTP status code for empty responses is typically 201. "400": content: application/json: @@ -102,7 +103,8 @@ paths: x-originalParamName: Payload responses: "204": - description: An empty response + description: Empty responses are sent when, for example, resources are deleted. + The HTTP status code for empty responses is typically 201. "400": content: application/json: @@ -597,55 +599,65 @@ paths: components: responses: emptyResponse: - description: An empty response + description: Empty responses are sent when, for example, resources are deleted. + The HTTP status code for empty responses is typically 201. schemas: UUID: format: uuid4 type: string expandTree: example: - subject_id: subject_id + tuple: + subject_id: subject_id + namespace: namespace + object: object + relation: relation + subject_set: + namespace: namespace + object: object + relation: relation children: - null - null type: union - subject_set: - namespace: namespace - object: object - relation: relation properties: children: description: The children of the node, possibly none. items: $ref: '#/components/schemas/expandTree' type: array - subject_id: - description: The subject ID the node represents. Either this field, or SubjectSet - are set. - type: string - subject_set: - $ref: '#/components/schemas/subjectSet' + tuple: + $ref: '#/components/schemas/relationTuple' type: description: |- The type of the node. - union Union - exclusion Exclusion - intersection Intersection - leaf Leaf - unspecified Unspecified + union TreeNodeUnion + exclusion TreeNodeExclusion + intersection TreeNodeIntersection + leaf TreeNodeLeaf + tuple_to_userset TreeNodeTupeToUserset + computed_userset TreeNodeComputedUserset + not TreeNodeNot + unspecified TreeNodeUnspecified enum: - union - exclusion - intersection - leaf + - tuple_to_userset + - computed_userset + - not - unspecified type: string x-go-enum-desc: |- - union Union - exclusion Exclusion - intersection Intersection - leaf Leaf - unspecified Unspecified + union TreeNodeUnion + exclusion TreeNodeExclusion + intersection TreeNodeIntersection + leaf TreeNodeLeaf + tuple_to_userset TreeNodeTupeToUserset + computed_userset TreeNodeComputedUserset + not TreeNodeNot + unspecified TreeNodeUnspecified required: - type type: object diff --git a/internal/httpclient-next/docs/ExpandTree.md b/internal/httpclient-next/docs/ExpandTree.md index 652d26d73..8d9d8b22b 100644 --- a/internal/httpclient-next/docs/ExpandTree.md +++ b/internal/httpclient-next/docs/ExpandTree.md @@ -5,9 +5,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Children** | Pointer to [**[]ExpandTree**](ExpandTree.md) | The children of the node, possibly none. | [optional] -**SubjectId** | Pointer to **string** | The subject ID the node represents. Either this field, or SubjectSet are set. | [optional] -**SubjectSet** | Pointer to [**SubjectSet**](SubjectSet.md) | | [optional] -**Type** | **string** | The type of the node. union Union exclusion Exclusion intersection Intersection leaf Leaf unspecified Unspecified | +**Tuple** | Pointer to [**RelationTuple**](RelationTuple.md) | | [optional] +**Type** | **string** | The type of the node. union TreeNodeUnion exclusion TreeNodeExclusion intersection TreeNodeIntersection leaf TreeNodeLeaf tuple_to_userset TreeNodeTupeToUserset computed_userset TreeNodeComputedUserset not TreeNodeNot unspecified TreeNodeUnspecified | ## Methods @@ -53,55 +52,30 @@ SetChildren sets Children field to given value. HasChildren returns a boolean if a field has been set. -### GetSubjectId +### GetTuple -`func (o *ExpandTree) GetSubjectId() string` +`func (o *ExpandTree) GetTuple() RelationTuple` -GetSubjectId returns the SubjectId field if non-nil, zero value otherwise. +GetTuple returns the Tuple field if non-nil, zero value otherwise. -### GetSubjectIdOk +### GetTupleOk -`func (o *ExpandTree) GetSubjectIdOk() (*string, bool)` +`func (o *ExpandTree) GetTupleOk() (*RelationTuple, bool)` -GetSubjectIdOk returns a tuple with the SubjectId field if it's non-nil, zero value otherwise +GetTupleOk returns a tuple with the Tuple field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetSubjectId +### SetTuple -`func (o *ExpandTree) SetSubjectId(v string)` +`func (o *ExpandTree) SetTuple(v RelationTuple)` -SetSubjectId sets SubjectId field to given value. +SetTuple sets Tuple field to given value. -### HasSubjectId +### HasTuple -`func (o *ExpandTree) HasSubjectId() bool` +`func (o *ExpandTree) HasTuple() bool` -HasSubjectId returns a boolean if a field has been set. - -### GetSubjectSet - -`func (o *ExpandTree) GetSubjectSet() SubjectSet` - -GetSubjectSet returns the SubjectSet field if non-nil, zero value otherwise. - -### GetSubjectSetOk - -`func (o *ExpandTree) GetSubjectSetOk() (*SubjectSet, bool)` - -GetSubjectSetOk returns a tuple with the SubjectSet field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetSubjectSet - -`func (o *ExpandTree) SetSubjectSet(v SubjectSet)` - -SetSubjectSet sets SubjectSet field to given value. - -### HasSubjectSet - -`func (o *ExpandTree) HasSubjectSet() bool` - -HasSubjectSet returns a boolean if a field has been set. +HasTuple returns a boolean if a field has been set. ### GetType diff --git a/internal/httpclient-next/model_expand_tree.go b/internal/httpclient-next/model_expand_tree.go index 961d8ca4c..2cfe26b3e 100644 --- a/internal/httpclient-next/model_expand_tree.go +++ b/internal/httpclient-next/model_expand_tree.go @@ -18,11 +18,9 @@ import ( // ExpandTree struct for ExpandTree type ExpandTree struct { // The children of the node, possibly none. - Children []ExpandTree `json:"children,omitempty"` - // The subject ID the node represents. Either this field, or SubjectSet are set. - SubjectId *string `json:"subject_id,omitempty"` - SubjectSet *SubjectSet `json:"subject_set,omitempty"` - // The type of the node. union Union exclusion Exclusion intersection Intersection leaf Leaf unspecified Unspecified + Children []ExpandTree `json:"children,omitempty"` + Tuple *RelationTuple `json:"tuple,omitempty"` + // The type of the node. union TreeNodeUnion exclusion TreeNodeExclusion intersection TreeNodeIntersection leaf TreeNodeLeaf tuple_to_userset TreeNodeTupeToUserset computed_userset TreeNodeComputedUserset not TreeNodeNot unspecified TreeNodeUnspecified Type string `json:"type"` } @@ -76,68 +74,36 @@ func (o *ExpandTree) SetChildren(v []ExpandTree) { o.Children = v } -// GetSubjectId returns the SubjectId field value if set, zero value otherwise. -func (o *ExpandTree) GetSubjectId() string { - if o == nil || o.SubjectId == nil { - var ret string +// GetTuple returns the Tuple field value if set, zero value otherwise. +func (o *ExpandTree) GetTuple() RelationTuple { + if o == nil || o.Tuple == nil { + var ret RelationTuple return ret } - return *o.SubjectId + return *o.Tuple } -// GetSubjectIdOk returns a tuple with the SubjectId field value if set, nil otherwise +// GetTupleOk returns a tuple with the Tuple field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *ExpandTree) GetSubjectIdOk() (*string, bool) { - if o == nil || o.SubjectId == nil { +func (o *ExpandTree) GetTupleOk() (*RelationTuple, bool) { + if o == nil || o.Tuple == nil { return nil, false } - return o.SubjectId, true + return o.Tuple, true } -// HasSubjectId returns a boolean if a field has been set. -func (o *ExpandTree) HasSubjectId() bool { - if o != nil && o.SubjectId != nil { +// HasTuple returns a boolean if a field has been set. +func (o *ExpandTree) HasTuple() bool { + if o != nil && o.Tuple != nil { return true } return false } -// SetSubjectId gets a reference to the given string and assigns it to the SubjectId field. -func (o *ExpandTree) SetSubjectId(v string) { - o.SubjectId = &v -} - -// GetSubjectSet returns the SubjectSet field value if set, zero value otherwise. -func (o *ExpandTree) GetSubjectSet() SubjectSet { - if o == nil || o.SubjectSet == nil { - var ret SubjectSet - return ret - } - return *o.SubjectSet -} - -// GetSubjectSetOk returns a tuple with the SubjectSet field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ExpandTree) GetSubjectSetOk() (*SubjectSet, bool) { - if o == nil || o.SubjectSet == nil { - return nil, false - } - return o.SubjectSet, true -} - -// HasSubjectSet returns a boolean if a field has been set. -func (o *ExpandTree) HasSubjectSet() bool { - if o != nil && o.SubjectSet != nil { - return true - } - - return false -} - -// SetSubjectSet gets a reference to the given SubjectSet and assigns it to the SubjectSet field. -func (o *ExpandTree) SetSubjectSet(v SubjectSet) { - o.SubjectSet = &v +// SetTuple gets a reference to the given RelationTuple and assigns it to the Tuple field. +func (o *ExpandTree) SetTuple(v RelationTuple) { + o.Tuple = &v } // GetType returns the Type field value @@ -169,11 +135,8 @@ func (o ExpandTree) MarshalJSON() ([]byte, error) { if o.Children != nil { toSerialize["children"] = o.Children } - if o.SubjectId != nil { - toSerialize["subject_id"] = o.SubjectId - } - if o.SubjectSet != nil { - toSerialize["subject_set"] = o.SubjectSet + if o.Tuple != nil { + toSerialize["tuple"] = o.Tuple } if true { toSerialize["type"] = o.Type diff --git a/internal/httpclient/client/write/delete_relation_tuples_responses.go b/internal/httpclient/client/write/delete_relation_tuples_responses.go index 587ed9e3e..b271c74bb 100644 --- a/internal/httpclient/client/write/delete_relation_tuples_responses.go +++ b/internal/httpclient/client/write/delete_relation_tuples_responses.go @@ -53,7 +53,7 @@ func NewDeleteRelationTuplesNoContent() *DeleteRelationTuplesNoContent { /* DeleteRelationTuplesNoContent describes a response with status code 204, with default header values. -An empty response +Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201. */ type DeleteRelationTuplesNoContent struct { } diff --git a/internal/httpclient/client/write/patch_relation_tuples_responses.go b/internal/httpclient/client/write/patch_relation_tuples_responses.go index d804a94d5..5bea532a3 100644 --- a/internal/httpclient/client/write/patch_relation_tuples_responses.go +++ b/internal/httpclient/client/write/patch_relation_tuples_responses.go @@ -59,7 +59,7 @@ func NewPatchRelationTuplesNoContent() *PatchRelationTuplesNoContent { /* PatchRelationTuplesNoContent describes a response with status code 204, with default header values. -An empty response +Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201. */ type PatchRelationTuplesNoContent struct { } diff --git a/internal/httpclient/models/expand_tree.go b/internal/httpclient/models/expand_tree.go index 68f35ee43..b454db2f7 100644 --- a/internal/httpclient/models/expand_tree.go +++ b/internal/httpclient/models/expand_tree.go @@ -24,20 +24,20 @@ type ExpandTree struct { // The children of the node, possibly none. Children []*ExpandTree `json:"children"` - // The subject ID the node represents. Either this field, or SubjectSet are set. - SubjectID string `json:"subject_id,omitempty"` - - // subject set - SubjectSet *SubjectSet `json:"subject_set,omitempty"` + // tuple + Tuple *RelationTuple `json:"tuple,omitempty"` // The type of the node. - // union Union - // exclusion Exclusion - // intersection Intersection - // leaf Leaf - // unspecified Unspecified + // union TreeNodeUnion + // exclusion TreeNodeExclusion + // intersection TreeNodeIntersection + // leaf TreeNodeLeaf + // tuple_to_userset TreeNodeTupeToUserset + // computed_userset TreeNodeComputedUserset + // not TreeNodeNot + // unspecified TreeNodeUnspecified // Required: true - // Enum: [union exclusion intersection leaf unspecified] + // Enum: [union exclusion intersection leaf tuple_to_userset computed_userset not unspecified] Type *string `json:"type"` } @@ -49,7 +49,7 @@ func (m *ExpandTree) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateSubjectSet(formats); err != nil { + if err := m.validateTuple(formats); err != nil { res = append(res, err) } @@ -89,17 +89,17 @@ func (m *ExpandTree) validateChildren(formats strfmt.Registry) error { return nil } -func (m *ExpandTree) validateSubjectSet(formats strfmt.Registry) error { - if swag.IsZero(m.SubjectSet) { // not required +func (m *ExpandTree) validateTuple(formats strfmt.Registry) error { + if swag.IsZero(m.Tuple) { // not required return nil } - if m.SubjectSet != nil { - if err := m.SubjectSet.Validate(formats); err != nil { + if m.Tuple != nil { + if err := m.Tuple.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject_set") + return ve.ValidateName("tuple") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("subject_set") + return ce.ValidateName("tuple") } return err } @@ -112,7 +112,7 @@ var expandTreeTypeTypePropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["union","exclusion","intersection","leaf","unspecified"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["union","exclusion","intersection","leaf","tuple_to_userset","computed_userset","not","unspecified"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -134,6 +134,15 @@ const ( // ExpandTreeTypeLeaf captures enum value "leaf" ExpandTreeTypeLeaf string = "leaf" + // ExpandTreeTypeTupleToUserset captures enum value "tuple_to_userset" + ExpandTreeTypeTupleToUserset string = "tuple_to_userset" + + // ExpandTreeTypeComputedUserset captures enum value "computed_userset" + ExpandTreeTypeComputedUserset string = "computed_userset" + + // ExpandTreeTypeNot captures enum value "not" + ExpandTreeTypeNot string = "not" + // ExpandTreeTypeUnspecified captures enum value "unspecified" ExpandTreeTypeUnspecified string = "unspecified" ) @@ -168,7 +177,7 @@ func (m *ExpandTree) ContextValidate(ctx context.Context, formats strfmt.Registr res = append(res, err) } - if err := m.contextValidateSubjectSet(ctx, formats); err != nil { + if err := m.contextValidateTuple(ctx, formats); err != nil { res = append(res, err) } @@ -198,14 +207,14 @@ func (m *ExpandTree) contextValidateChildren(ctx context.Context, formats strfmt return nil } -func (m *ExpandTree) contextValidateSubjectSet(ctx context.Context, formats strfmt.Registry) error { +func (m *ExpandTree) contextValidateTuple(ctx context.Context, formats strfmt.Registry) error { - if m.SubjectSet != nil { - if err := m.SubjectSet.ContextValidate(ctx, formats); err != nil { + if m.Tuple != nil { + if err := m.Tuple.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject_set") + return ve.ValidateName("tuple") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("subject_set") + return ce.ValidateName("tuple") } return err } diff --git a/internal/relationtuple/definitions.go b/internal/relationtuple/definitions.go index b7e38abc5..7f7d84306 100644 --- a/internal/relationtuple/definitions.go +++ b/internal/relationtuple/definitions.go @@ -2,6 +2,7 @@ package relationtuple import ( "context" + "fmt" "sync" "testing" @@ -41,6 +42,7 @@ type ( Subject interface { Equals(Subject) bool UniqueID() uuid.UUID + String() string } RelationTuple struct { Namespace string `json:"namespace"` @@ -54,10 +56,12 @@ type ( Object uuid.UUID `json:"object"` Relation string `json:"relation"` } + + // TODO(hperl): Also use a ketoapi.Tree here. Tree struct { - Type ketoapi.ExpandNodeType `json:"type"` - Subject Subject `json:"subject"` - Children []*Tree `json:"children,omitempty"` + Type ketoapi.TreeNodeType `json:"type"` + Subject Subject `json:"subject"` + Children []*Tree `json:"children,omitempty"` } ) @@ -73,9 +77,8 @@ func (s *SubjectID) Equals(other Subject) bool { return uv.ID == s.ID } -func (s *SubjectID) UniqueID() uuid.UUID { - return s.ID -} +func (s *SubjectID) UniqueID() uuid.UUID { return s.ID } +func (s *SubjectID) String() string { return s.ID.String() } func (s *SubjectSet) Equals(other Subject) bool { uv, ok := other.(*SubjectSet) @@ -89,6 +92,10 @@ func (s *SubjectSet) UniqueID() uuid.UUID { return uuid.NewV5(s.Object, s.Namespace+"-"+s.Relation) } +func (s *SubjectSet) String() string { + return fmt.Sprintf("%s:%s#%s", s.Namespace, s.Object, s.Relation) +} + func (t *RelationTuple) ToQuery() *RelationQuery { return &RelationQuery{ Namespace: &t.Namespace, @@ -98,6 +105,22 @@ func (t *RelationTuple) ToQuery() *RelationQuery { } } +func (t *RelationTuple) String() string { + if t == nil { + return "" + } + return fmt.Sprintf("%s:%s#%s@%s", t.Namespace, t.Object, t.Relation, t.Subject) +} + +func (t *RelationTuple) FromProto(proto *rts.RelationTuple) *RelationTuple { + // TODO(hperl) + return t +} +func (t *RelationTuple) ToProto() *rts.RelationTuple { + // TODO(hperl) + return &rts.RelationTuple{} +} + type ManagerWrapper struct { Reg ManagerProvider PageOpts []x.PaginationOptionSetter diff --git a/internal/relationtuple/manager_isolation.go b/internal/relationtuple/manager_isolation.go index 512f7c4ce..7080238df 100644 --- a/internal/relationtuple/manager_isolation.go +++ b/internal/relationtuple/manager_isolation.go @@ -122,7 +122,7 @@ func IsolationTest(t *testing.T, m0, m1 Manager) { cancel() - _, _, err := m0.GetRelationTuples(ctx, &RelationQuery{Namespace: nspace}) + _, _, err := m0.GetRelationTuples(ctx, &RelationQuery{Namespace: &nspace}) assert.ErrorIs(t, err, context.Canceled) }) }) diff --git a/internal/relationtuple/manager_requirements.go b/internal/relationtuple/manager_requirements.go index c115e972c..cc74c240f 100644 --- a/internal/relationtuple/manager_requirements.go +++ b/internal/relationtuple/manager_requirements.go @@ -22,7 +22,7 @@ func ManagerTest(t *testing.T, m Manager) { t.Run("method=Write", func(t *testing.T) { t.Run("case=success", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint tuples := []*RelationTuple{ { @@ -56,7 +56,7 @@ func ManagerTest(t *testing.T, m Manager) { t.Run("method=Get", func(t *testing.T) { t.Run("case=queries", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint tuples := make([]*RelationTuple, 10) ids := x.UUIDs(len(tuples)) @@ -169,7 +169,7 @@ func ManagerTest(t *testing.T, m Manager) { }) t.Run("case=pagination", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint tuples := make([]*RelationTuple, 20) oID := uuid.Must(uuid.NewV4()) @@ -241,7 +241,7 @@ func ManagerTest(t *testing.T, m Manager) { t.Run("method=Delete", func(t *testing.T) { t.Run("case=deletes tuple", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint oID := uuid.Must(uuid.NewV4()) sID := uuid.Must(uuid.NewV4()) @@ -284,7 +284,7 @@ func ManagerTest(t *testing.T, m Manager) { }) t.Run("case=deletes only one tuple", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint rs := make([]*RelationTuple, 4) oIDs, sIDs := make([]uuid.UUID, len(rs)), make([]uuid.UUID, len(rs)) @@ -352,7 +352,7 @@ func ManagerTest(t *testing.T, m Manager) { t.Run("method=Transact", func(t *testing.T) { t.Run("case=success", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint rs := make([]*RelationTuple, 4) oIDs, sIDs := make([]uuid.UUID, len(rs)), make([]uuid.UUID, len(rs)) @@ -390,7 +390,7 @@ func ManagerTest(t *testing.T, m Manager) { }) t.Run("case=err rolls back all", func(t *testing.T) { - nspace := strconv.Itoa(rand.Int()) + nspace := strconv.Itoa(rand.Int()) // nolint rs := make([]*RelationTuple, 2) oIDs, sIDs := make([]uuid.UUID, len(rs)), make([]uuid.UUID, len(rs)) diff --git a/internal/relationtuple/read_server.go b/internal/relationtuple/read_server.go index 4160b0c54..b158346b2 100644 --- a/internal/relationtuple/read_server.go +++ b/internal/relationtuple/read_server.go @@ -68,8 +68,8 @@ func (h *handler) ListRelationTuples(ctx context.Context, req *rts.ListRelationT switch { case req.RelationQuery != nil: q.FromDataProvider(&queryWrapper{req.RelationQuery}) - case req.Query != nil: - q.FromDataProvider(&deprecatedQueryWrapper{req.Query}) + case req.Query != nil: // nolint + q.FromDataProvider(&deprecatedQueryWrapper{req.Query}) // nolint default: return nil, herodot.ErrBadRequest.WithError("you must provide a query") } diff --git a/internal/relationtuple/read_server_test.go b/internal/relationtuple/read_server_test.go index 7cba75e1d..2ee61d3b2 100644 --- a/internal/relationtuple/read_server_test.go +++ b/internal/relationtuple/read_server_test.go @@ -219,17 +219,17 @@ func TestReadHandlers(t *testing.T) { } withDeprecatedQuery := func(req *rts.ListRelationTuplesRequest, query *ketoapi.RelationQuery) { pq := query.ToProto() - req.Query = &rts.ListRelationTuplesRequest_Query{ + req.Query = &rts.ListRelationTuplesRequest_Query{ // nolint Subject: pq.Subject, } if pq.Namespace != nil { - req.Query.Namespace = *pq.Namespace + req.Query.Namespace = *pq.Namespace // nolint } if pq.Object != nil { - req.Query.Object = *pq.Object + req.Query.Object = *pq.Object // nolint } if pq.Relation != nil { - req.Query.Relation = *pq.Relation + req.Query.Relation = *pq.Relation // nolint } } apiTuplesFromProto := func(t *testing.T, pts ...*rts.RelationTuple) []*ketoapi.RelationTuple { @@ -241,11 +241,11 @@ func TestReadHandlers(t *testing.T) { } return actual } - soc, err := net.Listen("tcp", ":0") + soc, err := net.Listen("tcp", ":0") // nolint require.NoError(t, err) srv := grpc.NewServer() h.RegisterReadGRPC(srv) - go srv.Serve(soc) + go srv.Serve(soc) // nolint t.Cleanup(srv.Stop) con, err := grpc.Dial(soc.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) diff --git a/internal/relationtuple/transact_server.go b/internal/relationtuple/transact_server.go index f45d91a83..ae831072b 100644 --- a/internal/relationtuple/transact_server.go +++ b/internal/relationtuple/transact_server.go @@ -67,8 +67,8 @@ func (h *handler) DeleteRelationTuples(ctx context.Context, req *rts.DeleteRelat switch { case req.RelationQuery != nil: q.FromDataProvider(&queryWrapper{req.RelationQuery}) - case req.Query != nil: - q.FromDataProvider(&deprecatedQueryWrapper{(*rts.ListRelationTuplesRequest_Query)(req.Query)}) + case req.Query != nil: // nolint + q.FromDataProvider(&deprecatedQueryWrapper{(*rts.ListRelationTuplesRequest_Query)(req.Query)}) // nolint default: return nil, errors.WithStack(herodot.ErrBadRequest.WithReason("invalid request")) } diff --git a/internal/relationtuple/uuid_mapping.go b/internal/relationtuple/uuid_mapping.go index cc1a9a82f..8eb8659d1 100644 --- a/internal/relationtuple/uuid_mapping.go +++ b/internal/relationtuple/uuid_mapping.go @@ -304,14 +304,15 @@ func (m *Mapper) FromSubjectSet(ctx context.Context, set *ketoapi.SubjectSet) (* }, nil } -func (m *Mapper) ToTree(ctx context.Context, tree *Tree) (res *ketoapi.ExpandTree, err error) { +func (m *Mapper) ToTree(ctx context.Context, tree *Tree) (res *ketoapi.Tree[*ketoapi.RelationTuple], err error) { onSuccess := newSuccess(&err) defer onSuccess.apply() var s []string var u []uuid.UUID - res = &ketoapi.ExpandTree{ - Type: tree.Type, + res = &ketoapi.Tree[*ketoapi.RelationTuple]{ + Type: tree.Type, + Tuple: &ketoapi.RelationTuple{}, } nm, err := m.D.Config(ctx).NamespaceManager() @@ -327,7 +328,7 @@ func (m *Mapper) ToTree(ctx context.Context, tree *Tree) (res *ketoapi.ExpandTre return nil, err } onSuccess.do(func() { - res.SubjectSet = &ketoapi.SubjectSet{ + res.Tuple.SubjectSet = &ketoapi.SubjectSet{ Namespace: n.Name, Object: s[0], Relation: sub.Relation, @@ -336,7 +337,7 @@ func (m *Mapper) ToTree(ctx context.Context, tree *Tree) (res *ketoapi.ExpandTre case *SubjectID: u = append(u, sub.ID) onSuccess.do(func() { - res.SubjectID = x.Ptr(s[0]) + res.Tuple.SubjectID = x.Ptr(s[0]) }) } for _, c := range tree.Children { diff --git a/internal/relationtuple/uuid_mapping_test.go b/internal/relationtuple/uuid_mapping_test.go index c21805366..7e8b678fa 100644 --- a/internal/relationtuple/uuid_mapping_test.go +++ b/internal/relationtuple/uuid_mapping_test.go @@ -210,14 +210,14 @@ func TestMapper(t *testing.T) { { name: "basic tree", tree: &relationtuple.Tree{ - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuids[0]}, }, }, { name: "basic tree with children", tree: &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Namespace: nspace.Name, Object: uuids[0], @@ -225,11 +225,11 @@ func TestMapper(t *testing.T) { }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuids[1]}, }, { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuids[2]}, }, }, @@ -238,7 +238,7 @@ func TestMapper(t *testing.T) { { name: "deeply nested tree", tree: &relationtuple.Tree{ - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Namespace: nspace.Name, Object: uuids[0], @@ -246,7 +246,7 @@ func TestMapper(t *testing.T) { }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeUnion, + Type: ketoapi.TreeNodeUnion, Subject: &relationtuple.SubjectSet{ Namespace: nspace.Name, Object: uuids[1], @@ -254,7 +254,7 @@ func TestMapper(t *testing.T) { }, Children: []*relationtuple.Tree{ { - Type: ketoapi.ExpandNodeLeaf, + Type: ketoapi.TreeNodeLeaf, Subject: &relationtuple.SubjectID{ID: uuids[2]}, }, }, @@ -271,19 +271,19 @@ func TestMapper(t *testing.T) { return } - var checkTree func(*ketoapi.ExpandTree, *relationtuple.Tree) - checkTree = func(mapped *ketoapi.ExpandTree, original *relationtuple.Tree) { + var checkTree func(*ketoapi.Tree[*ketoapi.RelationTuple], *relationtuple.Tree) + checkTree = func(mapped *ketoapi.Tree[*ketoapi.RelationTuple], original *relationtuple.Tree) { switch s := original.Subject.(type) { case *relationtuple.SubjectID: - require.NotNil(t, mapped.SubjectID) - assert.Nil(t, mapped.SubjectSet) - assert.Equal(t, strs[slices.Index(uuids, s.ID)], *mapped.SubjectID) + require.NotNil(t, mapped.Tuple.SubjectID) + assert.Nil(t, mapped.Tuple.SubjectSet) + assert.Equal(t, strs[slices.Index(uuids, s.ID)], *mapped.Tuple.SubjectID) case *relationtuple.SubjectSet: - require.NotNil(t, mapped.SubjectSet) - assert.Nil(t, mapped.SubjectID) - assert.Equal(t, nspace.Name, mapped.SubjectSet.Namespace) - assert.Equal(t, strs[slices.Index(uuids, s.Object)], mapped.SubjectSet.Object) - assert.Equal(t, s.Relation, mapped.SubjectSet.Relation) + require.NotNil(t, mapped.Tuple.SubjectSet) + assert.Nil(t, mapped.Tuple.SubjectID) + assert.Equal(t, nspace.Name, mapped.Tuple.SubjectSet.Namespace) + assert.Equal(t, strs[slices.Index(uuids, s.Object)], mapped.Tuple.SubjectSet.Object) + assert.Equal(t, s.Relation, mapped.Tuple.SubjectSet.Relation) default: t.Fatalf("expected subject to be set: %+v", mapped) } diff --git a/internal/x/graph/graph_utils_test.go b/internal/x/graph/graph_utils_test.go index 2b2947629..2b732c98c 100644 --- a/internal/x/graph/graph_utils_test.go +++ b/internal/x/graph/graph_utils_test.go @@ -40,7 +40,7 @@ func TestEngineUtilsProvider_CheckVisited(t *testing.T) { ctx := context.Background() var isThereACycle bool for i := range linkedList { - ctx, isThereACycle = CheckAndAddVisited(ctx, linkedList[i].UniqueID()) + ctx, isThereACycle = CheckAndAddVisited(ctx, &linkedList[i]) if isThereACycle { break } @@ -75,7 +75,7 @@ func TestEngineUtilsProvider_CheckVisited(t *testing.T) { ctx := context.Background() var isThereACycle bool for i := range list { - ctx, isThereACycle = CheckAndAddVisited(ctx, list[i].UniqueID()) + ctx, isThereACycle = CheckAndAddVisited(ctx, &list[i]) if isThereACycle { break } @@ -85,16 +85,21 @@ func TestEngineUtilsProvider_CheckVisited(t *testing.T) { }) t.Run("case=no race condition during adding", func(t *testing.T) { + racyObj := uuid.Must(uuid.NewV4()) + otherObj := uuid.Must(uuid.NewV4()) // we repeat this test a few times to ensure we don't have a race condition // the race detector alone was not able to catch it for i := 0; i < 500; i++ { subject := &relationtuple.SubjectSet{ Namespace: "default", - Object: "racy", + Object: racyObj, Relation: "connected", } - ctx, _ := CheckAndAddVisited(context.Background(), &relationtuple.SubjectSet{Object: "just to setup the context"}) + ctx, _ := CheckAndAddVisited( + context.Background(), + &relationtuple.SubjectSet{Object: otherObj}, + ) var wg sync.WaitGroup var aCycle, bCycle bool var aCtx, bCtx context.Context diff --git a/ketoapi/enc_proto.go b/ketoapi/enc_proto.go index 01ee75b16..727fca206 100644 --- a/ketoapi/enc_proto.go +++ b/ketoapi/enc_proto.go @@ -1,9 +1,10 @@ package ketoapi import ( + "github.com/pkg/errors" + "github.com/ory/keto/internal/x" rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" - "github.com/pkg/errors" ) type ( @@ -56,6 +57,26 @@ func (r *RelationTuple) ToProto() *rts.RelationTuple { return res } +func (r *RelationTuple) FromProto(proto *rts.RelationTuple) *RelationTuple { + r = &RelationTuple{ + Namespace: proto.Namespace, + Object: proto.Object, + Relation: proto.Relation, + } + switch subject := proto.Subject.Ref.(type) { + case *rts.Subject_Id: + r.SubjectID = x.Ptr(subject.Id) + case *rts.Subject_Set: + r.SubjectSet = &SubjectSet{ + Namespace: subject.Set.Namespace, + Object: subject.Set.Object, + Relation: subject.Set.Relation, + } + } + + return r +} + func (q *RelationQuery) FromDataProvider(d queryData) *RelationQuery { q.Namespace = d.GetNamespace() q.Object = d.GetObject() @@ -92,39 +113,44 @@ func (q *RelationQuery) ToProto() *rts.RelationQuery { return res } -func (t *ExpandTree) ToProto() *rts.SubjectTree { +func (t *Tree[NodeT]) ToProto() *rts.SubjectTree { res := &rts.SubjectTree{ NodeType: t.Type.ToProto(), Children: make([]*rts.SubjectTree, len(t.Children)), } - if t.SubjectID != nil { - res.Subject = rts.NewSubjectID(*t.SubjectID) - } else { - res.Subject = rts.NewSubjectSet(t.SubjectSet.Namespace, t.SubjectSet.Object, t.SubjectSet.Relation) - } + res.Tuple = t.Tuple.ToProto() + // nolint - fill deprecated field + res.Subject = res.Tuple.Subject for i := range t.Children { res.Children[i] = t.Children[i].ToProto() } return res } -func (t *ExpandTree) FromProto(pt *rts.SubjectTree) *ExpandTree { - t.Type = ExpandNodeType("").FromProto(pt.NodeType) +func TreeFromProto[T Tuple[T]](pt *rts.SubjectTree) *Tree[T] { + t := new(Tree[T]) + t.Type = TreeNodeType("").FromProto(pt.NodeType) - switch sub := pt.Subject.Ref.(type) { - case *rts.Subject_Id: - t.SubjectID = x.Ptr(sub.Id) - case *rts.Subject_Set: - t.SubjectSet = &SubjectSet{ - Namespace: sub.Set.Namespace, - Object: sub.Set.Object, - Relation: sub.Set.Relation, + var tuple T + if pt.Tuple == nil { + // legacy case: fetch from deprecated fields + // nolint + switch sub := pt.Subject.Ref.(type) { + case *rts.Subject_Id: + pt.Tuple.Subject = rts.NewSubjectID(sub.Id) + case *rts.Subject_Set: + pt.Tuple.Subject = rts.NewSubjectSet( + sub.Set.Namespace, + sub.Set.Object, + sub.Set.Relation, + ) } } + t.Tuple = tuple.FromProto(pt.Tuple) - t.Children = make([]*ExpandTree, len(pt.Children)) + t.Children = make([]*Tree[T], len(pt.Children)) for i := range pt.Children { - t.Children[i] = (&ExpandTree{}).FromProto(pt.Children[i]) + t.Children[i] = TreeFromProto[T](pt.Children[i]) } return t diff --git a/ketoapi/enc_string.go b/ketoapi/enc_string.go index 79d2c7762..8ae71a282 100644 --- a/ketoapi/enc_string.go +++ b/ketoapi/enc_string.go @@ -11,6 +11,9 @@ import ( var ErrMalformedInput = herodot.ErrBadRequest.WithError("malformed string input") func (r *RelationTuple) String() string { + if r == nil { + return "" + } sb := strings.Builder{} sb.WriteString(r.Namespace) sb.WriteRune(':') @@ -35,6 +38,10 @@ func (r *RelationTuple) String() string { return sb.String() } +func FromString(s string) (*RelationTuple, error) { + return (&RelationTuple{}).FromString(s) +} + func (r *RelationTuple) FromString(s string) (*RelationTuple, error) { var ( objectAndRelationAndSubject string @@ -90,28 +97,3 @@ func (s *SubjectSet) FromString(str string) (*SubjectSet, error) { Relation: relation, }, nil } - -func (t *ExpandTree) String() string { - if t == nil { - return "" - } - - sub := "" - switch { - case t.SubjectID != nil: - sub = *t.SubjectID - case t.SubjectSet != nil: - sub = t.SubjectSet.String() - } - - if t.Type == ExpandNodeLeaf { - return fmt.Sprintf("☘ %s️", sub) - } - - children := make([]string, len(t.Children)) - for i, c := range t.Children { - children[i] = strings.Join(strings.Split(c.String(), "\n"), "\n│ ") - } - - return fmt.Sprintf("∪ %s\n├─ %s", sub, strings.Join(children, "\n├─ ")) -} diff --git a/ketoapi/enc_test.go b/ketoapi/enc_test.go index 944e25a59..a3ee096db 100644 --- a/ketoapi/enc_test.go +++ b/ketoapi/enc_test.go @@ -4,12 +4,14 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ory/keto/internal/x" - rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "net/url" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/keto/internal/x" + rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" ) func TestRelationTuple(t *testing.T) { diff --git a/ketoapi/enc_url_query.go b/ketoapi/enc_url_query.go index 929d2f47c..e458dbc29 100644 --- a/ketoapi/enc_url_query.go +++ b/ketoapi/enc_url_query.go @@ -1,10 +1,12 @@ package ketoapi import ( - "github.com/ory/keto/internal/x" + "net/url" + "github.com/ory/x/pointerx" "github.com/pkg/errors" - "net/url" + + "github.com/ory/keto/internal/x" ) func (q *RelationQuery) FromURLQuery(query url.Values) (*RelationQuery, error) { diff --git a/ketoapi/public_api_definitions.go b/ketoapi/public_api_definitions.go index a10c49ad9..6a89d17a7 100644 --- a/ketoapi/public_api_definitions.go +++ b/ketoapi/public_api_definitions.go @@ -2,8 +2,8 @@ package ketoapi import ( "errors" + "github.com/ory/herodot" - rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" "github.com/sirupsen/logrus" ) @@ -126,74 +126,4 @@ func (r *RelationTuple) ToLoggerFields() logrus.Fields { } // swagger:enum ExpandNodeType -type ExpandNodeType string - -const ( - ExpandNodeUnion ExpandNodeType = "union" - ExpandNodeExclusion ExpandNodeType = "exclusion" - ExpandNodeIntersection ExpandNodeType = "intersection" - ExpandNodeLeaf ExpandNodeType = "leaf" - ExpandNodeUnspecified ExpandNodeType = "unspecified" -) - -// swagger:model expandTree -type ExpandTree struct { - // The type of the node. - // - // required: true - Type ExpandNodeType `json:"type"` - // The children of the node, possibly none. - Children []*ExpandTree `json:"children,omitempty"` - // The subject set the node represents. Either this field, or SubjectID are set. - SubjectSet *SubjectSet `json:"subject_set,omitempty"` - // The subject ID the node represents. Either this field, or SubjectSet are set. - SubjectID *string `json:"subject_id,omitempty"` -} - -func (t ExpandNodeType) String() string { - return string(t) -} - -func (t *ExpandNodeType) UnmarshalJSON(v []byte) error { - switch string(v) { - case `"union"`: - *t = ExpandNodeUnion - case `"exclusion"`: - *t = ExpandNodeExclusion - case `"intersection"`: - *t = ExpandNodeIntersection - case `"leaf"`: - *t = ExpandNodeLeaf - default: - return ErrUnknownNodeType - } - return nil -} - -func (t ExpandNodeType) ToProto() rts.NodeType { - switch t { - case ExpandNodeLeaf: - return rts.NodeType_NODE_TYPE_LEAF - case ExpandNodeUnion: - return rts.NodeType_NODE_TYPE_UNION - case ExpandNodeExclusion: - return rts.NodeType_NODE_TYPE_EXCLUSION - case ExpandNodeIntersection: - return rts.NodeType_NODE_TYPE_INTERSECTION - } - return rts.NodeType_NODE_TYPE_UNSPECIFIED -} - -func (ExpandNodeType) FromProto(pt rts.NodeType) ExpandNodeType { - switch pt { - case rts.NodeType_NODE_TYPE_LEAF: - return ExpandNodeLeaf - case rts.NodeType_NODE_TYPE_UNION: - return ExpandNodeUnion - case rts.NodeType_NODE_TYPE_EXCLUSION: - return ExpandNodeExclusion - case rts.NodeType_NODE_TYPE_INTERSECTION: - return ExpandNodeIntersection - } - return ExpandNodeUnspecified -} +type ExpandNodeType TreeNodeType diff --git a/ketoapi/tree.go b/ketoapi/tree.go new file mode 100644 index 000000000..e05bd8fe5 --- /dev/null +++ b/ketoapi/tree.go @@ -0,0 +1,172 @@ +package ketoapi + +import ( + "fmt" + "strings" + + rts "github.com/ory/keto/proto/ory/keto/relation_tuples/v1alpha2" +) + +// swagger:enum TreeNodeType +type TreeNodeType string + +const ( + TreeNodeUnion TreeNodeType = "union" + TreeNodeExclusion TreeNodeType = "exclusion" + TreeNodeIntersection TreeNodeType = "intersection" + TreeNodeLeaf TreeNodeType = "leaf" + TreeNodeTupeToUserset TreeNodeType = "tuple_to_userset" + TreeNodeComputedUserset TreeNodeType = "computed_userset" + TreeNodeNot TreeNodeType = "not" + TreeNodeUnspecified TreeNodeType = "unspecified" +) + +type Tuple[T any] interface { + fmt.Stringer + ToProto() *rts.RelationTuple + FromProto(*rts.RelationTuple) T +} + +// Tree is a generic tree of either internal relation tuples (with UUIDs for +// objects, etc.) or API relation tuples (with strings for objects, etc.). +type Tree[T Tuple[T]] struct { + // Propagate all struct changes to `SwaggerOnlyExpandTree` as well. + + // The type of the node. + // + // required: true + Type TreeNodeType `json:"type"` + + // The children of the node, possibly none. + Children []*Tree[T] `json:"children,omitempty"` + + // The relation tuple the node represents. + Tuple T `json:"tuple"` +} + +// IMPORTANT: We need a manual instantiation of the generic Tree[T] for the +// OpenAPI spec, since go-swagger does not understand generics :(. +// This can be fixed by using grpc-gateway. +// +// swagger:model expandTree +type SwaggerOnlyExpandTree struct { // nolint + // The type of the node. + // + // required: true + Type TreeNodeType `json:"type"` + + // The children of the node, possibly none. + Children []*SwaggerOnlyExpandTree `json:"children,omitempty"` + + // The relation tuple the node represents. + Tuple *RelationTuple `json:"tuple"` +} + +func (t TreeNodeType) String() string { + return string(t) +} + +func (t *TreeNodeType) UnmarshalJSON(v []byte) error { + switch string(v) { + case `"union"`: + *t = TreeNodeUnion + case `"exclusion"`: + *t = TreeNodeExclusion + case `"intersection"`: + *t = TreeNodeIntersection + case `"leaf"`: + *t = TreeNodeLeaf + case `"tuple_to_userset"`: + *t = TreeNodeTupeToUserset + case `"computed_userset"`: + *t = TreeNodeComputedUserset + case `"not"`: + *t = TreeNodeNot + case `"unspecified"`: + *t = TreeNodeUnspecified + default: + return ErrUnknownNodeType + } + return nil +} + +func (t TreeNodeType) ToProto() rts.NodeType { + switch t { + case TreeNodeLeaf: + return rts.NodeType_NODE_TYPE_LEAF + case TreeNodeUnion: + return rts.NodeType_NODE_TYPE_UNION + case TreeNodeExclusion: + return rts.NodeType_NODE_TYPE_EXCLUSION + case TreeNodeIntersection: + return rts.NodeType_NODE_TYPE_INTERSECTION + } + return rts.NodeType_NODE_TYPE_UNSPECIFIED +} + +func (TreeNodeType) FromProto(pt rts.NodeType) TreeNodeType { + switch pt { + case rts.NodeType_NODE_TYPE_LEAF: + return TreeNodeLeaf + case rts.NodeType_NODE_TYPE_UNION: + return TreeNodeUnion + case rts.NodeType_NODE_TYPE_EXCLUSION: + return TreeNodeExclusion + case rts.NodeType_NODE_TYPE_INTERSECTION: + return TreeNodeIntersection + } + return TreeNodeUnspecified +} + +func (t *Tree[NodeT]) Label() string { + if t == nil { + return "" + } + + return t.Tuple.String() +} + +func (t *Tree[NodeT]) String() string { + if t == nil { + return "" + } + + nodeLabel := t.Label() + + if t.Type == TreeNodeLeaf { + return fmt.Sprintf("∋ %s️", nodeLabel) + } + + children := make([]string, len(t.Children)) + for i, c := range t.Children { + var indent string + if i == len(t.Children)-1 { + indent = " " + } else { + indent = "│ " + } + children[i] = strings.Join(strings.Split(c.String(), "\n"), "\n"+indent) + } + + setOperation := "" + switch t.Type { + case TreeNodeIntersection: + setOperation = "and" + case TreeNodeUnion: + setOperation = "or" + case TreeNodeExclusion: + setOperation = `\` + case TreeNodeNot: + setOperation = `not` + case TreeNodeTupeToUserset: + setOperation = "┐ tuple to userset" + case TreeNodeComputedUserset: + setOperation = "┐ computed userset" + } + + boxSymbol := "├" + if len(children) == 1 { + boxSymbol = "└" + } + return fmt.Sprintf("%s %s\n%s──%s", setOperation, nodeLabel, boxSymbol, strings.Join(children, "\n└──")) +} diff --git a/proto/ory/keto/relation_tuples/v1alpha2/expand_service.pb.go b/proto/ory/keto/relation_tuples/v1alpha2/expand_service.pb.go index 370d38b0a..17a6a10f9 100644 --- a/proto/ory/keto/relation_tuples/v1alpha2/expand_service.pb.go +++ b/proto/ory/keto/relation_tuples/v1alpha2/expand_service.pb.go @@ -229,7 +229,12 @@ type SubjectTree struct { // The type of the node. NodeType NodeType `protobuf:"varint,1,opt,name=node_type,json=nodeType,proto3,enum=ory.keto.relation_tuples.v1alpha2.NodeType" json:"node_type,omitempty"` // The subject this node represents. + // Deprecated: More information is now available in the tuple field. + // + // Deprecated: Do not use. Subject *Subject `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` + // The relation tuple this node represents. + Tuple *RelationTuple `protobuf:"bytes,4,opt,name=tuple,proto3" json:"tuple,omitempty"` // The children of this node. // // This is never set if `node_type` == `NODE_TYPE_LEAF`. @@ -275,6 +280,7 @@ func (x *SubjectTree) GetNodeType() NodeType { return NodeType_NODE_TYPE_UNSPECIFIED } +// Deprecated: Do not use. func (x *SubjectTree) GetSubject() *Subject { if x != nil { return x.Subject @@ -282,6 +288,13 @@ func (x *SubjectTree) GetSubject() *Subject { return nil } +func (x *SubjectTree) GetTuple() *RelationTuple { + if x != nil { + return x.Tuple + } + return nil +} + func (x *SubjectTree) GetChildren() []*SubjectTree { if x != nil { return x.Children @@ -315,51 +328,56 @@ var file_ory_keto_relation_tuples_v1alpha2_expand_service_proto_rawDesc = []byte 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x22, 0xe9, 0x01, + 0x65, 0x63, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x22, 0xb5, 0x02, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x65, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x6e, - 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x53, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x4a, 0x0a, - 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, - 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x2a, 0x83, 0x01, 0x0a, 0x08, 0x4e, 0x6f, - 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, - 0x4e, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, - 0x1a, 0x0a, 0x16, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, - 0x45, 0x52, 0x53, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x4e, - 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x45, 0x41, 0x46, 0x10, 0x04, 0x32, - 0x7e, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x6d, 0x0a, 0x06, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x12, 0x30, 0x2e, 0x6f, 0x72, 0x79, - 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x45, - 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, - 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, - 0x2e, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0xc3, 0x01, 0x0a, 0x24, 0x73, 0x68, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x42, 0x12, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x72, 0x79, 0x2f, 0x6b, - 0x65, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x72, 0x79, 0x2f, 0x6b, 0x65, - 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, - 0x65, 0x73, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x3b, 0x72, 0x74, 0x73, 0xaa, - 0x02, 0x20, 0x4f, 0x72, 0x79, 0x2e, 0x4b, 0x65, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x32, 0xca, 0x02, 0x20, 0x4f, 0x72, 0x79, 0x5c, 0x4b, 0x65, 0x74, 0x6f, 0x5c, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x5c, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6a, 0x65, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x46, 0x0a, 0x05, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x32, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x52, 0x05, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x12, 0x4a, 0x0a, 0x08, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6f, 0x72, + 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x2a, 0x83, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x4f, 0x4e, + 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x45, 0x58, 0x43, 0x4c, 0x55, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x4e, + 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x53, 0x45, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x4e, 0x4f, 0x44, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x45, 0x41, 0x46, 0x10, 0x04, 0x32, 0x7e, 0x0a, 0x0d, 0x45, + 0x78, 0x70, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, 0x06, + 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x12, 0x30, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, + 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, + 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, + 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2e, 0x45, 0x78, 0x70, + 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xc3, 0x01, 0x0a, 0x24, + 0x73, 0x68, 0x2e, 0x6f, 0x72, 0x79, 0x2e, 0x6b, 0x65, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x32, 0x42, 0x12, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x72, 0x79, 0x2f, 0x6b, 0x65, 0x74, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x72, 0x79, 0x2f, 0x6b, 0x65, 0x74, 0x6f, 0x2f, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x3b, 0x72, 0x74, 0x73, 0xaa, 0x02, 0x20, 0x4f, 0x72, + 0x79, 0x2e, 0x4b, 0x65, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, + 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0xca, 0x02, + 0x20, 0x4f, 0x72, 0x79, 0x5c, 0x4b, 0x65, 0x74, 0x6f, 0x5c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -382,20 +400,22 @@ var file_ory_keto_relation_tuples_v1alpha2_expand_service_proto_goTypes = []inte (*ExpandResponse)(nil), // 2: ory.keto.relation_tuples.v1alpha2.ExpandResponse (*SubjectTree)(nil), // 3: ory.keto.relation_tuples.v1alpha2.SubjectTree (*Subject)(nil), // 4: ory.keto.relation_tuples.v1alpha2.Subject + (*RelationTuple)(nil), // 5: ory.keto.relation_tuples.v1alpha2.RelationTuple } var file_ory_keto_relation_tuples_v1alpha2_expand_service_proto_depIdxs = []int32{ 4, // 0: ory.keto.relation_tuples.v1alpha2.ExpandRequest.subject:type_name -> ory.keto.relation_tuples.v1alpha2.Subject 3, // 1: ory.keto.relation_tuples.v1alpha2.ExpandResponse.tree:type_name -> ory.keto.relation_tuples.v1alpha2.SubjectTree 0, // 2: ory.keto.relation_tuples.v1alpha2.SubjectTree.node_type:type_name -> ory.keto.relation_tuples.v1alpha2.NodeType 4, // 3: ory.keto.relation_tuples.v1alpha2.SubjectTree.subject:type_name -> ory.keto.relation_tuples.v1alpha2.Subject - 3, // 4: ory.keto.relation_tuples.v1alpha2.SubjectTree.children:type_name -> ory.keto.relation_tuples.v1alpha2.SubjectTree - 1, // 5: ory.keto.relation_tuples.v1alpha2.ExpandService.Expand:input_type -> ory.keto.relation_tuples.v1alpha2.ExpandRequest - 2, // 6: ory.keto.relation_tuples.v1alpha2.ExpandService.Expand:output_type -> ory.keto.relation_tuples.v1alpha2.ExpandResponse - 6, // [6:7] is the sub-list for method output_type - 5, // [5:6] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 5, // 4: ory.keto.relation_tuples.v1alpha2.SubjectTree.tuple:type_name -> ory.keto.relation_tuples.v1alpha2.RelationTuple + 3, // 5: ory.keto.relation_tuples.v1alpha2.SubjectTree.children:type_name -> ory.keto.relation_tuples.v1alpha2.SubjectTree + 1, // 6: ory.keto.relation_tuples.v1alpha2.ExpandService.Expand:input_type -> ory.keto.relation_tuples.v1alpha2.ExpandRequest + 2, // 7: ory.keto.relation_tuples.v1alpha2.ExpandService.Expand:output_type -> ory.keto.relation_tuples.v1alpha2.ExpandResponse + 7, // [7:8] is the sub-list for method output_type + 6, // [6:7] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_ory_keto_relation_tuples_v1alpha2_expand_service_proto_init() } diff --git a/proto/ory/keto/relation_tuples/v1alpha2/expand_service.proto b/proto/ory/keto/relation_tuples/v1alpha2/expand_service.proto index d5380f5a8..e0c776bf5 100644 --- a/proto/ory/keto/relation_tuples/v1alpha2/expand_service.proto +++ b/proto/ory/keto/relation_tuples/v1alpha2/expand_service.proto @@ -77,8 +77,14 @@ enum NodeType { message SubjectTree { // The type of the node. NodeType node_type = 1; + // The subject this node represents. - Subject subject = 2; + // Deprecated: More information is now available in the tuple field. + Subject subject = 2 [deprecated = true]; + + // The relation tuple this node represents. + RelationTuple tuple = 4; + // The children of this node. // // This is never set if `node_type` == `NODE_TYPE_LEAF`. diff --git a/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.d.ts b/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.d.ts index bd4b2b3e8..bbc463a9d 100644 --- a/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.d.ts +++ b/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.d.ts @@ -67,6 +67,11 @@ export class SubjectTree extends jspb.Message { clearSubject(): void; getSubject(): ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject | undefined; setSubject(value?: ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject): SubjectTree; + + hasTuple(): boolean; + clearTuple(): void; + getTuple(): ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple | undefined; + setTuple(value?: ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple): SubjectTree; clearChildrenList(): void; getChildrenList(): Array; setChildrenList(value: Array): SubjectTree; @@ -86,6 +91,7 @@ export namespace SubjectTree { export type AsObject = { nodeType: NodeType, subject?: ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject.AsObject, + tuple?: ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple.AsObject, childrenList: Array, } } diff --git a/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.js b/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.js index a2f8a316f..0f87759fb 100644 --- a/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.js +++ b/proto/ory/keto/relation_tuples/v1alpha2/expand_service_pb.js @@ -493,6 +493,7 @@ proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.toObject = function(includeI var f, obj = { nodeType: jspb.Message.getFieldWithDefault(msg, 1, 0), subject: (f = msg.getSubject()) && ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject.toObject(includeInstance, f), + tuple: (f = msg.getTuple()) && ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple.toObject(includeInstance, f), childrenList: jspb.Message.toObjectList(msg.getChildrenList(), proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.toObject, includeInstance) }; @@ -540,6 +541,11 @@ proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.deserializeBinaryFromReader reader.readMessage(value,ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject.deserializeBinaryFromReader); msg.setSubject(value); break; + case 4: + var value = new ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple; + reader.readMessage(value,ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple.deserializeBinaryFromReader); + msg.setTuple(value); + break; case 3: var value = new proto.ory.keto.relation_tuples.v1alpha2.SubjectTree; reader.readMessage(value,proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.deserializeBinaryFromReader); @@ -589,6 +595,14 @@ proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.serializeBinaryToWriter = fu ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.Subject.serializeBinaryToWriter ); } + f = message.getTuple(); + if (f != null) { + writer.writeMessage( + 4, + f, + ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple.serializeBinaryToWriter + ); + } f = message.getChildrenList(); if (f.length > 0) { writer.writeRepeatedMessage( @@ -655,6 +669,43 @@ proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.prototype.hasSubject = funct }; +/** + * optional RelationTuple tuple = 4; + * @return {?proto.ory.keto.relation_tuples.v1alpha2.RelationTuple} + */ +proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.prototype.getTuple = function() { + return /** @type{?proto.ory.keto.relation_tuples.v1alpha2.RelationTuple} */ ( + jspb.Message.getWrapperField(this, ory_keto_relation_tuples_v1alpha2_relation_tuples_pb.RelationTuple, 4)); +}; + + +/** + * @param {?proto.ory.keto.relation_tuples.v1alpha2.RelationTuple|undefined} value + * @return {!proto.ory.keto.relation_tuples.v1alpha2.SubjectTree} returns this +*/ +proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.prototype.setTuple = function(value) { + return jspb.Message.setWrapperField(this, 4, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.ory.keto.relation_tuples.v1alpha2.SubjectTree} returns this + */ +proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.prototype.clearTuple = function() { + return this.setTuple(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.ory.keto.relation_tuples.v1alpha2.SubjectTree.prototype.hasTuple = function() { + return jspb.Message.getField(this, 4) != null; +}; + + /** * repeated SubjectTree children = 3; * @return {!Array} diff --git a/spec/api.json b/spec/api.json index 7248399d8..009fd4df7 100755 --- a/spec/api.json +++ b/spec/api.json @@ -2,7 +2,7 @@ "components": { "responses": { "emptyResponse": { - "description": "An empty response" + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." } }, "schemas": { @@ -19,24 +19,23 @@ }, "type": "array" }, - "subject_id": { - "description": "The subject ID the node represents. Either this field, or SubjectSet are set.", - "type": "string" - }, - "subject_set": { - "$ref": "#/components/schemas/subjectSet" + "tuple": { + "$ref": "#/components/schemas/relationTuple" }, "type": { - "description": "The type of the node.\nunion Union\nexclusion Exclusion\nintersection Intersection\nleaf Leaf\nunspecified Unspecified", + "description": "The type of the node.\nunion TreeNodeUnion\nexclusion TreeNodeExclusion\nintersection TreeNodeIntersection\nleaf TreeNodeLeaf\ntuple_to_userset TreeNodeTupeToUserset\ncomputed_userset TreeNodeComputedUserset\nnot TreeNodeNot\nunspecified TreeNodeUnspecified", "enum": [ "union", "exclusion", "intersection", "leaf", + "tuple_to_userset", + "computed_userset", + "not", "unspecified" ], "type": "string", - "x-go-enum-desc": "union Union\nexclusion Exclusion\nintersection Intersection\nleaf Leaf\nunspecified Unspecified" + "x-go-enum-desc": "union TreeNodeUnion\nexclusion TreeNodeExclusion\nintersection TreeNodeIntersection\nleaf TreeNodeLeaf\ntuple_to_userset TreeNodeTupeToUserset\ncomputed_userset TreeNodeComputedUserset\nnot TreeNodeNot\nunspecified TreeNodeUnspecified" } }, "required": ["type"], diff --git a/spec/swagger.json b/spec/swagger.json index d4a741622..6f1503cc1 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -598,18 +598,23 @@ "$ref": "#/definitions/expandTree" } }, - "subject_id": { - "description": "The subject ID the node represents. Either this field, or SubjectSet are set.", - "type": "string" - }, - "subject_set": { - "$ref": "#/definitions/subjectSet" + "tuple": { + "$ref": "#/definitions/relationTuple" }, "type": { - "description": "The type of the node.\nunion Union\nexclusion Exclusion\nintersection Intersection\nleaf Leaf\nunspecified Unspecified", + "description": "The type of the node.\nunion TreeNodeUnion\nexclusion TreeNodeExclusion\nintersection TreeNodeIntersection\nleaf TreeNodeLeaf\ntuple_to_userset TreeNodeTupeToUserset\ncomputed_userset TreeNodeComputedUserset\nnot TreeNodeNot\nunspecified TreeNodeUnspecified", "type": "string", - "enum": ["union", "exclusion", "intersection", "leaf", "unspecified"], - "x-go-enum-desc": "union Union\nexclusion Exclusion\nintersection Intersection\nleaf Leaf\nunspecified Unspecified" + "enum": [ + "union", + "exclusion", + "intersection", + "leaf", + "tuple_to_userset", + "computed_userset", + "not", + "unspecified" + ], + "x-go-enum-desc": "union TreeNodeUnion\nexclusion TreeNodeExclusion\nintersection TreeNodeIntersection\nleaf TreeNodeLeaf\ntuple_to_userset TreeNodeTupeToUserset\ncomputed_userset TreeNodeComputedUserset\nnot TreeNodeNot\nunspecified TreeNodeUnspecified" } } }, @@ -783,7 +788,7 @@ }, "responses": { "emptyResponse": { - "description": "An empty response" + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." } } }