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

This commit is contained in:
upstream-mirror
2026-09-16 01:08:34 +05:00
commit 9456a4e922
2560 changed files with 120911 additions and 0 deletions

153
ketoapi/enc_string.go Normal file
View File

@@ -0,0 +1,153 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package ketoapi
import (
"fmt"
"strings"
"github.com/ory/herodot"
"github.com/pkg/errors"
)
func ErrMalformedInput() *herodot.DefaultError {
return herodot.ErrBadRequest().WithError("malformed string input")
}
func (r *RelationTuple) String() string {
if r == nil {
return ""
}
sb := strings.Builder{}
sb.WriteString(r.Namespace)
sb.WriteRune(':')
sb.WriteString(r.Object)
sb.WriteRune('#')
sb.WriteString(r.Relation)
sb.WriteRune('@')
if r.SubjectID != nil {
sb.WriteString(*r.SubjectID)
} else if r.SubjectSet != nil {
sb.WriteString(r.SubjectSet.String())
} else {
sb.WriteString("<ERROR: no subject>")
}
return sb.String()
}
func (r *RelationTuple) FromString(s string) (*RelationTuple, error) {
var (
objectAndRelationAndSubject string
relationAndSubject string
subject string
ok bool
)
if r.Namespace, objectAndRelationAndSubject, ok = strings.Cut(s, ":"); !ok {
return nil, errors.WithStack(ErrMalformedInput().WithDebug("expected input to contain ':'"))
}
if r.Object, relationAndSubject, ok = strings.Cut(objectAndRelationAndSubject, "#"); !ok {
return nil, errors.WithStack(ErrMalformedInput().WithDebug("expected input to contain '#'"))
}
if r.Relation, subject, ok = strings.Cut(relationAndSubject, "@"); !ok {
return nil, errors.WithStack(ErrMalformedInput().WithDebug("expected input to contain '@'"))
}
// remove optional brackets around the subject set
subject = strings.Trim(subject, "()")
if strings.Contains(subject, ":") {
subSet, err := (&SubjectSet{}).FromString(subject)
if err != nil {
return nil, err
}
r.SubjectSet = subSet
} else {
r.SubjectID = &subject
}
return r, nil
}
func (s *SubjectSet) String() string {
if s.Relation == "" {
return fmt.Sprintf("%s:%s", s.Namespace, s.Object)
}
return fmt.Sprintf("%s:%s#%s", s.Namespace, s.Object, s.Relation)
}
func (s *SubjectSet) FromString(str string) (*SubjectSet, error) {
// If there is no '#' we have a subject set without a relation, such as
// Users:Bob, which just means that the relation is empty.
namespaceAndObject, relation, _ := strings.Cut(str, "#")
namespace, object, ok := strings.Cut(namespaceAndObject, ":")
if !ok {
return nil, errors.WithStack(ErrMalformedInput().WithDebug("expected subject set to contain ':'"))
}
return &SubjectSet{
Namespace: namespace,
Object: object,
Relation: relation,
}, nil
}
func (t TreeNodeType) String() string {
return string(t)
}
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 TreeNodeTupleToSubjectSet:
setOperation = "┐ tuple to userset"
case TreeNodeComputedSubjectSet:
setOperation = "┐ computed userset"
}
boxSymbol := "├"
if len(children) == 1 {
boxSymbol = "└"
}
return fmt.Sprintf("%s %s\n%s──%s", setOperation, nodeLabel, boxSymbol, strings.Join(children, "\n└──"))
}