github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/relationships/validation_test.go (about) 1 package relationships 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/authzed/spicedb/internal/datastore/memdb" 10 "github.com/authzed/spicedb/internal/testfixtures" 11 core "github.com/authzed/spicedb/pkg/proto/core/v1" 12 "github.com/authzed/spicedb/pkg/tuple" 13 ) 14 15 const basicSchema = `definition user {} 16 17 caveat somecaveat(somecondition int) { 18 somecondition == 42 19 } 20 21 caveat anothercaveat(somecondition int) { 22 somecondition == 42 23 } 24 25 definition folder {} 26 27 definition resource { 28 relation folder: folder 29 relation viewer: user | user with somecaveat | user:* 30 relation editor: user with somecaveat 31 relation viewer2: user:* with somecaveat 32 33 permission view = viewer 34 }` 35 36 func TestValidateRelationshipOperations(t *testing.T) { 37 tcs := []struct { 38 name string 39 schema string 40 relationship string 41 operation core.RelationTupleUpdate_Operation 42 expectedError string 43 }{ 44 { 45 "basic create", 46 basicSchema, 47 "resource:foo#viewer@user:tom", 48 core.RelationTupleUpdate_CREATE, 49 "", 50 }, 51 { 52 "basic delete", 53 basicSchema, 54 "resource:foo#viewer@user:tom", 55 core.RelationTupleUpdate_DELETE, 56 "", 57 }, 58 { 59 "create over permission error", 60 basicSchema, 61 "resource:foo#view@user:tom", 62 core.RelationTupleUpdate_CREATE, 63 "cannot write a relationship to permission", 64 }, 65 { 66 "delete over permission error", 67 basicSchema, 68 "resource:foo#view@user:tom", 69 core.RelationTupleUpdate_DELETE, 70 "cannot write a relationship to permission", 71 }, 72 { 73 "create wrong subject type", 74 basicSchema, 75 "resource:foo#folder@user:tom", 76 core.RelationTupleUpdate_CREATE, 77 "subjects of type `user` are not allowed on relation", 78 }, 79 { 80 "delete wrong subject type", 81 basicSchema, 82 "resource:foo#folder@user:tom", 83 core.RelationTupleUpdate_DELETE, 84 "subjects of type `user` are not allowed on relation", 85 }, 86 { 87 "unknown subject type", 88 basicSchema, 89 "resource:foo#folder@foobar:tom", 90 core.RelationTupleUpdate_CREATE, 91 "object definition `foobar` not found", 92 }, 93 { 94 "unknown resource type", 95 basicSchema, 96 "foobar:foo#folder@user:tom", 97 core.RelationTupleUpdate_CREATE, 98 "object definition `foobar` not found", 99 }, 100 { 101 "create with wrong caveat", 102 basicSchema, 103 "resource:fo#viewer@user:tom[anothercaveat]", 104 core.RelationTupleUpdate_CREATE, 105 "subjects of type `user with anothercaveat` are not allowed on relation `resource#viewer`", 106 }, 107 { 108 "delete with wrong caveat", 109 basicSchema, 110 "resource:fo#viewer@user:tom[anothercaveat]", 111 core.RelationTupleUpdate_DELETE, 112 "subjects of type `user with anothercaveat` are not allowed on relation `resource#viewer`", 113 }, 114 { 115 "create with correct caveat", 116 basicSchema, 117 "resource:fo#viewer@user:tom[somecaveat]", 118 core.RelationTupleUpdate_CREATE, 119 "", 120 }, 121 { 122 "delete with correct caveat", 123 basicSchema, 124 "resource:fo#viewer@user:tom[somecaveat]", 125 core.RelationTupleUpdate_DELETE, 126 "", 127 }, 128 { 129 "create with no caveat should error", 130 basicSchema, 131 "resource:fo#editor@user:tom", 132 core.RelationTupleUpdate_CREATE, 133 "subjects of type `user` are not allowed on relation `resource#editor`", 134 }, 135 { 136 "delete with no caveat should be okay", 137 basicSchema, 138 "resource:fo#editor@user:tom", 139 core.RelationTupleUpdate_DELETE, 140 "", 141 }, 142 { 143 "create with wildcard", 144 basicSchema, 145 "resource:fo#viewer@user:*", 146 core.RelationTupleUpdate_CREATE, 147 "", 148 }, 149 { 150 "delete with wildcard", 151 basicSchema, 152 "resource:fo#viewer@user:*", 153 core.RelationTupleUpdate_DELETE, 154 "", 155 }, 156 { 157 "create with invalid wildcard", 158 basicSchema, 159 "resource:fo#editor@user:*", 160 core.RelationTupleUpdate_CREATE, 161 "subjects of type `user:*` are not allowed on relation `resource#editor`", 162 }, 163 { 164 "delete with invalid wildcard", 165 basicSchema, 166 "resource:fo#editor@user:*", 167 core.RelationTupleUpdate_DELETE, 168 "subjects of type `user:*` are not allowed on relation `resource#editor`", 169 }, 170 { 171 "create with no caveat over wildcard should error", 172 basicSchema, 173 "resource:fo#viewer2@user:*", 174 core.RelationTupleUpdate_CREATE, 175 "subjects of type `user:*` are not allowed on relation `resource#viewer2`", 176 }, 177 { 178 "delete with no caveat over wildcard should be okay", 179 basicSchema, 180 "resource:fo#viewer2@user:*", 181 core.RelationTupleUpdate_DELETE, 182 "", 183 }, 184 { 185 "create with no caveat over concrete subject should error", 186 basicSchema, 187 "resource:fo#viewer2@user:tom", 188 core.RelationTupleUpdate_CREATE, 189 "subjects of type `user` are not allowed on relation `resource#viewer2`", 190 }, 191 { 192 "delete with no caveat over concrete subject should error", 193 basicSchema, 194 "resource:fo#viewer2@user:tom", 195 core.RelationTupleUpdate_DELETE, 196 "subjects of type `user` are not allowed on relation `resource#viewer2`", 197 }, 198 } 199 200 for _, tc := range tcs { 201 t.Run(tc.name, func(t *testing.T) { 202 req := require.New(t) 203 204 ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) 205 req.NoError(err) 206 207 uds, rev := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, nil, req) 208 reader := uds.SnapshotReader(rev) 209 210 op := tuple.Create 211 if tc.operation == core.RelationTupleUpdate_DELETE { 212 op = tuple.Delete 213 } 214 215 // Validate update. 216 err = ValidateRelationshipUpdates(context.Background(), reader, []*core.RelationTupleUpdate{ 217 op(tuple.MustParse(tc.relationship)), 218 }) 219 if tc.expectedError != "" { 220 req.ErrorContains(err, tc.expectedError) 221 } else { 222 req.NoError(err) 223 } 224 225 // Validate create/touch. 226 if tc.operation != core.RelationTupleUpdate_DELETE { 227 err = ValidateRelationshipsForCreateOrTouch(context.Background(), reader, []*core.RelationTuple{ 228 tuple.MustParse(tc.relationship), 229 }) 230 if tc.expectedError != "" { 231 req.ErrorContains(err, tc.expectedError) 232 } else { 233 req.NoError(err) 234 } 235 } 236 }) 237 } 238 }