github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/dispatch/keys/keys.go (about)

     1  package keys
     2  
     3  import (
     4  	"context"
     5  
     6  	datastoremw "github.com/authzed/spicedb/internal/middleware/datastore"
     7  	"github.com/authzed/spicedb/internal/namespace"
     8  	v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1"
     9  )
    10  
    11  // Handler is an interface defining how keys are computed for dispatching and caching.
    12  type Handler interface {
    13  	// CheckCacheKey computes the caching key for a Check operation.
    14  	CheckCacheKey(ctx context.Context, req *v1.DispatchCheckRequest) (DispatchCacheKey, error)
    15  
    16  	// LookupResourcesCacheKey computes the caching key for a LookupResources operation.
    17  	LookupResourcesCacheKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) (DispatchCacheKey, error)
    18  
    19  	// LookupSubjectsCacheKey computes the caching key for a LookupSubjects operation.
    20  	LookupSubjectsCacheKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error)
    21  
    22  	// ExpandCacheKey computes the caching key for an Expand operation.
    23  	ExpandCacheKey(ctx context.Context, req *v1.DispatchExpandRequest) (DispatchCacheKey, error)
    24  
    25  	// ReachableResourcesCacheKey computes the caching key for a ReachableResources operation.
    26  	ReachableResourcesCacheKey(ctx context.Context, req *v1.DispatchReachableResourcesRequest) (DispatchCacheKey, error)
    27  
    28  	// CheckDispatchKey computes the dispatch key for a Check operation.
    29  	CheckDispatchKey(ctx context.Context, req *v1.DispatchCheckRequest) ([]byte, error)
    30  
    31  	// LookupResourcesDispatchKey computes the dispatch key for a LookupResources operation.
    32  	LookupResourcesDispatchKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) ([]byte, error)
    33  
    34  	// LookupSubjectsDispatchKey computes the key for a LookupSubjects operation.
    35  	LookupSubjectsDispatchKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error)
    36  
    37  	// ExpandDispatchKey computes the dispatch key for an Expand operation.
    38  	ExpandDispatchKey(ctx context.Context, req *v1.DispatchExpandRequest) ([]byte, error)
    39  
    40  	// ReachableResourcesDispatchKey computes the key for a ReachableResources operation.
    41  	ReachableResourcesDispatchKey(ctx context.Context, req *v1.DispatchReachableResourcesRequest) ([]byte, error)
    42  }
    43  
    44  type baseKeyHandler struct{}
    45  
    46  func (b baseKeyHandler) LookupResourcesCacheKey(_ context.Context, req *v1.DispatchLookupResourcesRequest) (DispatchCacheKey, error) {
    47  	return lookupResourcesRequestToKey(req, computeBothHashes), nil
    48  }
    49  
    50  func (b baseKeyHandler) LookupSubjectsCacheKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error) {
    51  	return lookupSubjectsRequestToKey(req, computeBothHashes), nil
    52  }
    53  
    54  func (b baseKeyHandler) ExpandCacheKey(_ context.Context, req *v1.DispatchExpandRequest) (DispatchCacheKey, error) {
    55  	return expandRequestToKey(req, computeBothHashes), nil
    56  }
    57  
    58  func (b baseKeyHandler) ReachableResourcesCacheKey(_ context.Context, req *v1.DispatchReachableResourcesRequest) (DispatchCacheKey, error) {
    59  	return reachableResourcesRequestToKey(req, computeBothHashes), nil
    60  }
    61  
    62  func (b baseKeyHandler) CheckDispatchKey(_ context.Context, req *v1.DispatchCheckRequest) ([]byte, error) {
    63  	return checkRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil
    64  }
    65  
    66  func (b baseKeyHandler) LookupResourcesDispatchKey(_ context.Context, req *v1.DispatchLookupResourcesRequest) ([]byte, error) {
    67  	return lookupResourcesRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil
    68  }
    69  
    70  func (b baseKeyHandler) LookupSubjectsDispatchKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error) {
    71  	return lookupSubjectsRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil
    72  }
    73  
    74  func (b baseKeyHandler) ExpandDispatchKey(_ context.Context, req *v1.DispatchExpandRequest) ([]byte, error) {
    75  	return expandRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil
    76  }
    77  
    78  func (b baseKeyHandler) ReachableResourcesDispatchKey(_ context.Context, req *v1.DispatchReachableResourcesRequest) ([]byte, error) {
    79  	return reachableResourcesRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil
    80  }
    81  
    82  // DirectKeyHandler is a key handler that uses the relation name itself as the key.
    83  type DirectKeyHandler struct {
    84  	baseKeyHandler
    85  }
    86  
    87  func (d *DirectKeyHandler) CheckCacheKey(_ context.Context, req *v1.DispatchCheckRequest) (DispatchCacheKey, error) {
    88  	return checkRequestToKey(req, computeBothHashes), nil
    89  }
    90  
    91  // CanonicalKeyHandler is a key handler which makes use of the canonical key for relations for
    92  // dispatching.
    93  type CanonicalKeyHandler struct {
    94  	baseKeyHandler
    95  }
    96  
    97  func (c *CanonicalKeyHandler) CheckCacheKey(ctx context.Context, req *v1.DispatchCheckRequest) (DispatchCacheKey, error) {
    98  	// NOTE: We do not use the canonicalized cache key when checking within the same namespace, as
    99  	// we may get different results if the subject being checked matches the resource exactly, e.g.
   100  	// a check for `somenamespace:someobject#somerel@somenamespace:someobject#somerel`.
   101  	if req.ResourceRelation.Namespace != req.Subject.Namespace {
   102  		// Load the relation to get its computed cache key, if any.
   103  		ds := datastoremw.MustFromContext(ctx)
   104  
   105  		revision, err := ds.RevisionFromString(req.Metadata.AtRevision)
   106  		if err != nil {
   107  			return emptyDispatchCacheKey, err
   108  		}
   109  		r := ds.SnapshotReader(revision)
   110  
   111  		_, relation, err := namespace.ReadNamespaceAndRelation(
   112  			ctx,
   113  			req.ResourceRelation.Namespace,
   114  			req.ResourceRelation.Relation,
   115  			r,
   116  		)
   117  		if err != nil {
   118  			return emptyDispatchCacheKey, err
   119  		}
   120  
   121  		if relation.CanonicalCacheKey != "" {
   122  			return checkRequestToKeyWithCanonical(req, relation.CanonicalCacheKey)
   123  		}
   124  	}
   125  
   126  	return checkRequestToKey(req, computeBothHashes), nil
   127  }