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:
130
schema/typechecks.go
Normal file
130
schema/typechecks.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package schema
|
||||
|
||||
import "github.com/ory/keto/internal/namespace/ast"
|
||||
|
||||
type (
|
||||
namespaceQuery []namespace
|
||||
relationQuery []ast.Relation
|
||||
typeCheck func(p *parser)
|
||||
)
|
||||
|
||||
func (p *parser) query() namespaceQuery {
|
||||
return p.namespaces
|
||||
}
|
||||
|
||||
func (ns namespaceQuery) find(name string) (*namespace, bool) {
|
||||
for _, n := range ns {
|
||||
if n.Name == name {
|
||||
return &n, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (ns namespaceQuery) findRelation(namespace, relation string) (*ast.Relation, bool) {
|
||||
n, ok := ns.find(namespace)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return relationQuery(n.Relations).find(relation)
|
||||
}
|
||||
|
||||
func (rs relationQuery) find(name string) (*ast.Relation, bool) {
|
||||
for _, r := range rs {
|
||||
if r.Name == name {
|
||||
return &r, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *parser) typeCheck() {
|
||||
for _, check := range p.checks {
|
||||
check(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) addCheck(check typeCheck) {
|
||||
p.checks = append(p.checks, check)
|
||||
}
|
||||
|
||||
// checkNamespace checks that the there exists a namespace with the given name.
|
||||
func checkNamespaceExists(namespace item) typeCheck {
|
||||
return func(p *parser) {
|
||||
if _, ok := namespaceQuery(p.namespaces).find(namespace.Val); ok {
|
||||
return
|
||||
}
|
||||
p.addErr(namespace, "namespace %q was not declared", namespace.Val)
|
||||
}
|
||||
}
|
||||
|
||||
// checkNamespaceHasRelation checks that 1. there exists the given namespace,
|
||||
// and 2. that there exists the given relation in that namespace.
|
||||
func checkNamespaceHasRelation(namespace, relation item) typeCheck {
|
||||
return func(p *parser) {
|
||||
if n, ok := namespaceQuery(p.namespaces).find(namespace.Val); ok {
|
||||
if _, ok := relationQuery(n.Relations).find(relation.Val); ok {
|
||||
return
|
||||
}
|
||||
p.addErr(relation,
|
||||
"namespace %q did not declare relation %q",
|
||||
namespace.Val, relation.Val)
|
||||
return
|
||||
}
|
||||
p.addErr(namespace, "namespace %q was not declared", namespace.Val)
|
||||
}
|
||||
}
|
||||
|
||||
// checkCurrentNamespaceHasRelation checks that the give relation exists in the
|
||||
// current namespace.
|
||||
func checkCurrentNamespaceHasRelation(current *namespace, relation item) typeCheck {
|
||||
namespace := current.Name
|
||||
return func(p *parser) {
|
||||
if n, ok := namespaceQuery(p.namespaces).find(namespace); ok {
|
||||
if _, ok := relationQuery(n.Relations).find(relation.Val); ok {
|
||||
return
|
||||
}
|
||||
p.addErr(relation,
|
||||
"namespace %q did not declare relation %q",
|
||||
namespace, relation.Val)
|
||||
return
|
||||
}
|
||||
p.addErr(relation, "namespace %q was not declared", namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func checkAllRelationsTypesHaveRelation(current *namespace, relationType item, relation string) typeCheck {
|
||||
namespace := current.Name
|
||||
return func(p *parser) {
|
||||
recursiveCheckAllRelationsTypesHaveRelation(p, relationType, namespace, relationType.Val, relation, tupleToSubjectSetTypeCheckMaxDepth)
|
||||
}
|
||||
}
|
||||
|
||||
func recursiveCheckAllRelationsTypesHaveRelation(p *parser, item item, namespace string, relationType string, relation string, depth int) {
|
||||
if depth < 0 {
|
||||
p.addErr(item, "could not typecheck deeply nested SubjectSet further")
|
||||
return
|
||||
}
|
||||
r, ok := namespaceQuery(p.namespaces).findRelation(namespace, relationType)
|
||||
if !ok {
|
||||
p.addErr(item, "relation %q was not declared in namespace %q",
|
||||
relationType, namespace)
|
||||
return
|
||||
}
|
||||
for _, t := range r.Types {
|
||||
if t.Relation == "" {
|
||||
if _, ok := p.query().findRelation(t.Namespace, relation); !ok {
|
||||
p.addErr(item, "relation %q was not declared in namespace %q",
|
||||
relation, t.Namespace)
|
||||
}
|
||||
} else {
|
||||
// Type is a subject set, we need to recursively check if the type has
|
||||
// the required relation.
|
||||
recursiveCheckAllRelationsTypesHaveRelation(
|
||||
p, item, t.Namespace, t.Relation, relation, depth-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user