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

     1  package namespace
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/authzed/spicedb/pkg/typesystem"
     7  )
     8  
     9  // computePermissionAliases computes a map of aliases between the various permissions in a
    10  // namespace. A permission is considered an alias if it *directly* refers to another permission
    11  // or relation without any other form of expression.
    12  func computePermissionAliases(typeSystem *typesystem.ValidatedNamespaceTypeSystem) (map[string]string, error) {
    13  	aliases := map[string]string{}
    14  	done := map[string]struct{}{}
    15  	unresolvedAliases := map[string]string{}
    16  
    17  	for _, rel := range typeSystem.Namespace().Relation {
    18  		// Ensure the relation has a rewrite...
    19  		if rel.GetUsersetRewrite() == nil {
    20  			done[rel.Name] = struct{}{}
    21  			continue
    22  		}
    23  
    24  		// ... with a union ...
    25  		union := rel.GetUsersetRewrite().GetUnion()
    26  		if union == nil {
    27  			done[rel.Name] = struct{}{}
    28  			continue
    29  		}
    30  
    31  		// ... with a single child ...
    32  		if len(union.Child) != 1 {
    33  			done[rel.Name] = struct{}{}
    34  			continue
    35  		}
    36  
    37  		// ... that is a computed userset.
    38  		computedUserset := union.Child[0].GetComputedUserset()
    39  		if computedUserset == nil {
    40  			done[rel.Name] = struct{}{}
    41  			continue
    42  		}
    43  
    44  		// If the aliased item is a relation, then we've found the alias target.
    45  		aliasedPermOrRel := computedUserset.GetRelation()
    46  		if !typeSystem.IsPermission(aliasedPermOrRel) {
    47  			done[rel.Name] = struct{}{}
    48  			aliases[rel.Name] = aliasedPermOrRel
    49  			continue
    50  		}
    51  
    52  		// Otherwise, add the permission to the working set.
    53  		unresolvedAliases[rel.Name] = aliasedPermOrRel
    54  	}
    55  
    56  	for len(unresolvedAliases) > 0 {
    57  		startingCount := len(unresolvedAliases)
    58  		for relName, aliasedPermission := range unresolvedAliases {
    59  			if _, ok := done[aliasedPermission]; ok {
    60  				done[relName] = struct{}{}
    61  
    62  				if alias, ok := aliases[aliasedPermission]; ok {
    63  					aliases[relName] = alias
    64  				} else {
    65  					aliases[relName] = aliasedPermission
    66  				}
    67  				delete(unresolvedAliases, relName)
    68  				continue
    69  			}
    70  		}
    71  		if len(unresolvedAliases) == startingCount {
    72  			keys := make([]string, 0, len(unresolvedAliases))
    73  			for key := range unresolvedAliases {
    74  				keys = append(keys, key)
    75  			}
    76  			sort.Strings(keys)
    77  			return nil, NewPermissionsCycleErr(typeSystem.Namespace().Name, keys)
    78  		}
    79  	}
    80  
    81  	return aliases, nil
    82  }