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  }