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

     1  package namespace
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    10  	"github.com/authzed/spicedb/pkg/typesystem"
    11  
    12  	"github.com/authzed/spicedb/internal/datastore/memdb"
    13  	ns "github.com/authzed/spicedb/pkg/namespace"
    14  )
    15  
    16  func TestAliasing(t *testing.T) {
    17  	testCases := []struct {
    18  		name             string
    19  		toCheck          *core.NamespaceDefinition
    20  		expectedError    string
    21  		expectedAliasMap map[string]string
    22  	}{
    23  		{
    24  			"basic aliasing",
    25  			ns.Namespace(
    26  				"document",
    27  				ns.MustRelation("owner", nil),
    28  				ns.MustRelation("viewer", nil),
    29  				ns.MustRelation("edit", ns.Union(
    30  					ns.ComputedUserset("owner"),
    31  				)),
    32  				ns.MustRelation("view", ns.Union(
    33  					ns.ComputedUserset("viewer"),
    34  					ns.ComputedUserset("edit"),
    35  				)),
    36  			),
    37  			"",
    38  			map[string]string{
    39  				"edit": "owner",
    40  			},
    41  		},
    42  		{
    43  			"multiple aliasing",
    44  			ns.Namespace(
    45  				"document",
    46  				ns.MustRelation("owner", nil),
    47  				ns.MustRelation("viewer", nil),
    48  				ns.MustRelation("edit", ns.Union(
    49  					ns.ComputedUserset("owner"),
    50  				)),
    51  				ns.MustRelation("view", ns.Union(
    52  					ns.ComputedUserset("viewer"),
    53  					ns.ComputedUserset("edit"),
    54  				)),
    55  				ns.MustRelation("another_viewer", ns.Union(
    56  					ns.ComputedUserset("viewer"),
    57  				)),
    58  			),
    59  			"",
    60  			map[string]string{
    61  				"edit":           "owner",
    62  				"another_viewer": "viewer",
    63  			},
    64  		},
    65  		{
    66  			"alias cycle",
    67  			ns.Namespace(
    68  				"document",
    69  				ns.MustRelation("edit", ns.Union(
    70  					ns.ComputedUserset("owner"),
    71  				)),
    72  				ns.MustRelation("owner", ns.Union(
    73  					ns.ComputedUserset("edit"),
    74  				)),
    75  			),
    76  			"under definition `document`, there exists a cycle in permissions: edit, owner",
    77  			map[string]string{},
    78  		},
    79  		{
    80  			"alias multi-level cycle",
    81  			ns.Namespace(
    82  				"document",
    83  				ns.MustRelation("edit", ns.Union(
    84  					ns.ComputedUserset("owner"),
    85  				)),
    86  				ns.MustRelation("owner", ns.Union(
    87  					ns.ComputedUserset("foo"),
    88  				)),
    89  				ns.MustRelation("foo", ns.Union(
    90  					ns.ComputedUserset("edit"),
    91  				)),
    92  			),
    93  			"under definition `document`, there exists a cycle in permissions: edit, foo, owner",
    94  			map[string]string{},
    95  		},
    96  		{
    97  			"multi-level aliasing",
    98  			ns.Namespace(
    99  				"document",
   100  				ns.MustRelation("owner", nil),
   101  				ns.MustRelation("viewer", nil),
   102  				ns.MustRelation("cool_viewer", ns.Union(
   103  					ns.ComputedUserset("viewer"),
   104  				)),
   105  				ns.MustRelation("edit", ns.Union(
   106  					ns.ComputedUserset("owner"),
   107  				)),
   108  				ns.MustRelation("admin", ns.Union(
   109  					ns.ComputedUserset("edit"),
   110  				)),
   111  				ns.MustRelation("view", ns.Union(
   112  					ns.ComputedUserset("viewer"),
   113  					ns.ComputedUserset("edit"),
   114  				)),
   115  				ns.MustRelation("reallyadmin", ns.Union(
   116  					ns.ComputedUserset("admin"),
   117  				)),
   118  				ns.MustRelation("reallynotadmin", ns.Union(
   119  					ns.ComputedUserset("admin"),
   120  					ns.ComputedUserset("viewer"),
   121  				)),
   122  			),
   123  			"",
   124  			map[string]string{
   125  				"edit":        "owner",
   126  				"admin":       "owner",
   127  				"reallyadmin": "owner",
   128  				"cool_viewer": "viewer",
   129  			},
   130  		},
   131  		{
   132  			"permission-only alias",
   133  			ns.Namespace(
   134  				"document",
   135  				ns.MustRelation("owner", nil),
   136  				ns.MustRelation("viewer", nil),
   137  				ns.MustRelation("view", ns.Union(
   138  					ns.ComputedUserset("viewer"),
   139  					ns.ComputedUserset("owner"),
   140  				)),
   141  				ns.MustRelation("can_view", ns.Union(
   142  					ns.ComputedUserset("view"),
   143  				)),
   144  			),
   145  			"",
   146  			map[string]string{
   147  				"can_view": "view",
   148  			},
   149  		},
   150  		{
   151  			"non-aliasing",
   152  			ns.Namespace(
   153  				"document",
   154  				ns.MustRelation("owner", nil),
   155  				ns.MustRelation("viewer", nil),
   156  				ns.MustRelation("edit", ns.Intersection(
   157  					ns.ComputedUserset("owner"),
   158  				)),
   159  				ns.MustRelation("view", ns.Union(
   160  					ns.ComputedUserset("viewer"),
   161  					ns.ComputedUserset("edit"),
   162  				)),
   163  				ns.MustRelation("somethingelse", ns.Exclusion(
   164  					ns.ComputedUserset("owner"),
   165  					ns.ComputedUserset("viewer"),
   166  				)),
   167  				ns.MustRelation("witharrow", ns.Union(
   168  					ns.TupleToUserset("owner", "something"),
   169  				)),
   170  			),
   171  			"",
   172  			map[string]string{},
   173  		},
   174  		{
   175  			"non-aliasing with nil",
   176  			ns.Namespace(
   177  				"document",
   178  				ns.MustRelation("owner", nil),
   179  				ns.MustRelation("viewer", nil),
   180  				ns.MustRelation("view", ns.Union(
   181  					ns.ComputedUserset("viewer"),
   182  					ns.Nil(),
   183  				)),
   184  				ns.MustRelation("another", ns.Union(
   185  					ns.Nil(),
   186  				)),
   187  			),
   188  			"",
   189  			map[string]string{},
   190  		},
   191  	}
   192  
   193  	for _, tc := range testCases {
   194  		tc := tc
   195  		t.Run(tc.name, func(t *testing.T) {
   196  			require := require.New(t)
   197  
   198  			ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC)
   199  			require.NoError(err)
   200  
   201  			lastRevision, err := ds.HeadRevision(context.Background())
   202  			require.NoError(err)
   203  
   204  			ts, err := typesystem.NewNamespaceTypeSystem(tc.toCheck, typesystem.ResolverForDatastoreReader(ds.SnapshotReader(lastRevision)))
   205  			require.NoError(err)
   206  
   207  			ctx := context.Background()
   208  			vts, terr := ts.Validate(ctx)
   209  			require.NoError(terr)
   210  
   211  			computed, aerr := computePermissionAliases(vts)
   212  			if tc.expectedError != "" {
   213  				require.Equal(tc.expectedError, aerr.Error())
   214  			} else {
   215  				require.NoError(aerr)
   216  				require.Equal(tc.expectedAliasMap, computed)
   217  			}
   218  		})
   219  	}
   220  }