1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
package namespace
import (
"context"
"github.com/authzed/spicedb/pkg/datastore"
"github.com/authzed/spicedb/pkg/genutil/mapz"
core "github.com/authzed/spicedb/pkg/proto/core/v1"
)
// ReadNamespaceAndRelation checks that the specified namespace and relation exist in the
// datastore.
//
// Returns NamespaceNotFoundError if the namespace cannot be found.
// Returns RelationNotFoundError if the relation was not found in the namespace.
// Returns the direct downstream error for all other unknown error.
func ReadNamespaceAndRelation(
ctx context.Context,
namespace string,
relation string,
ds datastore.Reader,
) (*core.NamespaceDefinition, *core.Relation, error) {
config, _, err := ds.ReadNamespaceByName(ctx, namespace)
if err != nil {
return nil, nil, err
}
for _, rel := range config.Relation {
if rel.Name == relation {
return config, rel, nil
}
}
return nil, nil, NewRelationNotFoundErr(namespace, relation)
}
// TypeAndRelationToCheck is a single check of a namespace+relation pair.
type TypeAndRelationToCheck struct {
// NamespaceName is the namespace name to ensure exists.
NamespaceName string
// RelationName is the relation name to ensure exists under the namespace.
RelationName string
// AllowEllipsis, if true, allows for the ellipsis as the RelationName.
AllowEllipsis bool
}
// CheckNamespaceAndRelations ensures that the given namespace+relation checks all succeed. If any fail, returns an error.
//
// Returns NamespaceNotFoundError if the namespace cannot be found.
// Returns RelationNotFoundError if the relation was not found in the namespace.
// Returns the direct downstream error for all other unknown error.
func CheckNamespaceAndRelations(ctx context.Context, checks []TypeAndRelationToCheck, ds datastore.Reader) error {
nsNames := mapz.NewSet[string]()
for _, toCheck := range checks {
nsNames.Insert(toCheck.NamespaceName)
}
if nsNames.IsEmpty() {
return nil
}
namespaces, err := ds.LookupNamespacesWithNames(ctx, nsNames.AsSlice())
if err != nil {
return err
}
mappedNamespaces := make(map[string]*core.NamespaceDefinition, len(namespaces))
for _, namespace := range namespaces {
mappedNamespaces[namespace.Definition.Name] = namespace.Definition
}
for _, toCheck := range checks {
nsDef, ok := mappedNamespaces[toCheck.NamespaceName]
if !ok {
return NewNamespaceNotFoundErr(toCheck.NamespaceName)
}
if toCheck.AllowEllipsis && toCheck.RelationName == datastore.Ellipsis {
continue
}
foundRelation := false
for _, rel := range nsDef.Relation {
if rel.Name == toCheck.RelationName {
foundRelation = true
break
}
}
if !foundRelation {
return NewRelationNotFoundErr(toCheck.NamespaceName, toCheck.RelationName)
}
}
return nil
}
// CheckNamespaceAndRelation checks that the specified namespace and relation exist in the
// datastore.
//
// Returns datastore.NamespaceNotFoundError if the namespace cannot be found.
// Returns RelationNotFoundError if the relation was not found in the namespace.
// Returns the direct downstream error for all other unknown error.
func CheckNamespaceAndRelation(
ctx context.Context,
namespace string,
relation string,
allowEllipsis bool,
ds datastore.Reader,
) error {
config, _, err := ds.ReadNamespaceByName(ctx, namespace)
if err != nil {
return err
}
if allowEllipsis && relation == datastore.Ellipsis {
return nil
}
for _, rel := range config.Relation {
if rel.Name == relation {
return nil
}
}
return NewRelationNotFoundErr(namespace, relation)
}
// ListReferencedNamespaces returns the names of all namespaces referenced in the
// given namespace definitions. This includes the namespaces themselves, as well as
// any found in type information on relations.
func ListReferencedNamespaces(nsdefs []*core.NamespaceDefinition) []string {
referencedNamespaceNamesSet := mapz.NewSet[string]()
for _, nsdef := range nsdefs {
referencedNamespaceNamesSet.Insert(nsdef.Name)
for _, relation := range nsdef.Relation {
if relation.GetTypeInformation() != nil {
for _, allowedRel := range relation.GetTypeInformation().AllowedDirectRelations {
referencedNamespaceNamesSet.Insert(allowedRel.GetNamespace())
}
}
}
}
return referencedNamespaceNamesSet.AsSlice()
}
|