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