Skip to content

Commit

Permalink
feat: compare this with ctx.subject (ory#1204)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmmoran committed Feb 7, 2024
1 parent 6b122a1 commit 53b655d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 32 deletions.
1 change: 1 addition & 0 deletions contrib/rewrites-example/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ require (
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
Expand Down
51 changes: 51 additions & 0 deletions internal/check/rewrites.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ func (e *Engine) checkSubjectSetRewrite(
Tuple: *tuple,
Type: ketoapi.TreeNodeNot,
}, e.checkInverted(ctx, tuple, c, restDepth)))
case *ast.SubjectEqualsObject:
checks = append(checks, checkgroup.WithEdge(checkgroup.Edge{
Tuple: *tuple,
Type: ketoapi.TreeNodeLeaf,
}, e.checkSubjectEqualsObject(ctx, tuple, restDepth)))

default:
return checkNotImplemented
Expand Down Expand Up @@ -175,6 +180,11 @@ func (e *Engine) checkInverted(
Tuple: *tuple,
Type: ketoapi.TreeNodeNot,
}, e.checkInverted(ctx, tuple, c, restDepth))
case *ast.SubjectEqualsObject:
check = checkgroup.WithEdge(checkgroup.Edge{
Tuple: *tuple,
Type: ketoapi.TreeNodeLeaf,
}, e.checkSubjectEqualsObject(ctx, tuple, restDepth))

default:
return checkNotImplemented
Expand All @@ -199,6 +209,47 @@ func (e *Engine) checkInverted(
}
}

// checkSubjectEqualsObject rewrites the relation tuple to use the subject-set relation
// instead of the relation from the tuple.
//
// A relation tuple n:obj#original_rel@user is rewritten to
// n:obj#subject-set@user, where the 'subject-set' relation is taken from the
// subjectSet.Relation.
func (e *Engine) checkSubjectEqualsObject(
_ context.Context,
r *relationTuple,
restDepth int,
) checkgroup.CheckFunc {
if restDepth < 0 {
e.d.Logger().Debug("reached max-depth, therefore this query will not be further expanded")
return checkgroup.UnknownMemberFunc
}

e.d.Logger().
WithField("request", r.String()).
Trace("check subject equals object")

var objAsSubj relationtuple.Subject
switch r.Subject.(type) {
case *relationtuple.SubjectSet:
objAsSubj = &relationtuple.SubjectSet{
Namespace: r.Namespace,
Object: r.Object,
}
case *relationtuple.SubjectID:
objAsSubj = &relationtuple.SubjectID{
ID: r.Object,
}
default:
return checkgroup.UnknownMemberFunc
}
if r.Subject.Equals(objAsSubj) {
return checkgroup.IsMemberFunc
}

return checkgroup.NotMemberFunc
}

