snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
274
internal/relationtuple/definitions.go
Normal file
274
internal/relationtuple/definitions.go
Normal file
@@ -0,0 +1,274 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package relationtuple
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
keysetpagination "github.com/ory/x/pagination/keysetpagination_v2"
|
||||
|
||||
rts "github.com/ory/keto/gen/go/ory/keto/relation_tuples/v1alpha2"
|
||||
"github.com/ory/keto/ketoapi"
|
||||
)
|
||||
|
||||
type (
|
||||
ManagerProvider interface {
|
||||
RelationTupleManager() Manager
|
||||
}
|
||||
|
||||
// SubjectSetType identifies a subject-set pointer by its target namespace and
|
||||
// relation, as declared in OPL. AllowsDirect indicates that OPL permits a
|
||||
// direct subject match for this type.
|
||||
SubjectSetType struct {
|
||||
Namespace string
|
||||
Relation string
|
||||
AllowsDirect bool
|
||||
}
|
||||
|
||||
Manager interface {
|
||||
GetRelationTuples(ctx context.Context, query *RelationQuery, options ...keysetpagination.Option) ([]*RelationTuple, *keysetpagination.Paginator, error)
|
||||
ExistsRelationTuples(ctx context.Context, query *RelationQuery) (bool, error)
|
||||
WriteRelationTuples(ctx context.Context, rs ...*RelationTuple) error
|
||||
DeleteRelationTuples(ctx context.Context, rs ...*RelationTuple) error
|
||||
DeleteAllRelationTuples(ctx context.Context, query *RelationQuery) error
|
||||
TransactRelationTuples(ctx context.Context, insert []*RelationTuple, delete []*RelationTuple) error
|
||||
// TraverseSubjectSetExpansion gets all subject sets for the tuple's
|
||||
// object#relation and checks whether the tuple's subject is a member of
|
||||
// each. When allowedSubjectSets is non-empty, only subject-set pointers
|
||||
// matching the declared (namespace, relation) pairs are considered.
|
||||
TraverseSubjectSetExpansion(ctx context.Context, tuple *RelationTuple, allowedSubjectSets []SubjectSetType) ([]*TraversalResult, error)
|
||||
// FindTupleWithRelations returns the first tuple that matches the given
|
||||
// tuple on namespace, object, and subject and has any of the given
|
||||
// relations, or nil if there is none.
|
||||
FindTupleWithRelations(ctx context.Context, tuple *RelationTuple, relations []string) (*RelationTuple, error)
|
||||
|
||||
// Transaction runs f inside a database transaction. Nested calls reuse
|
||||
// the outer transaction.
|
||||
Transaction(ctx context.Context, f func(ctx context.Context) error) error
|
||||
// WithLatestSnapshot runs f inside a snapshot-isolated read transaction,
|
||||
// ensuring all SQL queries within f see a consistent database state.
|
||||
WithLatestSnapshot(ctx context.Context, f func(ctx context.Context) error) error
|
||||
}
|
||||
SubjectID struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
}
|
||||
RelationQuery struct {
|
||||
Namespace *string `json:"namespace"`
|
||||
Object *uuid.UUID `json:"object"`
|
||||
Relation *string `json:"relation"`
|
||||
Subject Subject `json:"subject_id,omitempty"`
|
||||
}
|
||||
TupleData interface {
|
||||
GetSubject() *rts.Subject
|
||||
GetObject() string
|
||||
GetNamespace() string
|
||||
GetRelation() string
|
||||
}
|
||||
Subject interface {
|
||||
Equals(Subject) bool
|
||||
UniqueID() uuid.UUID
|
||||
String() string
|
||||
}
|
||||
RelationTuple struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Object uuid.UUID `json:"object"`
|
||||
Relation string `json:"relation"`
|
||||
Subject Subject `json:"subject"`
|
||||
}
|
||||
InternalRelationTuples []*RelationTuple
|
||||
SubjectSet struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Object uuid.UUID `json:"object"`
|
||||
Relation string `json:"relation"`
|
||||
}
|
||||
|
||||
// TODO(hperl): Also use a ketoapi.Tree here.
|
||||
Tree struct {
|
||||
Type ketoapi.TreeNodeType `json:"type"`
|
||||
Subject Subject `json:"subject"`
|
||||
Children []*Tree `json:"children,omitempty"`
|
||||
Truncation *Truncation `json:"truncation,omitempty"`
|
||||
}
|
||||
|
||||
Truncation struct {
|
||||
Reason TruncationReason `json:"reason,omitempty"`
|
||||
Cursor *ExpandCursor `json:"cursor,omitempty"`
|
||||
}
|
||||
|
||||
TruncationReason string
|
||||
ExpandCursorKind string
|
||||
ExpandCursor struct {
|
||||
Kind ExpandCursorKind `json:"kind"`
|
||||
SubjectSet *SubjectSet `json:"subject_set,omitempty"`
|
||||
NextPageToken keysetpagination.PageToken `json:"next_page_token,omitempty"`
|
||||
|
||||
// TraverseRelation is the ComputedRelation in TupleToSubjectSet relation
|
||||
TraverseRelation *string `json:"traverse_relation,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
ExpandCursorKindDirect ExpandCursorKind = "direct"
|
||||
ExpandCursorKindTTU ExpandCursorKind = "ttu"
|
||||
)
|
||||
|
||||
type (
|
||||
TraversalResult struct {
|
||||
From *RelationTuple
|
||||
To *RelationTuple
|
||||
Via Traversal
|
||||
Found bool
|
||||
}
|
||||
|
||||
Traversal string
|
||||
)
|
||||
|
||||
const (
|
||||
TraversalUnknown Traversal = "unknown"
|
||||
TraversalSubjectSetExpand Traversal = "subject set expand"
|
||||
TraversalComputedUserset Traversal = "computed userset"
|
||||
TraversalTupleToUserset Traversal = "tuple to userset"
|
||||
)
|
||||
|
||||
const (
|
||||
TruncationReasonDepthLimit TruncationReason = "depth_limit"
|
||||
TruncationReasonTupleLimit TruncationReason = "tuple_limit"
|
||||
TruncationReasonCycle TruncationReason = "cycle"
|
||||
)
|
||||
|
||||
var _, _ Subject = (*SubjectID)(nil), (*SubjectSet)(nil)
|
||||
|
||||
func (s *SubjectID) Equals(other Subject) bool {
|
||||
uv, ok := other.(*SubjectID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return uv.ID == 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)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return uv.Relation == s.Relation && uv.Object == s.Object && uv.Namespace == s.Namespace
|
||||
}
|
||||
|
||||
func (s *SubjectSet) UniqueID() uuid.UUID {
|
||||
return uuid.NewV5(s.Object, s.Namespace+"-"+s.Relation)
|
||||
}
|
||||
|
||||
func (s *SubjectSet) String() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return s.Namespace + ":" + s.Object.String() + "#" + s.Relation
|
||||
}
|
||||
|
||||
func (t *RelationTuple) ToQuery() *RelationQuery {
|
||||
return &RelationQuery{
|
||||
Namespace: &t.Namespace,
|
||||
Object: &t.Object,
|
||||
Relation: &t.Relation,
|
||||
Subject: t.Subject,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *RelationTuple) String() string {
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
subject := ""
|
||||
if t.Subject != nil {
|
||||
subject = t.Subject.String()
|
||||
}
|
||||
return t.Namespace + ":" + t.Object.String() + "#" + t.Relation + "@" + 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 []keysetpagination.Option
|
||||
RequestedPages []keysetpagination.PageToken
|
||||
// lock is necessary so that GetRelationTuples() is safe for concurrency.
|
||||
requestedPagesLock sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
_ Manager = (*ManagerWrapper)(nil)
|
||||
_ ManagerProvider = (*ManagerWrapper)(nil)
|
||||
)
|
||||
|
||||
func NewManagerWrapper(_ any, reg ManagerProvider, options ...keysetpagination.Option) *ManagerWrapper {
|
||||
return &ManagerWrapper{
|
||||
Reg: reg,
|
||||
PageOpts: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) GetRelationTuples(ctx context.Context, query *RelationQuery, options ...keysetpagination.Option) ([]*RelationTuple, *keysetpagination.Paginator, error) {
|
||||
p, err := keysetpagination.NewPaginator(options...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t.requestedPagesLock.Lock()
|
||||
defer t.requestedPagesLock.Unlock()
|
||||
t.RequestedPages = append(t.RequestedPages, p.PageToken())
|
||||
return t.Reg.RelationTupleManager().GetRelationTuples(ctx, query, append(t.PageOpts, options...)...)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) ExistsRelationTuples(ctx context.Context, query *RelationQuery) (bool, error) {
|
||||
return t.Reg.RelationTupleManager().ExistsRelationTuples(ctx, query)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) WriteRelationTuples(ctx context.Context, rs ...*RelationTuple) error {
|
||||
return t.Reg.RelationTupleManager().WriteRelationTuples(ctx, rs...)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) DeleteRelationTuples(ctx context.Context, rs ...*RelationTuple) error {
|
||||
return t.Reg.RelationTupleManager().DeleteRelationTuples(ctx, rs...)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) DeleteAllRelationTuples(ctx context.Context, query *RelationQuery) error {
|
||||
return t.Reg.RelationTupleManager().DeleteAllRelationTuples(ctx, query)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) TransactRelationTuples(ctx context.Context, insert []*RelationTuple, delete []*RelationTuple) error {
|
||||
return t.Reg.RelationTupleManager().TransactRelationTuples(ctx, insert, delete)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) TraverseSubjectSetExpansion(ctx context.Context, tuple *RelationTuple, allowedSubjectSets []SubjectSetType) ([]*TraversalResult, error) {
|
||||
return t.Reg.RelationTupleManager().TraverseSubjectSetExpansion(ctx, tuple, allowedSubjectSets)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) FindTupleWithRelations(ctx context.Context, tuple *RelationTuple, relations []string) (*RelationTuple, error) {
|
||||
return t.Reg.RelationTupleManager().FindTupleWithRelations(ctx, tuple, relations)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) Transaction(ctx context.Context, f func(ctx context.Context) error) error {
|
||||
return t.Reg.RelationTupleManager().Transaction(ctx, f)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) WithLatestSnapshot(ctx context.Context, f func(ctx context.Context) error) error {
|
||||
return t.Reg.RelationTupleManager().WithLatestSnapshot(ctx, f)
|
||||
}
|
||||
|
||||
func (t *ManagerWrapper) RelationTupleManager() Manager {
|
||||
return t
|
||||
}
|
||||
Reference in New Issue
Block a user