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 }