// checkComputedSubjectSet rewrites the relation tuple to use the subject-set relation
// instead of the relation from the tuple.
//
Expand Down
32 changes: 25 additions & 7 deletions internal/check/rewrites_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,29 @@ var namespaces = []*namespace.Namespace{
{Name: "read",
SubjectSetRewrite: &ast.SubjectSetRewrite{
Children: ast.Children{
&ast.SubjectEqualsObject{},
&ast.ComputedSubjectSet{Relation: "viewer"},
&ast.ComputedSubjectSet{Relation: "owner"}}}},
{Name: "update",
SubjectSetRewrite: &ast.SubjectSetRewrite{
Children: ast.Children{
&ast.SubjectEqualsObject{},
&ast.ComputedSubjectSet{Relation: "owner"}}}},
{Name: "delete",
SubjectSetRewrite: &ast.SubjectSetRewrite{
Operation: ast.OperatorAnd,
Operation: ast.OperatorOr,
Children: ast.Children{
&ast.ComputedSubjectSet{Relation: "owner"},
&ast.TupleToSubjectSet{
Relation: "level",
ComputedSubjectSetRelation: "member"}}}},
&ast.SubjectSetRewrite{
Operation: ast.OperatorAnd,
Children: ast.Children{
&ast.ComputedSubjectSet{Relation: "owner"},
&ast.TupleToSubjectSet{
Relation: "level",
ComputedSubjectSetRelation: "member"},
},
},
&ast.SubjectEqualsObject{},
}}},
}},
{Name: "acl",
Relations: []ast.Relation{
Expand Down Expand Up @@ -192,9 +201,18 @@ func TestUsersetRewrites(t *testing.T) {
query: "resource:topsecret#delete@mark",
expected: checkgroup.ResultIsMember, // mark is both editor and has correct level
expectedPaths: []path{
{"*", "resource:topsecret#delete@mark", "level:superadmin#member@mark"},
{"*", "resource:topsecret#delete@mark", "resource:topsecret#owner@mark", "group:editors#member@mark"},
{"*", "*", "resource:topsecret#delete@mark", "level:superadmin#member@mark"},
{"*", "*", "resource:topsecret#delete@mark", "resource:topsecret#owner@mark", "group:editors#member@mark"},
},
}, {
query: "resource:topsecret#delete@topsecret",
expected: checkgroup.ResultIsMember, // topsecret may delete topsecret
}, {
query: "resource:topsecret#update@topsecret",
expected: checkgroup.ResultIsMember, // topsecret may update topsecret
}, {
query: "resource:topsecret#read@topsecret",
expected: checkgroup.ResultIsMember, // topsecret may read topsecret
}, {
query: "resource:topsecret#update@mike",
expected: checkgroup.ResultIsMember, // mike owns the resource
Expand Down
8 changes: 4 additions & 4 deletions internal/driver/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

"github.com/ory/x/otelx/semconv"

grpcRecovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
grpcRecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

Expand All @@ -32,7 +32,7 @@ import (

"github.com/ory/x/logrusx"

grpcLogrus "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpcLogrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
"github.com/julienschmidt/httprouter"
"github.com/ory/herodot"
"github.com/ory/x/reqlog"
Expand Down Expand Up @@ -457,7 +457,7 @@ func (r *RegistryDefault) unaryInterceptors(ctx context.Context) []grpc.UnarySer
is = append(is, r.defaultUnaryInterceptors...)
is = append(is,
herodot.UnaryErrorUnwrapInterceptor,
grpcLogrus.UnaryServerInterceptor(InterceptorLogger(r.l.Logrus())),
grpcLogrus.UnaryServerInterceptor(r.l.Entry),
r.pmm.UnaryServerInterceptor,
)
if r.sqaService != nil {
Expand All @@ -476,7 +476,7 @@ func (r *RegistryDefault) streamInterceptors(ctx context.Context) []grpc.StreamS
is = append(is, r.defaultStreamInterceptors...)
is = append(is,
herodot.StreamErrorUnwrapInterceptor,
grpcLogrus.StreamServerInterceptor(InterceptorLogger(r.l.Logrus())),
grpcLogrus.StreamServerInterceptor(r.l.Entry),
r.pmm.StreamServerInterceptor,
)
if r.sqaService != nil {
Expand Down
5 changes: 5 additions & 0 deletions internal/namespace/ast/ast_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type (
AsRewrite() *SubjectSetRewrite
}

SubjectEqualsObject struct{}

ComputedSubjectSet struct {
Relation string `json:"relation"`
}
Expand Down Expand Up @@ -69,3 +71,6 @@ func (t *TupleToSubjectSet) AsRewrite() *SubjectSetRewrite {
func (i *InvertResult) AsRewrite() *SubjectSetRewrite {
return &SubjectSetRewrite{Children: []Child{i}}
}
func (e *SubjectEqualsObject) AsRewrite() *SubjectSetRewrite {
return &SubjectSetRewrite{Children: []Child{e}}
}
36 changes: 17 additions & 19 deletions internal/schema/itemtype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions internal/schema/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const (
itemOperatorOr // "||"
itemOperatorNot // "!"
itemOperatorAssign // "="
itemOperatorEquals // "=="
itemOperatorArrow // "=>"
itemOperatorDot // "."
itemOperatorColon // ":"
Expand Down Expand Up @@ -236,6 +237,7 @@ var oneRuneTokens = map[rune]itemType{
}

var multiRuneTokens = map[string]itemType{
"==": itemOperatorEquals,
"=>": itemOperatorArrow,
"||": itemOperatorOr,
"&&": itemOperatorAnd,
Expand Down
7 changes: 6 additions & 1 deletion internal/schema/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,12 @@ func (p *parser) matchPropertyAccess(propertyName any) bool {
func (p *parser) parsePermissionExpression() (child ast.Child) {
var name, verb item

if !p.match("this", ".", &verb) {
switch {
case !p.match("this"):
return
case p.matchIf(is(itemOperatorEquals), "==", "ctx", ".", "subject"):
return &ast.SubjectEqualsObject{}
case !p.match(".", &verb):
return
}
if !p.matchPropertyAccess(&name) {
Expand Down

0 comments on commit 53b655d

Please sign in to comment.