Passed
Push — master ( 636f33...90be02 )
by Tolga
01:08 queued 13s
created

internal/engines/cache/cache_test.go   A

Size/Duplication

Total Lines 398
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 236
dl 0
loc 398
rs 10
c 0
b 0
f 0
1
package cache
2
3
import (
4
	"context"
5
	"fmt"
6
	"sort"
7
	"testing"
8
9
	"github.com/rs/xid"
10
	"google.golang.org/protobuf/types/known/anypb"
11
	"google.golang.org/protobuf/types/known/structpb"
12
13
	. "github.com/onsi/ginkgo/v2"
14
	. "github.com/onsi/gomega"
15
16
	"github.com/Permify/permify/internal/config"
17
	"github.com/Permify/permify/internal/engines"
18
	"github.com/Permify/permify/internal/factories"
19
	"github.com/Permify/permify/internal/invoke"
20
	"github.com/Permify/permify/internal/storage"
21
	"github.com/Permify/permify/pkg/attribute"
22
	pkgcache "github.com/Permify/permify/pkg/cache"
23
	"github.com/Permify/permify/pkg/cache/ristretto"
24
	"github.com/Permify/permify/pkg/database"
25
	"github.com/Permify/permify/pkg/dsl/compiler"
26
	"github.com/Permify/permify/pkg/dsl/parser"
27
	base "github.com/Permify/permify/pkg/pb/base/v1"
28
	"github.com/Permify/permify/pkg/telemetry"
29
	"github.com/Permify/permify/pkg/token"
30
	"github.com/Permify/permify/pkg/tuple"
31
)
32
33
func TestEngines(t *testing.T) {
34
	RegisterFailHandler(Fail)
35
	RunSpecs(t, "cache-suite")
36
}
37
38
var _ = Describe("cache", func() {
39
	Context("Set Cache Key", func() {
40
		It("Success", func() {
41
			// Initialize a new Ristretto cache with a capacity of 10 cache
42
			cache, err := ristretto.New()
43
			Expect(err).ShouldNot(HaveOccurred())
44
45
			// Initialize a new EngineKeys struct with a new cache.Cache instance
46
			engineKeys := CheckEngineWithCache{nil, nil, cache}
47
48
			// Create a new PermissionCheckRequest and PermissionCheckResponse
49
			checkReq := &base.PermissionCheckRequest{
50
				TenantId: "t1",
51
				Metadata: &base.PermissionCheckRequestMetadata{
52
					SchemaVersion: "test_version",
53
					SnapToken:     "test_snap_token",
54
					Depth:         20,
55
				},
56
				Entity: &base.Entity{
57
					Type: "test-entity",
58
					Id:   "e1",
59
				},
60
				Permission: "test-permission",
61
				Subject: &base.Subject{
62
					Type: "user",
63
					Id:   "u1",
64
				},
65
			}
66
67
			checkResp := &base.PermissionCheckResponse{
68
				Can: base.CheckResult_CHECK_RESULT_ALLOWED,
69
				Metadata: &base.PermissionCheckResponseMetadata{
70
					CheckCount: 0,
71
				},
72
			}
73
74
			// Set the value for the given key in the cache
75
			success := engineKeys.setCheckKey(checkReq, checkResp, true)
76
77
			cache.Wait()
78
79
			// Check that the operation was successful
80
			Expect(success).Should(BeTrue())
81
82
			// Retrieve the value for the given key from the cache
83
			resp, found := engineKeys.getCheckKey(checkReq, true)
84
85
			// Check that the key was found and the retrieved value is the same as the original value
86
			Expect(found).Should(BeTrue())
87
			Expect(checkResp).Should(Equal(resp))
88
		})
89
90
		It("With Hash Error", func() {
91
			// Initialize a new Ristretto cache with a capacity of 10 cache
92
			cache, err := ristretto.New()
93
			Expect(err).ShouldNot(HaveOccurred())
94
95
			// Initialize a new EngineKeys struct with a new cache.Cache instance
96
			engineKeys := CheckEngineWithCache{nil, nil, cache}
97
98
			// Create a new PermissionCheckRequest and PermissionCheckResponse
99
			checkReq := &base.PermissionCheckRequest{
100
				TenantId: "t1",
101
				Metadata: &base.PermissionCheckRequestMetadata{
102
					SchemaVersion: "test_version",
103
					SnapToken:     "test_snap_token",
104
					Depth:         20,
105
				},
106
				Entity: &base.Entity{
107
					Type: "test-entity",
108
					Id:   "e1",
109
				},
110
				Permission: "test-permission",
111
				Subject: &base.Subject{
112
					Type: "user",
113
					Id:   "u1",
114
				},
115
			}
116
117
			checkResp := &base.PermissionCheckResponse{
118
				Can: base.CheckResult_CHECK_RESULT_ALLOWED,
119
				Metadata: &base.PermissionCheckResponseMetadata{
120
					CheckCount: 0,
121
				},
122
			}
123
124
			// Force an error while writing the key to the hash object by passing a nil key
125
			success := engineKeys.setCheckKey(nil, checkResp, true)
126
127
			cache.Wait()
128
129
			// Check that the operation was unsuccessful
130
			Expect(success).Should(BeFalse())
131
132
			// Retrieve the value for the given key from the cache
133
			resp, found := engineKeys.getCheckKey(checkReq, true)
134
135
			// Check that the key was not found
136
			Expect(found).Should(BeFalse())
137
			Expect(resp).Should(BeNil())
138
		})
139
140
		It("With Arguments", func() {
141
			// Initialize a new Ristretto cache with a capacity of 10 cache
142
			cache, err := ristretto.New()
143
			Expect(err).ShouldNot(HaveOccurred())
144
145
			// Initialize a new EngineKeys struct with a new cache.Cache instance
146
			engineKeys := CheckEngineWithCache{nil, nil, cache}
147
148
			// Create a new PermissionCheckRequest and PermissionCheckResponse
149
			checkReq := &base.PermissionCheckRequest{
150
				TenantId: "t1",
151
				Metadata: &base.PermissionCheckRequestMetadata{
152
					SchemaVersion: "test_version",
153
					SnapToken:     "test_snap_token",
154
					Depth:         20,
155
				},
156
				Arguments: []*base.Argument{
157
					{
158
						Type: &base.Argument_ComputedAttribute{
159
							ComputedAttribute: &base.ComputedAttribute{
160
								Name: "test_argument_1",
161
							},
162
						},
163
					},
164
					{
165
						Type: &base.Argument_ComputedAttribute{
166
							ComputedAttribute: &base.ComputedAttribute{
167
								Name: "test_argument_2",
168
							},
169
						},
170
					},
171
				},
172
				Entity: &base.Entity{
173
					Type: "test-entity",
174
					Id:   "e1",
175
				},
176
				Permission: "test-rule",
177
				Subject: &base.Subject{
178
					Type: "user",
179
					Id:   "u1",
180
				},
181
			}
182
183
			checkResp := &base.PermissionCheckResponse{
184
				Can: base.CheckResult_CHECK_RESULT_ALLOWED,
185
				Metadata: &base.PermissionCheckResponseMetadata{
186
					CheckCount: 0,
187
				},
188
			}
189
190
			// Set the value for the given key in the cache
191
			success := engineKeys.setCheckKey(checkReq, checkResp, true)
192
193
			cache.Wait()
194
195
			// Check that the operation was successful
196
			Expect(success).Should(BeTrue())
197
198
			// Retrieve the value for the given key from the cache
199
			resp, found := engineKeys.getCheckKey(checkReq, true)
200
201
			// Check that the key was found and the retrieved value is the same as the original value
202
			Expect(found).Should(BeTrue())
203
			Expect(checkResp).Should(Equal(resp))
204
		})
205
206
		It("With Context", func() {
207
			value, err := anypb.New(&base.BooleanValue{Data: true})
208
			if err != nil {
209
			}
210
211
			data, err := structpb.NewStruct(map[string]interface{}{
212
				"day_of_a_week": "saturday",
213
				"day_of_a_year": 356,
214
			})
215
			if err != nil {
216
			}
217
218
			// Create a new PermissionCheckRequest and PermissionCheckResponse
219
			checkReq := &base.PermissionCheckRequest{
220
				TenantId: "t1",
221
				Metadata: &base.PermissionCheckRequestMetadata{
222
					SchemaVersion: "test_version",
223
					SnapToken:     "test_snap_token",
224
					Depth:         20,
225
				},
226
				Arguments: []*base.Argument{
227
					{
228
						Type: &base.Argument_ComputedAttribute{
229
							ComputedAttribute: &base.ComputedAttribute{
230
								Name: "test_argument_1",
231
							},
232
						},
233
					},
234
					{
235
						Type: &base.Argument_ComputedAttribute{
236
							ComputedAttribute: &base.ComputedAttribute{
237
								Name: "test_argument_2",
238
							},
239
						},
240
					},
241
				},
242
				Context: &base.Context{
243
					Tuples: []*base.Tuple{
244
						{
245
							Entity: &base.Entity{
246
								Type: "entity_type",
247
								Id:   "entity_id",
248
							},
249
							Relation: "relation",
250
							Subject: &base.Subject{
251
								Type: "subject_type",
252
								Id:   "subject_id",
253
							},
254
						},
255
					},
256
					Attributes: []*base.Attribute{
257
						{
258
							Entity: &base.Entity{
259
								Type: "entity_type",
260
								Id:   "entity_id",
261
							},
262
							Attribute: "is_public",
263
							Value:     value,
264
						},
265
					},
266
					Data: data,
267
				},
268
				Entity: &base.Entity{
269
					Type: "test-entity",
270
					Id:   "e1",
271
				},
272
				Permission: "test-rule",
273
				Subject: &base.Subject{
274
					Type: "user",
275
					Id:   "u1",
276
				},
277
			}
278
279
			Expect(engines.GenerateKey(checkReq, false)).Should(Equal("check|t1|test_version|test_snap_token|entity_type:entity_id#relation@subject_type:subject_id,entity_type:entity_id$is_public|boolean:true,day_of_a_week:saturday,day_of_a_year:356|test-entity:e1$test-rule(test_argument_1,test_argument_2)"))
280
		})
281
	})
282
283
	Context("Get Cache Key", func() {
284
		It("Key Not Found", func() {
285
			// Initialize a new Ristretto cache with a capacity of 10 cache
286
			cache, err := ristretto.New()
287
			Expect(err).ShouldNot(HaveOccurred())
288
289
			// Initialize a new EngineKeys struct with a new cache.Cache instance
290
			engineKeys := CheckEngineWithCache{nil, nil, cache}
291
292
			// Create a new PermissionCheckRequest
293
			checkReq := &base.PermissionCheckRequest{
294
				TenantId: "t1",
295
				Metadata: &base.PermissionCheckRequestMetadata{
296
					SchemaVersion: "test_version",
297
					SnapToken:     "test_snap_token",
298
					Depth:         20,
299
				},
300
				Entity: &base.Entity{
301
					Type: "test-entity",
302
					Id:   "e1",
303
				},
304
				Permission: "test-permission",
305
				Subject: &base.Subject{
306
					Type: "user",
307
					Id:   "u1",
308
				},
309
			}
310
311
			// Retrieve the value for a non-existent key from the cache
312
			resp, found := engineKeys.getCheckKey(checkReq, true)
313
314
			// Check that the key was not found
315
			Expect(found).Should(BeFalse())
316
			Expect(resp).Should(BeNil())
317
		})
318
319
		It("Set and Get Multiple Keys", func() {
320
			// Initialize a new Ristretto cache with a capacity of 10 cache
321
			cache, err := ristretto.New()
322
			Expect(err).ShouldNot(HaveOccurred())
323
324
			// Initialize a new EngineKeys struct with a new cache.Cache instance
325
			engineKeys := CheckEngineWithCache{nil, nil, cache}
326
327
			// Create some new PermissionCheckRequests and PermissionCheckResponses
328
			checkReq1 := &base.PermissionCheckRequest{
329
				TenantId: "t1",
330
				Metadata: &base.PermissionCheckRequestMetadata{
331
					SchemaVersion: "test_version",
332
					SnapToken:     "test_snap_token",
333
					Depth:         20,
334
				},
335
				Entity: &base.Entity{
336
					Type: "test-entity",
337
					Id:   "e1",
338
				},
339
				Permission: "test-permission",
340
				Subject: &base.Subject{
341
					Type: "user",
342
					Id:   "u1",
343
				},
344
			}
345
			checkResp1 := &base.PermissionCheckResponse{
346
				Can: base.CheckResult_CHECK_RESULT_ALLOWED,
347
				Metadata: &base.PermissionCheckResponseMetadata{
348
					CheckCount: 0,
349
				},
350
			}
351
352
			checkReq2 := &base.PermissionCheckRequest{
353
				TenantId: "t1",
354
				Metadata: &base.PermissionCheckRequestMetadata{
355
					SchemaVersion: "test_version",
356
					SnapToken:     "test_snap_token",
357
					Depth:         20,
358
				},
359
				Entity: &base.Entity{
360
					Type: "test-entity",
361
					Id:   "e2",
362
				},
363
				Permission: "test-permission",
364
				Subject: &base.Subject{
365
					Type: "user",
366
					Id:   "u1",
367
				},
368
			}
369
			checkResp2 := &base.PermissionCheckResponse{
370
				Can: base.CheckResult_CHECK_RESULT_DENIED,
371
				Metadata: &base.PermissionCheckResponseMetadata{
372
					CheckCount: 0,
373
				},
374
			}
375
376
			checkReq3 := &base.PermissionCheckRequest{
377
				TenantId: "t2",
378
				Metadata: &base.PermissionCheckRequestMetadata{
379
					SchemaVersion: "test_version",
380
					SnapToken:     "test_snap_token",
381
					Depth:         20,
382
				},
383
				Entity: &base.Entity{
384
					Type: "test-entity",
385
					Id:   "e1",
386
				},
387
				Permission: "test-permission",
388
				Subject: &base.Subject{
389
					Type: "user",
390
					Id:   "u2",
391
				},
392
			}
393
			checkResp3 := &base.PermissionCheckResponse{
394
				Can: base.CheckResult_CHECK_RESULT_DENIED,
395
				Metadata: &base.PermissionCheckResponseMetadata{
396
					CheckCount: 0,
397
				},
398
			}
399
400
			// Set the values for the given cache in the cache
401
			success1 := engineKeys.setCheckKey(checkReq1, checkResp1, true)
402
			success2 := engineKeys.setCheckKey(checkReq2, checkResp2, true)
403
			success3 := engineKeys.setCheckKey(checkReq3, checkResp3, true)
404
405
			cache.Wait()
406
407
			// Check that all the operations were successful
408
			Expect(success1).Should(BeTrue())
409
			Expect(success2).Should(BeTrue())
410
			Expect(success3).Should(BeTrue())
411
412
			// Retrieve the value for the given key from the cache
413
			resp1, found1 := engineKeys.getCheckKey(checkReq1, true)
414
			resp2, found2 := engineKeys.getCheckKey(checkReq2, true)
415
			resp3, found3 := engineKeys.getCheckKey(checkReq3, true)
416
417
			// Check that the key was not found
418
			Expect(found1).Should(BeTrue())
419
			Expect(checkResp1).Should(Equal(resp1))
420
421
			Expect(found2).Should(BeTrue())
422
			Expect(checkResp2).Should(Equal(resp2))
423
424
			Expect(found3).Should(BeTrue())
425
			Expect(checkResp3).Should(Equal(resp3))
426
		})
427
	})
428
429
	// DRIVE SAMPLE
430
	driveSchema := `
431
		entity user {}
432
	
433
		entity organization {
434
			relation admin @user
435
		}
436
	
437
		entity folder {
438
			relation org @organization
439
			relation creator @user
440
			relation collaborator @user
441
	
442
			permission read = collaborator
443
			permission update = collaborator
444
			permission delete = creator or org.admin
445
		}
446
	
447
		entity doc {
448
			relation org @organization
449
			relation parent @folder
450
			relation owner @user
451
	
452
			permission read = (owner or parent.collaborator) or org.admin
453
			permission update = owner and org.admin
454
			permission delete = owner or org.admin
455
			permission share = update and (owner or parent.update)
456
		}
457
		`
458
459
	Context("Drive Sample: Check", func() {
460
		It("Drive Sample: Case 1", func() {
461
			db, err := factories.DatabaseFactory(
462
				config.Database{
463
					Engine: "memory",
464
				},
465
			)
466
467
			Expect(err).ShouldNot(HaveOccurred())
468
469
			conf, err := newSchema(driveSchema)
470
			Expect(err).ShouldNot(HaveOccurred())
471
472
			schemaWriter := factories.SchemaWriterFactory(db)
473
			err = schemaWriter.WriteSchema(context.Background(), conf)
474
475
			Expect(err).ShouldNot(HaveOccurred())
476
477
			type check struct {
478
				entity     string
479
				subject    string
480
				assertions map[string]base.CheckResult
481
			}
482
483
			tests := struct {
484
				relationships []string
485
				checks        []check
486
			}{
487
				relationships: []string{
488
					"doc:1#owner@user:2",
489
					"doc:1#folder@user:3",
490
					"folder:1#collaborator@user:1",
491
					"folder:1#collaborator@user:3",
492
					"organization:1#admin@user:1",
493
					"doc:1#org@organization:1#...",
494
				},
495
				checks: []check{
496
					{
497
						entity:  "doc:1",
498
						subject: "user:1",
499
						assertions: map[string]base.CheckResult{
500
							"read": base.CheckResult_CHECK_RESULT_ALLOWED,
501
						},
502
					},
503
				},
504
			}
505
506
			schemaReader := factories.SchemaReaderFactory(db)
507
			dataReader := factories.DataReaderFactory(db)
508
			dataWriter := factories.DataWriterFactory(db)
509
510
			// engines cache cache
511
			var engineKeyCache pkgcache.Cache
512
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
513
			Expect(err).ShouldNot(HaveOccurred())
514
515
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
516
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
517
518
			invoker := invoke.NewDirectInvoker(
519
				schemaReader,
520
				dataReader,
521
				checkEngineWithCache,
522
				nil,
523
				nil,
524
				nil,
525
				telemetry.NewNoopMeter(),
526
			)
527
528
			checkEngine.SetInvoker(invoker)
529
530
			var tuples []*base.Tuple
531
532
			for _, relationship := range tests.relationships {
533
				t, err := tuple.Tuple(relationship)
534
				Expect(err).ShouldNot(HaveOccurred())
535
				tuples = append(tuples, t)
536
			}
537
538
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
539
			Expect(err).ShouldNot(HaveOccurred())
540
541
			for _, check := range tests.checks {
542
				entity, err := tuple.E(check.entity)
543
				Expect(err).ShouldNot(HaveOccurred())
544
545
				ear, err := tuple.EAR(check.subject)
546
				Expect(err).ShouldNot(HaveOccurred())
547
548
				subject := &base.Subject{
549
					Type:     ear.GetEntity().GetType(),
550
					Id:       ear.GetEntity().GetId(),
551
					Relation: ear.GetRelation(),
552
				}
553
554
				for permission, res := range check.assertions {
555
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
556
						TenantId:   "t1",
557
						Entity:     entity,
558
						Subject:    subject,
559
						Permission: permission,
560
						Metadata: &base.PermissionCheckRequestMetadata{
561
							SnapToken:     token.NewNoopToken().Encode().String(),
562
							SchemaVersion: "",
563
							Depth:         20,
564
						},
565
					})
566
567
					Expect(err).ShouldNot(HaveOccurred())
568
					Expect(res).Should(Equal(response.GetCan()))
569
				}
570
			}
571
		})
572
573
		It("Drive Sample: Case 2", func() {
574
			db, err := factories.DatabaseFactory(
575
				config.Database{
576
					Engine: "memory",
577
				},
578
			)
579
580
			Expect(err).ShouldNot(HaveOccurred())
581
582
			conf, err := newSchema(driveSchema)
583
			Expect(err).ShouldNot(HaveOccurred())
584
585
			schemaWriter := factories.SchemaWriterFactory(db)
586
			err = schemaWriter.WriteSchema(context.Background(), conf)
587
588
			Expect(err).ShouldNot(HaveOccurred())
589
590
			type check struct {
591
				entity     string
592
				subject    string
593
				assertions map[string]base.CheckResult
594
			}
595
596
			tests := struct {
597
				relationships []string
598
				checks        []check
599
			}{
600
				relationships: []string{
601
					"doc:1#owner@user:2",
602
					"doc:1#org@organization:1#...",
603
					"organization:1#admin@user:1",
604
				},
605
				checks: []check{
606
					{
607
						entity:  "doc:1",
608
						subject: "user:1",
609
						assertions: map[string]base.CheckResult{
610
							"update": base.CheckResult_CHECK_RESULT_DENIED,
611
						},
612
					},
613
				},
614
			}
615
616
			schemaReader := factories.SchemaReaderFactory(db)
617
			dataReader := factories.DataReaderFactory(db)
618
			dataWriter := factories.DataWriterFactory(db)
619
620
			// engines cache cache
621
			var engineKeyCache pkgcache.Cache
622
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
623
			Expect(err).ShouldNot(HaveOccurred())
624
625
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
626
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
627
628
			invoker := invoke.NewDirectInvoker(
629
				schemaReader,
630
				dataReader,
631
				checkEngineWithCache,
632
				nil,
633
				nil,
634
				nil,
635
				telemetry.NewNoopMeter(),
636
			)
637
638
			checkEngine.SetInvoker(invoker)
639
640
			var tuples []*base.Tuple
641
642
			for _, relationship := range tests.relationships {
643
				t, err := tuple.Tuple(relationship)
644
				Expect(err).ShouldNot(HaveOccurred())
645
				tuples = append(tuples, t)
646
			}
647
648
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
649
			Expect(err).ShouldNot(HaveOccurred())
650
651
			for _, check := range tests.checks {
652
				entity, err := tuple.E(check.entity)
653
				Expect(err).ShouldNot(HaveOccurred())
654
655
				ear, err := tuple.EAR(check.subject)
656
				Expect(err).ShouldNot(HaveOccurred())
657
658
				subject := &base.Subject{
659
					Type:     ear.GetEntity().GetType(),
660
					Id:       ear.GetEntity().GetId(),
661
					Relation: ear.GetRelation(),
662
				}
663
664
				for permission, res := range check.assertions {
665
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
666
						TenantId:   "t1",
667
						Entity:     entity,
668
						Subject:    subject,
669
						Permission: permission,
670
						Metadata: &base.PermissionCheckRequestMetadata{
671
							SnapToken:     token.NewNoopToken().Encode().String(),
672
							SchemaVersion: "",
673
							Depth:         20,
674
						},
675
					})
676
677
					Expect(err).ShouldNot(HaveOccurred())
678
					Expect(res).Should(Equal(response.GetCan()))
679
				}
680
			}
681
		})
682
683
		It("Drive Sample: Case 3", func() {
684
			db, err := factories.DatabaseFactory(
685
				config.Database{
686
					Engine: "memory",
687
				},
688
			)
689
690
			Expect(err).ShouldNot(HaveOccurred())
691
692
			conf, err := newSchema(driveSchema)
693
			Expect(err).ShouldNot(HaveOccurred())
694
695
			schemaWriter := factories.SchemaWriterFactory(db)
696
			err = schemaWriter.WriteSchema(context.Background(), conf)
697
			Expect(err).ShouldNot(HaveOccurred())
698
699
			type check struct {
700
				entity     string
701
				subject    string
702
				assertions map[string]base.CheckResult
703
			}
704
705
			tests := struct {
706
				relationships []string
707
				checks        []check
708
			}{
709
				relationships: []string{
710
					"doc:1#owner@user:2",
711
					"doc:1#parent@folder:1#...",
712
					"folder:1#collaborator@user:7",
713
					"folder:1#collaborator@user:3",
714
					"doc:1#org@organization:1#...",
715
					"organization:1#admin@user:7",
716
				},
717
				checks: []check{
718
					{
719
						entity:  "doc:1",
720
						subject: "user:1",
721
						assertions: map[string]base.CheckResult{
722
							"read": base.CheckResult_CHECK_RESULT_DENIED,
723
						},
724
					},
725
				},
726
			}
727
728
			schemaReader := factories.SchemaReaderFactory(db)
729
			dataReader := factories.DataReaderFactory(db)
730
			dataWriter := factories.DataWriterFactory(db)
731
732
			// engines cache cache
733
			var engineKeyCache pkgcache.Cache
734
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
735
			Expect(err).ShouldNot(HaveOccurred())
736
737
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
738
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
739
740
			invoker := invoke.NewDirectInvoker(
741
				schemaReader,
742
				dataReader,
743
				checkEngineWithCache,
744
				nil,
745
				nil,
746
				nil,
747
				telemetry.NewNoopMeter(),
748
			)
749
750
			checkEngine.SetInvoker(invoker)
751
752
			var tuples []*base.Tuple
753
754
			for _, relationship := range tests.relationships {
755
				t, err := tuple.Tuple(relationship)
756
				Expect(err).ShouldNot(HaveOccurred())
757
				tuples = append(tuples, t)
758
			}
759
760
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
761
			Expect(err).ShouldNot(HaveOccurred())
762
763
			for _, check := range tests.checks {
764
				entity, err := tuple.E(check.entity)
765
				Expect(err).ShouldNot(HaveOccurred())
766
767
				ear, err := tuple.EAR(check.subject)
768
				Expect(err).ShouldNot(HaveOccurred())
769
770
				subject := &base.Subject{
771
					Type:     ear.GetEntity().GetType(),
772
					Id:       ear.GetEntity().GetId(),
773
					Relation: ear.GetRelation(),
774
				}
775
776
				for permission, res := range check.assertions {
777
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
778
						TenantId:   "t1",
779
						Entity:     entity,
780
						Subject:    subject,
781
						Permission: permission,
782
						Metadata: &base.PermissionCheckRequestMetadata{
783
							SnapToken:     token.NewNoopToken().Encode().String(),
784
							SchemaVersion: "",
785
							Depth:         20,
786
						},
787
					})
788
789
					Expect(err).ShouldNot(HaveOccurred())
790
					Expect(res).Should(Equal(response.GetCan()))
791
				}
792
			}
793
		})
794
	})
795
796
	// GITHUB SAMPLE
797
798
	githubSchema := `
799
		entity user {}
800
	
801
		entity organization {
802
			relation admin @user
803
			relation member @user
804
	
805
			action create_repository = admin or member
806
			action delete = admin
807
		}
808
	
809
		entity repository {
810
			relation parent @organization
811
			relation owner @user
812
	
813
			action push   = owner
814
			action read   = owner and (parent.admin or parent.member)
815
			action delete = parent.member and (parent.admin or owner)
816
		}
817
		`
818
819
	Context("Github Sample: Check", func() {
820
		It("Github Sample: Case 1", func() {
821
			db, err := factories.DatabaseFactory(
822
				config.Database{
823
					Engine: "memory",
824
				},
825
			)
826
827
			Expect(err).ShouldNot(HaveOccurred())
828
829
			conf, err := newSchema(githubSchema)
830
			Expect(err).ShouldNot(HaveOccurred())
831
832
			schemaWriter := factories.SchemaWriterFactory(db)
833
			err = schemaWriter.WriteSchema(context.Background(), conf)
834
			Expect(err).ShouldNot(HaveOccurred())
835
836
			type check struct {
837
				entity     string
838
				subject    string
839
				assertions map[string]base.CheckResult
840
			}
841
842
			tests := struct {
843
				relationships []string
844
				checks        []check
845
			}{
846
				relationships: []string{
847
					"repository:1#owner@user:2",
848
				},
849
				checks: []check{
850
					{
851
						entity:  "repository:1",
852
						subject: "user:1",
853
						assertions: map[string]base.CheckResult{
854
							"push": base.CheckResult_CHECK_RESULT_DENIED,
855
						},
856
					},
857
				},
858
			}
859
860
			schemaReader := factories.SchemaReaderFactory(db)
861
			dataReader := factories.DataReaderFactory(db)
862
			dataWriter := factories.DataWriterFactory(db)
863
864
			// engines cache cache
865
			var engineKeyCache pkgcache.Cache
866
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
867
			Expect(err).ShouldNot(HaveOccurred())
868
869
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
870
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
871
872
			invoker := invoke.NewDirectInvoker(
873
				schemaReader,
874
				dataReader,
875
				checkEngineWithCache,
876
				nil,
877
				nil,
878
				nil,
879
				telemetry.NewNoopMeter(),
880
			)
881
882
			checkEngine.SetInvoker(invoker)
883
884
			var tuples []*base.Tuple
885
886
			for _, relationship := range tests.relationships {
887
				t, err := tuple.Tuple(relationship)
888
				Expect(err).ShouldNot(HaveOccurred())
889
				tuples = append(tuples, t)
890
			}
891
892
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
893
			Expect(err).ShouldNot(HaveOccurred())
894
895
			for _, check := range tests.checks {
896
				entity, err := tuple.E(check.entity)
897
				Expect(err).ShouldNot(HaveOccurred())
898
899
				ear, err := tuple.EAR(check.subject)
900
				Expect(err).ShouldNot(HaveOccurred())
901
902
				subject := &base.Subject{
903
					Type:     ear.GetEntity().GetType(),
904
					Id:       ear.GetEntity().GetId(),
905
					Relation: ear.GetRelation(),
906
				}
907
908
				for permission, res := range check.assertions {
909
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
910
						TenantId:   "t1",
911
						Entity:     entity,
912
						Subject:    subject,
913
						Permission: permission,
914
						Metadata: &base.PermissionCheckRequestMetadata{
915
							SnapToken:     token.NewNoopToken().Encode().String(),
916
							SchemaVersion: "",
917
							Depth:         20,
918
						},
919
					})
920
921
					Expect(err).ShouldNot(HaveOccurred())
922
					Expect(res).Should(Equal(response.GetCan()))
923
				}
924
			}
925
		})
926
927
		It("Github Sample: Case 2", func() {
928
			db, err := factories.DatabaseFactory(
929
				config.Database{
930
					Engine: "memory",
931
				},
932
			)
933
934
			Expect(err).ShouldNot(HaveOccurred())
935
936
			conf, err := newSchema(githubSchema)
937
			Expect(err).ShouldNot(HaveOccurred())
938
939
			schemaWriter := factories.SchemaWriterFactory(db)
940
			err = schemaWriter.WriteSchema(context.Background(), conf)
941
			Expect(err).ShouldNot(HaveOccurred())
942
943
			type check struct {
944
				entity     string
945
				subject    string
946
				assertions map[string]base.CheckResult
947
			}
948
949
			tests := struct {
950
				relationships []string
951
				checks        []check
952
			}{
953
				relationships: []string{
954
					"repository:1#owner@organization:2#admin",
955
					"organization:2#admin@organization:3#member",
956
					"organization:2#admin@user:3",
957
					"organization:2#admin@user:8",
958
					"organization:3#member@user:1",
959
				},
960
				checks: []check{
961
					{
962
						entity:  "repository:1",
963
						subject: "user:1",
964
						assertions: map[string]base.CheckResult{
965
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
966
						},
967
					},
968
					{
969
						entity:  "repository:1",
970
						subject: "user:1",
971
						assertions: map[string]base.CheckResult{
972
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
973
						},
974
					},
975
					{
976
						entity:  "repository:1",
977
						subject: "user:1",
978
						assertions: map[string]base.CheckResult{
979
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
980
						},
981
					},
982
				},
983
			}
984
985
			schemaReader := factories.SchemaReaderFactory(db)
986
			dataReader := factories.DataReaderFactory(db)
987
988
			// engines cache cache
989
			var engineKeyCache pkgcache.Cache
990
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
991
			Expect(err).ShouldNot(HaveOccurred())
992
993
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
994
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
995
996
			invoker := invoke.NewDirectInvoker(
997
				schemaReader,
998
				dataReader,
999
				checkEngineWithCache,
1000
				nil,
1001
				nil,
1002
				nil,
1003
				telemetry.NewNoopMeter(),
1004
			)
1005
1006
			checkEngine.SetInvoker(invoker)
1007
1008
			var tuples []*base.Tuple
1009
1010
			for _, relationship := range tests.relationships {
1011
				t, err := tuple.Tuple(relationship)
1012
				Expect(err).ShouldNot(HaveOccurred())
1013
				tuples = append(tuples, t)
1014
			}
1015
1016
			for _, check := range tests.checks {
1017
				entity, err := tuple.E(check.entity)
1018
				Expect(err).ShouldNot(HaveOccurred())
1019
1020
				ear, err := tuple.EAR(check.subject)
1021
				Expect(err).ShouldNot(HaveOccurred())
1022
1023
				subject := &base.Subject{
1024
					Type:     ear.GetEntity().GetType(),
1025
					Id:       ear.GetEntity().GetId(),
1026
					Relation: ear.GetRelation(),
1027
				}
1028
1029
				for permission, res := range check.assertions {
1030
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1031
						TenantId:   "t1",
1032
						Entity:     entity,
1033
						Subject:    subject,
1034
						Permission: permission,
1035
						Metadata: &base.PermissionCheckRequestMetadata{
1036
							SnapToken:     token.NewNoopToken().Encode().String(),
1037
							SchemaVersion: "",
1038
							Depth:         20,
1039
						},
1040
						Context: &base.Context{
1041
							Tuples: tuples,
1042
						},
1043
					})
1044
1045
					Expect(err).ShouldNot(HaveOccurred())
1046
					Expect(res).Should(Equal(response.GetCan()))
1047
				}
1048
			}
1049
		})
1050
1051
		It("Github Sample: Case 3", func() {
1052
			db, err := factories.DatabaseFactory(
1053
				config.Database{
1054
					Engine: "memory",
1055
				},
1056
			)
1057
1058
			Expect(err).ShouldNot(HaveOccurred())
1059
1060
			conf, err := newSchema(githubSchema)
1061
			Expect(err).ShouldNot(HaveOccurred())
1062
1063
			schemaWriter := factories.SchemaWriterFactory(db)
1064
			err = schemaWriter.WriteSchema(context.Background(), conf)
1065
			Expect(err).ShouldNot(HaveOccurred())
1066
1067
			type check struct {
1068
				entity     string
1069
				subject    string
1070
				assertions map[string]base.CheckResult
1071
			}
1072
1073
			tests := struct {
1074
				relationships []string
1075
				checks        []check
1076
			}{
1077
				relationships: []string{
1078
					"repository:1#parent@organization:8#...",
1079
					"organization:8#member@user:1",
1080
					"organization:8#admin@user:2",
1081
					"repository:1#owner@user:7",
1082
				},
1083
				checks: []check{
1084
					{
1085
						entity:  "repository:1",
1086
						subject: "user:1",
1087
						assertions: map[string]base.CheckResult{
1088
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
1089
						},
1090
					},
1091
					{
1092
						entity:  "repository:1",
1093
						subject: "user:1",
1094
						assertions: map[string]base.CheckResult{
1095
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
1096
						},
1097
					},
1098
				},
1099
			}
1100
1101
			schemaReader := factories.SchemaReaderFactory(db)
1102
			dataReader := factories.DataReaderFactory(db)
1103
			dataWriter := factories.DataWriterFactory(db)
1104
1105
			// engines cache cache
1106
			var engineKeyCache pkgcache.Cache
1107
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1108
			Expect(err).ShouldNot(HaveOccurred())
1109
1110
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1111
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1112
1113
			invoker := invoke.NewDirectInvoker(
1114
				schemaReader,
1115
				dataReader,
1116
				checkEngineWithCache,
1117
				nil,
1118
				nil,
1119
				nil,
1120
				telemetry.NewNoopMeter(),
1121
			)
1122
1123
			checkEngine.SetInvoker(invoker)
1124
1125
			var tuples []*base.Tuple
1126
1127
			for _, relationship := range tests.relationships {
1128
				t, err := tuple.Tuple(relationship)
1129
				Expect(err).ShouldNot(HaveOccurred())
1130
				tuples = append(tuples, t)
1131
			}
1132
1133
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1134
			Expect(err).ShouldNot(HaveOccurred())
1135
1136
			for _, check := range tests.checks {
1137
				entity, err := tuple.E(check.entity)
1138
				Expect(err).ShouldNot(HaveOccurred())
1139
1140
				ear, err := tuple.EAR(check.subject)
1141
				Expect(err).ShouldNot(HaveOccurred())
1142
1143
				subject := &base.Subject{
1144
					Type:     ear.GetEntity().GetType(),
1145
					Id:       ear.GetEntity().GetId(),
1146
					Relation: ear.GetRelation(),
1147
				}
1148
1149
				for permission, res := range check.assertions {
1150
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1151
						TenantId:   "t1",
1152
						Entity:     entity,
1153
						Subject:    subject,
1154
						Permission: permission,
1155
						Metadata: &base.PermissionCheckRequestMetadata{
1156
							SnapToken:     token.NewNoopToken().Encode().String(),
1157
							SchemaVersion: "",
1158
							Depth:         20,
1159
						},
1160
					})
1161
1162
					Expect(err).ShouldNot(HaveOccurred())
1163
					Expect(res).Should(Equal(response.GetCan()))
1164
				}
1165
			}
1166
		})
1167
	})
1168
1169
	// EXCLUSION SAMPLE
1170
1171
	exclusionSchema := `
1172
	entity user {}
1173
	
1174
	entity organization {
1175
		relation member @user
1176
	}
1177
	
1178
	entity parent {
1179
		relation member @user
1180
		relation admin @user
1181
	}
1182
	
1183
	entity repo {
1184
		relation org @organization
1185
		relation parent @parent
1186
		relation member @user
1187
	
1188
		permission push   = org.member not parent.member
1189
		permission delete = push
1190
	
1191
		permission update = (org.member not parent.member) and member
1192
		permission view = member not update
1193
		permission manage = update
1194
		permission admin = manage
1195
	}
1196
	`
1197
1198
	Context("Exclusion Sample: Check", func() {
1199
		It("Exclusion Sample: Case 1", func() {
1200
			db, err := factories.DatabaseFactory(
1201
				config.Database{
1202
					Engine: "memory",
1203
				},
1204
			)
1205
1206
			Expect(err).ShouldNot(HaveOccurred())
1207
1208
			conf, err := newSchema(exclusionSchema)
1209
			Expect(err).ShouldNot(HaveOccurred())
1210
1211
			schemaWriter := factories.SchemaWriterFactory(db)
1212
			err = schemaWriter.WriteSchema(context.Background(), conf)
1213
			Expect(err).ShouldNot(HaveOccurred())
1214
1215
			type check struct {
1216
				entity     string
1217
				subject    string
1218
				assertions map[string]base.CheckResult
1219
			}
1220
1221
			tests := struct {
1222
				relationships []string
1223
				checks        []check
1224
			}{
1225
				relationships: []string{
1226
					"organization:1#member@user:1",
1227
					"organization:1#member@user:2",
1228
					"parent:1#member@user:1",
1229
					"repo:1#org@organization:1#...",
1230
					"repo:1#parent@parent:1#...",
1231
				},
1232
				checks: []check{
1233
					{
1234
						entity:  "repo:1",
1235
						subject: "user:2",
1236
						assertions: map[string]base.CheckResult{
1237
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
1238
						},
1239
					},
1240
					{
1241
						entity:  "repo:1",
1242
						subject: "user:2",
1243
						assertions: map[string]base.CheckResult{
1244
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
1245
						},
1246
					},
1247
					{
1248
						entity:  "repo:1",
1249
						subject: "user:2",
1250
						assertions: map[string]base.CheckResult{
1251
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
1252
						},
1253
					},
1254
				},
1255
			}
1256
1257
			schemaReader := factories.SchemaReaderFactory(db)
1258
			dataReader := factories.DataReaderFactory(db)
1259
1260
			// engines cache cache
1261
			var engineKeyCache pkgcache.Cache
1262
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1263
			Expect(err).ShouldNot(HaveOccurred())
1264
1265
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1266
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1267
1268
			invoker := invoke.NewDirectInvoker(
1269
				schemaReader,
1270
				dataReader,
1271
				checkEngineWithCache,
1272
				nil,
1273
				nil,
1274
				nil,
1275
				telemetry.NewNoopMeter(),
1276
			)
1277
1278
			checkEngine.SetInvoker(invoker)
1279
1280
			var tuples []*base.Tuple
1281
1282
			for _, relationship := range tests.relationships {
1283
				t, err := tuple.Tuple(relationship)
1284
				Expect(err).ShouldNot(HaveOccurred())
1285
				tuples = append(tuples, t)
1286
			}
1287
1288
			for _, check := range tests.checks {
1289
				entity, err := tuple.E(check.entity)
1290
				Expect(err).ShouldNot(HaveOccurred())
1291
1292
				ear, err := tuple.EAR(check.subject)
1293
				Expect(err).ShouldNot(HaveOccurred())
1294
1295
				subject := &base.Subject{
1296
					Type:     ear.GetEntity().GetType(),
1297
					Id:       ear.GetEntity().GetId(),
1298
					Relation: ear.GetRelation(),
1299
				}
1300
1301
				for permission, res := range check.assertions {
1302
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1303
						TenantId:   "t1",
1304
						Entity:     entity,
1305
						Subject:    subject,
1306
						Permission: permission,
1307
						Metadata: &base.PermissionCheckRequestMetadata{
1308
							SnapToken:     token.NewNoopToken().Encode().String(),
1309
							SchemaVersion: "",
1310
							Depth:         20,
1311
						},
1312
						Context: &base.Context{
1313
							Tuples: tuples,
1314
						},
1315
					})
1316
1317
					Expect(err).ShouldNot(HaveOccurred())
1318
					Expect(res).Should(Equal(response.GetCan()))
1319
				}
1320
			}
1321
		})
1322
1323
		It("Exclusion Sample: Case 2", func() {
1324
			db, err := factories.DatabaseFactory(
1325
				config.Database{
1326
					Engine: "memory",
1327
				},
1328
			)
1329
1330
			Expect(err).ShouldNot(HaveOccurred())
1331
1332
			conf, err := newSchema(exclusionSchema)
1333
			Expect(err).ShouldNot(HaveOccurred())
1334
1335
			schemaWriter := factories.SchemaWriterFactory(db)
1336
			err = schemaWriter.WriteSchema(context.Background(), conf)
1337
			Expect(err).ShouldNot(HaveOccurred())
1338
1339
			type check struct {
1340
				entity     string
1341
				subject    string
1342
				assertions map[string]base.CheckResult
1343
			}
1344
1345
			tests := struct {
1346
				relationships []string
1347
				contextual    []string
1348
				checks        []check
1349
			}{
1350
				relationships: []string{
1351
					"organization:1#member@user:1",
1352
					"organization:1#member@user:2",
1353
					"parent:1#admin@user:2",
1354
					"parent:1#member@user:1",
1355
					"parent:1#member@parent:1#admin",
1356
					"repo:1#org@organization:1#...",
1357
					"repo:1#parent@parent:1#...",
1358
				},
1359
				contextual: []string{
1360
					"parent:1#member@parent:1#admin",
1361
					"repo:1#org@organization:1#...",
1362
					"repo:1#parent@parent:1#...",
1363
				},
1364
				checks: []check{
1365
					{
1366
						entity:  "repo:1",
1367
						subject: "user:2",
1368
						assertions: map[string]base.CheckResult{
1369
							"push": base.CheckResult_CHECK_RESULT_DENIED,
1370
						},
1371
					},
1372
					{
1373
						entity:  "repo:1",
1374
						subject: "user:2",
1375
						assertions: map[string]base.CheckResult{
1376
							"push": base.CheckResult_CHECK_RESULT_DENIED,
1377
						},
1378
					},
1379
				},
1380
			}
1381
1382
			schemaReader := factories.SchemaReaderFactory(db)
1383
			dataReader := factories.DataReaderFactory(db)
1384
			dataWriter := factories.DataWriterFactory(db)
1385
1386
			// engines cache cache
1387
			var engineKeyCache pkgcache.Cache
1388
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1389
			Expect(err).ShouldNot(HaveOccurred())
1390
1391
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1392
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1393
1394
			invoker := invoke.NewDirectInvoker(
1395
				schemaReader,
1396
				dataReader,
1397
				checkEngineWithCache,
1398
				nil,
1399
				nil,
1400
				nil,
1401
				telemetry.NewNoopMeter(),
1402
			)
1403
1404
			checkEngine.SetInvoker(invoker)
1405
1406
			var tuples []*base.Tuple
1407
1408
			for _, relationship := range tests.relationships {
1409
				t, err := tuple.Tuple(relationship)
1410
				Expect(err).ShouldNot(HaveOccurred())
1411
				tuples = append(tuples, t)
1412
			}
1413
1414
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1415
			Expect(err).ShouldNot(HaveOccurred())
1416
1417
			var contextual []*base.Tuple
1418
1419
			for _, relationship := range tests.contextual {
1420
				t, err := tuple.Tuple(relationship)
1421
				Expect(err).ShouldNot(HaveOccurred())
1422
				contextual = append(contextual, t)
1423
			}
1424
1425
			for _, check := range tests.checks {
1426
				entity, err := tuple.E(check.entity)
1427
				Expect(err).ShouldNot(HaveOccurred())
1428
1429
				ear, err := tuple.EAR(check.subject)
1430
				Expect(err).ShouldNot(HaveOccurred())
1431
1432
				subject := &base.Subject{
1433
					Type:     ear.GetEntity().GetType(),
1434
					Id:       ear.GetEntity().GetId(),
1435
					Relation: ear.GetRelation(),
1436
				}
1437
1438
				for permission, res := range check.assertions {
1439
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1440
						TenantId:   "t1",
1441
						Entity:     entity,
1442
						Subject:    subject,
1443
						Permission: permission,
1444
						Metadata: &base.PermissionCheckRequestMetadata{
1445
							SnapToken:     token.NewNoopToken().Encode().String(),
1446
							SchemaVersion: "",
1447
							Depth:         20,
1448
						},
1449
						Context: &base.Context{
1450
							Tuples: contextual,
1451
						},
1452
					})
1453
1454
					Expect(err).ShouldNot(HaveOccurred())
1455
					Expect(res).Should(Equal(response.GetCan()))
1456
				}
1457
			}
1458
		})
1459
1460
		It("Exclusion Sample: Case 3", func() {
1461
			db, err := factories.DatabaseFactory(
1462
				config.Database{
1463
					Engine: "memory",
1464
				},
1465
			)
1466
			Expect(err).ShouldNot(HaveOccurred())
1467
1468
			conf, err := newSchema(exclusionSchema)
1469
			Expect(err).ShouldNot(HaveOccurred())
1470
1471
			schemaWriter := factories.SchemaWriterFactory(db)
1472
			err = schemaWriter.WriteSchema(context.Background(), conf)
1473
			Expect(err).ShouldNot(HaveOccurred())
1474
1475
			type check struct {
1476
				entity     string
1477
				subject    string
1478
				assertions map[string]base.CheckResult
1479
			}
1480
1481
			tests := struct {
1482
				relationships []string
1483
				checks        []check
1484
			}{
1485
				relationships: []string{
1486
					"organization:1#member@user:1",
1487
					"organization:1#member@user:2",
1488
					"parent:1#admin@user:2",
1489
					"parent:1#member@user:1",
1490
					"parent:1#member@user:2",
1491
					"repo:1#org@organization:1#...",
1492
					"repo:1#parent@parent:1#...",
1493
				},
1494
				checks: []check{
1495
					{
1496
						entity:  "repo:1",
1497
						subject: "user:2",
1498
						assertions: map[string]base.CheckResult{
1499
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
1500
						},
1501
					},
1502
					{
1503
						entity:  "repo:1",
1504
						subject: "user:2",
1505
						assertions: map[string]base.CheckResult{
1506
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
1507
						},
1508
					},
1509
					{
1510
						entity:  "repo:1",
1511
						subject: "user:2",
1512
						assertions: map[string]base.CheckResult{
1513
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
1514
						},
1515
					},
1516
				},
1517
			}
1518
1519
			schemaReader := factories.SchemaReaderFactory(db)
1520
			dataReader := factories.DataReaderFactory(db)
1521
			dataWriter := factories.DataWriterFactory(db)
1522
1523
			// engines cache cache
1524
			var engineKeyCache pkgcache.Cache
1525
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1526
			Expect(err).ShouldNot(HaveOccurred())
1527
1528
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1529
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1530
1531
			invoker := invoke.NewDirectInvoker(
1532
				schemaReader,
1533
				dataReader,
1534
				checkEngineWithCache,
1535
				nil,
1536
				nil,
1537
				nil,
1538
				telemetry.NewNoopMeter(),
1539
			)
1540
1541
			checkEngine.SetInvoker(invoker)
1542
1543
			var tuples []*base.Tuple
1544
1545
			for _, relationship := range tests.relationships {
1546
				t, err := tuple.Tuple(relationship)
1547
				Expect(err).ShouldNot(HaveOccurred())
1548
				tuples = append(tuples, t)
1549
			}
1550
1551
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1552
			Expect(err).ShouldNot(HaveOccurred())
1553
1554
			for _, check := range tests.checks {
1555
				entity, err := tuple.E(check.entity)
1556
				Expect(err).ShouldNot(HaveOccurred())
1557
1558
				ear, err := tuple.EAR(check.subject)
1559
				Expect(err).ShouldNot(HaveOccurred())
1560
1561
				subject := &base.Subject{
1562
					Type:     ear.GetEntity().GetType(),
1563
					Id:       ear.GetEntity().GetId(),
1564
					Relation: ear.GetRelation(),
1565
				}
1566
1567
				for permission, res := range check.assertions {
1568
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1569
						TenantId:   "t1",
1570
						Entity:     entity,
1571
						Subject:    subject,
1572
						Permission: permission,
1573
						Metadata: &base.PermissionCheckRequestMetadata{
1574
							SnapToken:     token.NewNoopToken().Encode().String(),
1575
							SchemaVersion: "",
1576
							Depth:         20,
1577
						},
1578
					})
1579
1580
					Expect(err).ShouldNot(HaveOccurred())
1581
					Expect(res).Should(Equal(response.GetCan()))
1582
				}
1583
			}
1584
		})
1585
1586
		It("Exclusion Sample: Case 4", func() {
1587
			db, err := factories.DatabaseFactory(
1588
				config.Database{
1589
					Engine: "memory",
1590
				},
1591
			)
1592
			Expect(err).ShouldNot(HaveOccurred())
1593
1594
			conf, err := newSchema(exclusionSchema)
1595
			Expect(err).ShouldNot(HaveOccurred())
1596
1597
			schemaWriter := factories.SchemaWriterFactory(db)
1598
			err = schemaWriter.WriteSchema(context.Background(), conf)
1599
			Expect(err).ShouldNot(HaveOccurred())
1600
1601
			type check struct {
1602
				entity     string
1603
				subject    string
1604
				assertions map[string]base.CheckResult
1605
			}
1606
1607
			tests := struct {
1608
				relationships []string
1609
				checks        []check
1610
			}{
1611
				relationships: []string{
1612
					"organization:1#member@user:1",
1613
1614
					"parent:1#admin@user:2",
1615
					"parent:1#member@user:1",
1616
					"parent:1#member@parent:1#admin",
1617
1618
					"repo:1#org@organization:1#...",
1619
					"repo:1#parent@parent:1#...",
1620
					"repo:1#member@user:2",
1621
				},
1622
				checks: []check{
1623
					{
1624
						entity:  "repo:1",
1625
						subject: "user:2",
1626
						assertions: map[string]base.CheckResult{
1627
							"update": base.CheckResult_CHECK_RESULT_DENIED,
1628
						},
1629
					},
1630
					{
1631
						entity:  "repo:1",
1632
						subject: "user:2",
1633
						assertions: map[string]base.CheckResult{
1634
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
1635
						},
1636
					},
1637
					{
1638
						entity:  "repo:1",
1639
						subject: "user:2",
1640
						assertions: map[string]base.CheckResult{
1641
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
1642
						},
1643
					},
1644
				},
1645
			}
1646
1647
			schemaReader := factories.SchemaReaderFactory(db)
1648
			dataReader := factories.DataReaderFactory(db)
1649
			dataWriter := factories.DataWriterFactory(db)
1650
1651
			// engines cache cache
1652
			var engineKeyCache pkgcache.Cache
1653
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1654
			Expect(err).ShouldNot(HaveOccurred())
1655
1656
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1657
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1658
1659
			invoker := invoke.NewDirectInvoker(
1660
				schemaReader,
1661
				dataReader,
1662
				checkEngineWithCache,
1663
				nil,
1664
				nil,
1665
				nil,
1666
				telemetry.NewNoopMeter(),
1667
			)
1668
1669
			checkEngine.SetInvoker(invoker)
1670
1671
			var tuples []*base.Tuple
1672
1673
			for _, relationship := range tests.relationships {
1674
				t, err := tuple.Tuple(relationship)
1675
				Expect(err).ShouldNot(HaveOccurred())
1676
				tuples = append(tuples, t)
1677
			}
1678
1679
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1680
			Expect(err).ShouldNot(HaveOccurred())
1681
1682
			for _, check := range tests.checks {
1683
				entity, err := tuple.E(check.entity)
1684
				Expect(err).ShouldNot(HaveOccurred())
1685
1686
				ear, err := tuple.EAR(check.subject)
1687
				Expect(err).ShouldNot(HaveOccurred())
1688
1689
				subject := &base.Subject{
1690
					Type:     ear.GetEntity().GetType(),
1691
					Id:       ear.GetEntity().GetId(),
1692
					Relation: ear.GetRelation(),
1693
				}
1694
1695
				for permission, res := range check.assertions {
1696
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1697
						TenantId:   "t1",
1698
						Entity:     entity,
1699
						Subject:    subject,
1700
						Permission: permission,
1701
						Metadata: &base.PermissionCheckRequestMetadata{
1702
							SnapToken:     token.NewNoopToken().Encode().String(),
1703
							SchemaVersion: "",
1704
							Depth:         20,
1705
						},
1706
					})
1707
1708
					Expect(err).ShouldNot(HaveOccurred())
1709
					Expect(res).Should(Equal(response.GetCan()))
1710
				}
1711
			}
1712
		})
1713
1714
		It("Exclusion Sample: Case 5", func() {
1715
			db, err := factories.DatabaseFactory(
1716
				config.Database{
1717
					Engine: "memory",
1718
				},
1719
			)
1720
			Expect(err).ShouldNot(HaveOccurred())
1721
1722
			conf, err := newSchema(exclusionSchema)
1723
			Expect(err).ShouldNot(HaveOccurred())
1724
1725
			schemaWriter := factories.SchemaWriterFactory(db)
1726
			err = schemaWriter.WriteSchema(context.Background(), conf)
1727
			Expect(err).ShouldNot(HaveOccurred())
1728
1729
			type check struct {
1730
				entity     string
1731
				subject    string
1732
				assertions map[string]base.CheckResult
1733
			}
1734
1735
			tests := struct {
1736
				relationships []string
1737
				checks        []check
1738
			}{
1739
				relationships: []string{
1740
					"organization:1#member@user:1",
1741
					"parent:1#admin@user:2",
1742
					"parent:1#member@user:1",
1743
					"parent:1#member@parent:1#admin",
1744
					"repo:1#org@organization:1#...",
1745
					"repo:1#parent@parent:1#...",
1746
				},
1747
				checks: []check{
1748
					{
1749
						entity:  "repo:1",
1750
						subject: "user:2",
1751
						assertions: map[string]base.CheckResult{
1752
							"view": base.CheckResult_CHECK_RESULT_DENIED,
1753
						},
1754
					},
1755
					{
1756
						entity:  "repo:1",
1757
						subject: "user:2",
1758
						assertions: map[string]base.CheckResult{
1759
							"view": base.CheckResult_CHECK_RESULT_DENIED,
1760
						},
1761
					},
1762
				},
1763
			}
1764
1765
			schemaReader := factories.SchemaReaderFactory(db)
1766
			dataReader := factories.DataReaderFactory(db)
1767
			dataWriter := factories.DataWriterFactory(db)
1768
1769
			// engines cache cache
1770
			var engineKeyCache pkgcache.Cache
1771
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1772
			Expect(err).ShouldNot(HaveOccurred())
1773
1774
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1775
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1776
1777
			invoker := invoke.NewDirectInvoker(
1778
				schemaReader,
1779
				dataReader,
1780
				checkEngineWithCache,
1781
				nil,
1782
				nil,
1783
				nil,
1784
				telemetry.NewNoopMeter(),
1785
			)
1786
1787
			checkEngine.SetInvoker(invoker)
1788
1789
			var tuples []*base.Tuple
1790
1791
			for _, relationship := range tests.relationships {
1792
				t, err := tuple.Tuple(relationship)
1793
				Expect(err).ShouldNot(HaveOccurred())
1794
				tuples = append(tuples, t)
1795
			}
1796
1797
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1798
			Expect(err).ShouldNot(HaveOccurred())
1799
1800
			for _, check := range tests.checks {
1801
				entity, err := tuple.E(check.entity)
1802
				Expect(err).ShouldNot(HaveOccurred())
1803
1804
				ear, err := tuple.EAR(check.subject)
1805
				Expect(err).ShouldNot(HaveOccurred())
1806
1807
				subject := &base.Subject{
1808
					Type:     ear.GetEntity().GetType(),
1809
					Id:       ear.GetEntity().GetId(),
1810
					Relation: ear.GetRelation(),
1811
				}
1812
1813
				for permission, res := range check.assertions {
1814
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1815
						TenantId:   "t1",
1816
						Entity:     entity,
1817
						Subject:    subject,
1818
						Permission: permission,
1819
						Metadata: &base.PermissionCheckRequestMetadata{
1820
							SnapToken:     token.NewNoopToken().Encode().String(),
1821
							SchemaVersion: "",
1822
							Depth:         20,
1823
						},
1824
					})
1825
1826
					Expect(err).ShouldNot(HaveOccurred())
1827
					Expect(res).Should(Equal(response.GetCan()))
1828
				}
1829
			}
1830
		})
1831
1832
		It("Exclusion Sample: Case 6", func() {
1833
			db, err := factories.DatabaseFactory(
1834
				config.Database{
1835
					Engine: "memory",
1836
				},
1837
			)
1838
			Expect(err).ShouldNot(HaveOccurred())
1839
1840
			conf, err := newSchema(exclusionSchema)
1841
			Expect(err).ShouldNot(HaveOccurred())
1842
1843
			schemaWriter := factories.SchemaWriterFactory(db)
1844
			err = schemaWriter.WriteSchema(context.Background(), conf)
1845
			Expect(err).ShouldNot(HaveOccurred())
1846
1847
			type check struct {
1848
				entity     string
1849
				subject    string
1850
				assertions map[string]base.CheckResult
1851
			}
1852
1853
			tests := struct {
1854
				relationships []string
1855
				checks        []check
1856
			}{
1857
				relationships: []string{
1858
					"organization:1#member@user:1",
1859
					"parent:1#admin@user:2",
1860
					"parent:1#member@user:1",
1861
					"parent:1#member@parent:1#admin",
1862
					"repo:1#org@organization:1#...",
1863
					"repo:1#parent@parent:1#...",
1864
				},
1865
				checks: []check{
1866
					{
1867
						entity:  "repo:1",
1868
						subject: "user:2",
1869
						assertions: map[string]base.CheckResult{
1870
							"admin": base.CheckResult_CHECK_RESULT_DENIED,
1871
						},
1872
					},
1873
					{
1874
						entity:  "repo:1",
1875
						subject: "user:2",
1876
						assertions: map[string]base.CheckResult{
1877
							"admin": base.CheckResult_CHECK_RESULT_DENIED,
1878
						},
1879
					},
1880
				},
1881
			}
1882
1883
			schemaReader := factories.SchemaReaderFactory(db)
1884
			dataReader := factories.DataReaderFactory(db)
1885
			dataWriter := factories.DataWriterFactory(db)
1886
1887
			// engines cache cache
1888
			var engineKeyCache pkgcache.Cache
1889
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
1890
			Expect(err).ShouldNot(HaveOccurred())
1891
1892
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
1893
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
1894
1895
			invoker := invoke.NewDirectInvoker(
1896
				schemaReader,
1897
				dataReader,
1898
				checkEngineWithCache,
1899
				nil,
1900
				nil,
1901
				nil,
1902
				telemetry.NewNoopMeter(),
1903
			)
1904
1905
			checkEngine.SetInvoker(invoker)
1906
1907
			var tuples []*base.Tuple
1908
1909
			for _, relationship := range tests.relationships {
1910
				t, err := tuple.Tuple(relationship)
1911
				Expect(err).ShouldNot(HaveOccurred())
1912
				tuples = append(tuples, t)
1913
			}
1914
1915
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection())
1916
			Expect(err).ShouldNot(HaveOccurred())
1917
1918
			for _, check := range tests.checks {
1919
				entity, err := tuple.E(check.entity)
1920
				Expect(err).ShouldNot(HaveOccurred())
1921
1922
				ear, err := tuple.EAR(check.subject)
1923
				Expect(err).ShouldNot(HaveOccurred())
1924
1925
				subject := &base.Subject{
1926
					Type:     ear.GetEntity().GetType(),
1927
					Id:       ear.GetEntity().GetId(),
1928
					Relation: ear.GetRelation(),
1929
				}
1930
1931
				for permission, res := range check.assertions {
1932
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
1933
						TenantId:   "t1",
1934
						Entity:     entity,
1935
						Subject:    subject,
1936
						Permission: permission,
1937
						Metadata: &base.PermissionCheckRequestMetadata{
1938
							SnapToken:     token.NewNoopToken().Encode().String(),
1939
							SchemaVersion: "",
1940
							Depth:         20,
1941
						},
1942
					})
1943
1944
					Expect(err).ShouldNot(HaveOccurred())
1945
					Expect(res).Should(Equal(response.GetCan()))
1946
				}
1947
			}
1948
		})
1949
	})
1950
1951
	// POLYMORPHIC RELATIONS SAMPLE
1952
1953
	polymorphicRelationsSchema := `
1954
	entity googleuser {}
1955
	
1956
	entity facebookuser {}
1957
	
1958
	entity company {
1959
		relation member @googleuser @facebookuser
1960
	}
1961
	
1962
	entity organization {
1963
		relation member @googleuser @facebookuser
1964
	
1965
		action edit = member
1966
	}
1967
	
1968
	entity repo {
1969
		relation parent @company @organization
1970
	
1971
		permission push   = parent.member
1972
		permission delete = push
1973
	}
1974
	`
1975
1976
	Context("Polymorphic Relations Sample: Check", func() {
1977
		It("Polymorphic Relations Sample: Case 1", func() {
1978
			db, err := factories.DatabaseFactory(
1979
				config.Database{
1980
					Engine: "memory",
1981
				},
1982
			)
1983
1984
			Expect(err).ShouldNot(HaveOccurred())
1985
1986
			conf, err := newSchema(polymorphicRelationsSchema)
1987
			Expect(err).ShouldNot(HaveOccurred())
1988
1989
			schemaWriter := factories.SchemaWriterFactory(db)
1990
			err = schemaWriter.WriteSchema(context.Background(), conf)
1991
			Expect(err).ShouldNot(HaveOccurred())
1992
1993
			type check struct {
1994
				entity     string
1995
				subject    string
1996
				assertions map[string]base.CheckResult
1997
			}
1998
1999
			tests := struct {
2000
				relationships []string
2001
				checks        []check
2002
			}{
2003
				relationships: []string{
2004
					"repo:1#parent@organization:1",
2005
					"repo:1#parent@company:1",
2006
					"company:1#member@googleuser:2",
2007
					"organization:1#member@facebookuser:3",
2008
				},
2009
				checks: []check{
2010
					{
2011
						entity:  "repo:1",
2012
						subject: "googleuser:2",
2013
						assertions: map[string]base.CheckResult{
2014
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
2015
						},
2016
					},
2017
					{
2018
						entity:  "repo:1",
2019
						subject: "facebookuser:3",
2020
						assertions: map[string]base.CheckResult{
2021
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
2022
						},
2023
					},
2024
					{
2025
						entity:  "repo:1",
2026
						subject: "facebookuser:3",
2027
						assertions: map[string]base.CheckResult{
2028
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
2029
						},
2030
					},
2031
					{
2032
						entity:  "repo:1",
2033
						subject: "facebookuser:3",
2034
						assertions: map[string]base.CheckResult{
2035
							"push": base.CheckResult_CHECK_RESULT_ALLOWED,
2036
						},
2037
					},
2038
					{
2039
						entity:  "organization:1",
2040
						subject: "facebookuser:3",
2041
						assertions: map[string]base.CheckResult{
2042
							"edit": base.CheckResult_CHECK_RESULT_ALLOWED,
2043
						},
2044
					},
2045
				},
2046
			}
2047
2048
			schemaReader := factories.SchemaReaderFactory(db)
2049
			dataReader := factories.DataReaderFactory(db)
2050
2051
			// engines cache cache
2052
			var engineKeyCache pkgcache.Cache
2053
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
2054
			Expect(err).ShouldNot(HaveOccurred())
2055
2056
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
2057
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
2058
2059
			invoker := invoke.NewDirectInvoker(
2060
				schemaReader,
2061
				dataReader,
2062
				checkEngineWithCache,
2063
				nil,
2064
				nil,
2065
				nil,
2066
				telemetry.NewNoopMeter(),
2067
			)
2068
2069
			checkEngine.SetInvoker(invoker)
2070
2071
			var tuples []*base.Tuple
2072
2073
			for _, relationship := range tests.relationships {
2074
				t, err := tuple.Tuple(relationship)
2075
				Expect(err).ShouldNot(HaveOccurred())
2076
				tuples = append(tuples, t)
2077
			}
2078
2079
			for _, check := range tests.checks {
2080
				entity, err := tuple.E(check.entity)
2081
				Expect(err).ShouldNot(HaveOccurred())
2082
2083
				ear, err := tuple.EAR(check.subject)
2084
				Expect(err).ShouldNot(HaveOccurred())
2085
2086
				subject := &base.Subject{
2087
					Type:     ear.GetEntity().GetType(),
2088
					Id:       ear.GetEntity().GetId(),
2089
					Relation: ear.GetRelation(),
2090
				}
2091
2092
				for permission, res := range check.assertions {
2093
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
2094
						TenantId:   "t1",
2095
						Entity:     entity,
2096
						Subject:    subject,
2097
						Permission: permission,
2098
						Metadata: &base.PermissionCheckRequestMetadata{
2099
							SnapToken:     token.NewNoopToken().Encode().String(),
2100
							SchemaVersion: "",
2101
							Depth:         20,
2102
						},
2103
						Context: &base.Context{
2104
							Tuples: tuples,
2105
						},
2106
					})
2107
2108
					Expect(err).ShouldNot(HaveOccurred())
2109
					Expect(res).Should(Equal(response.GetCan()))
2110
				}
2111
			}
2112
		})
2113
	})
2114
2115
	// WEEKDAY SAMPLE
2116
	weekdaySchema := `
2117
		entity user {}
2118
		
2119
		entity organization {
2120
		
2121
			relation member @user
2122
		
2123
			attribute balance integer
2124
2125
			permission view = check_balance(balance) and member
2126
		}
2127
		
2128
		entity repository {
2129
		
2130
			relation organization  @organization
2131
			
2132
			attribute is_public boolean
2133
2134
			permission view = is_public
2135
			permission edit = organization.view
2136
			permission delete = is_weekday(request.day_of_week)
2137
		}
2138
		
2139
		rule check_balance(balance integer) {
2140
			balance > 5000
2141
		}
2142
2143
		rule is_weekday(day_of_week string) {
2144
			  day_of_week != 'saturday' && day_of_week != 'sunday'
2145
		}
2146
		`
2147
2148
	Context("Weekday Sample: Check", func() {
2149
		It("Weekday Sample: Case 1", func() {
2150
			db, err := factories.DatabaseFactory(
2151
				config.Database{
2152
					Engine: "memory",
2153
				},
2154
			)
2155
2156
			Expect(err).ShouldNot(HaveOccurred())
2157
2158
			conf, err := newSchema(weekdaySchema)
2159
			Expect(err).ShouldNot(HaveOccurred())
2160
2161
			schemaWriter := factories.SchemaWriterFactory(db)
2162
			err = schemaWriter.WriteSchema(context.Background(), conf)
2163
2164
			Expect(err).ShouldNot(HaveOccurred())
2165
2166
			type check struct {
2167
				entity     string
2168
				subject    string
2169
				assertions map[string]base.CheckResult
2170
			}
2171
2172
			tests := struct {
2173
				relationships []string
2174
				attributes    []string
2175
				checks        []check
2176
			}{
2177
				relationships: []string{},
2178
				attributes: []string{
2179
					"repository:1$is_public|boolean:true",
2180
				},
2181
				checks: []check{
2182
					{
2183
						entity:  "repository:1",
2184
						subject: "user:1",
2185
						assertions: map[string]base.CheckResult{
2186
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2187
						},
2188
					},
2189
					{
2190
						entity:  "repository:1",
2191
						subject: "user:1",
2192
						assertions: map[string]base.CheckResult{
2193
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2194
						},
2195
					},
2196
				},
2197
			}
2198
2199
			schemaReader := factories.SchemaReaderFactory(db)
2200
			dataReader := factories.DataReaderFactory(db)
2201
			dataWriter := factories.DataWriterFactory(db)
2202
2203
			// engines cache cache
2204
			var engineKeyCache pkgcache.Cache
2205
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
2206
			Expect(err).ShouldNot(HaveOccurred())
2207
2208
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
2209
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
2210
2211
			invoker := invoke.NewDirectInvoker(
2212
				schemaReader,
2213
				dataReader,
2214
				checkEngineWithCache,
2215
				nil,
2216
				nil,
2217
				nil,
2218
				telemetry.NewNoopMeter(),
2219
			)
2220
2221
			checkEngine.SetInvoker(invoker)
2222
2223
			var tuples []*base.Tuple
2224
			var attributes []*base.Attribute
2225
2226
			for _, relationship := range tests.relationships {
2227
				t, err := tuple.Tuple(relationship)
2228
				Expect(err).ShouldNot(HaveOccurred())
2229
				tuples = append(tuples, t)
2230
			}
2231
2232
			for _, attr := range tests.attributes {
2233
				t, err := attribute.Attribute(attr)
2234
				Expect(err).ShouldNot(HaveOccurred())
2235
				attributes = append(attributes, t)
2236
			}
2237
2238
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection(attributes...))
2239
			Expect(err).ShouldNot(HaveOccurred())
2240
2241
			for _, check := range tests.checks {
2242
				entity, err := tuple.E(check.entity)
2243
				Expect(err).ShouldNot(HaveOccurred())
2244
2245
				ear, err := tuple.EAR(check.subject)
2246
				Expect(err).ShouldNot(HaveOccurred())
2247
2248
				subject := &base.Subject{
2249
					Type:     ear.GetEntity().GetType(),
2250
					Id:       ear.GetEntity().GetId(),
2251
					Relation: ear.GetRelation(),
2252
				}
2253
2254
				for permission, res := range check.assertions {
2255
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
2256
						TenantId:   "t1",
2257
						Entity:     entity,
2258
						Subject:    subject,
2259
						Permission: permission,
2260
						Metadata: &base.PermissionCheckRequestMetadata{
2261
							SnapToken:     token.NewNoopToken().Encode().String(),
2262
							SchemaVersion: "",
2263
							Depth:         20,
2264
						},
2265
					})
2266
2267
					Expect(err).ShouldNot(HaveOccurred())
2268
					Expect(res).Should(Equal(response.GetCan()))
2269
				}
2270
			}
2271
		})
2272
2273
		It("Weekday Sample: Case 2", func() {
2274
			db, err := factories.DatabaseFactory(
2275
				config.Database{
2276
					Engine: "memory",
2277
				},
2278
			)
2279
2280
			Expect(err).ShouldNot(HaveOccurred())
2281
2282
			conf, err := newSchema(weekdaySchema)
2283
			Expect(err).ShouldNot(HaveOccurred())
2284
2285
			schemaWriter := factories.SchemaWriterFactory(db)
2286
			err = schemaWriter.WriteSchema(context.Background(), conf)
2287
2288
			Expect(err).ShouldNot(HaveOccurred())
2289
2290
			type check struct {
2291
				entity     string
2292
				subject    string
2293
				context    map[string]interface{}
2294
				assertions map[string]base.CheckResult
2295
			}
2296
2297
			tests := struct {
2298
				relationships []string
2299
				attributes    []string
2300
				checks        []check
2301
			}{
2302
				relationships: []string{
2303
					"organization:1#member@user:1",
2304
					"repository:1#organization@organization:1",
2305
				},
2306
				attributes: []string{
2307
					"organization:1$balance|integer:7000",
2308
				},
2309
				checks: []check{
2310
					{
2311
						entity:  "organization:1",
2312
						subject: "user:1",
2313
						assertions: map[string]base.CheckResult{
2314
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2315
						},
2316
					},
2317
					{
2318
						entity:  "repository:1",
2319
						subject: "user:1",
2320
						assertions: map[string]base.CheckResult{
2321
							"edit": base.CheckResult_CHECK_RESULT_ALLOWED,
2322
						},
2323
					},
2324
					{
2325
						entity:  "repository:1",
2326
						subject: "user:1",
2327
						assertions: map[string]base.CheckResult{
2328
							"edit": base.CheckResult_CHECK_RESULT_ALLOWED,
2329
						},
2330
					},
2331
					{
2332
						entity:  "repository:1",
2333
						subject: "user:1",
2334
						context: map[string]interface{}{
2335
							"day_of_week": "saturday",
2336
						},
2337
						assertions: map[string]base.CheckResult{
2338
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
2339
						},
2340
					},
2341
					{
2342
						entity:  "repository:1",
2343
						subject: "user:1",
2344
						context: map[string]interface{}{
2345
							"day_of_week": "saturday",
2346
						},
2347
						assertions: map[string]base.CheckResult{
2348
							"delete": base.CheckResult_CHECK_RESULT_DENIED,
2349
						},
2350
					},
2351
				},
2352
			}
2353
2354
			schemaReader := factories.SchemaReaderFactory(db)
2355
			dataReader := factories.DataReaderFactory(db)
2356
			dataWriter := factories.DataWriterFactory(db)
2357
2358
			// engines cache cache
2359
			var engineKeyCache pkgcache.Cache
2360
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
2361
			Expect(err).ShouldNot(HaveOccurred())
2362
2363
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
2364
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
2365
2366
			invoker := invoke.NewDirectInvoker(
2367
				schemaReader,
2368
				dataReader,
2369
				checkEngineWithCache,
2370
				nil,
2371
				nil,
2372
				nil,
2373
				telemetry.NewNoopMeter(),
2374
			)
2375
2376
			checkEngine.SetInvoker(invoker)
2377
2378
			var tuples []*base.Tuple
2379
			var attributes []*base.Attribute
2380
2381
			for _, relationship := range tests.relationships {
2382
				t, err := tuple.Tuple(relationship)
2383
				Expect(err).ShouldNot(HaveOccurred())
2384
				tuples = append(tuples, t)
2385
			}
2386
2387
			for _, attr := range tests.attributes {
2388
				t, err := attribute.Attribute(attr)
2389
				Expect(err).ShouldNot(HaveOccurred())
2390
				attributes = append(attributes, t)
2391
			}
2392
2393
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection(attributes...))
2394
			Expect(err).ShouldNot(HaveOccurred())
2395
2396
			for _, check := range tests.checks {
2397
				entity, err := tuple.E(check.entity)
2398
				Expect(err).ShouldNot(HaveOccurred())
2399
2400
				ear, err := tuple.EAR(check.subject)
2401
				Expect(err).ShouldNot(HaveOccurred())
2402
2403
				subject := &base.Subject{
2404
					Type:     ear.GetEntity().GetType(),
2405
					Id:       ear.GetEntity().GetId(),
2406
					Relation: ear.GetRelation(),
2407
				}
2408
2409
				for permission, res := range check.assertions {
2410
2411
					ctx := &base.Context{
2412
						Tuples:     []*base.Tuple{},
2413
						Attributes: []*base.Attribute{},
2414
						Data:       &structpb.Struct{},
2415
					}
2416
2417
					if check.context != nil {
2418
						value, err := structpb.NewStruct(check.context)
2419
						if err != nil {
2420
							fmt.Printf("Error creating struct: %v", err)
2421
						}
2422
						ctx.Data = value
2423
					}
2424
2425
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
2426
						TenantId:   "t1",
2427
						Entity:     entity,
2428
						Subject:    subject,
2429
						Permission: permission,
2430
						Context:    ctx,
2431
						Metadata: &base.PermissionCheckRequestMetadata{
2432
							SnapToken:     token.NewNoopToken().Encode().String(),
2433
							SchemaVersion: "",
2434
							Depth:         20,
2435
						},
2436
					})
2437
2438
					Expect(err).ShouldNot(HaveOccurred())
2439
					Expect(res).Should(Equal(response.GetCan()))
2440
				}
2441
			}
2442
		})
2443
2444
		It("Weekday Sample: Case 3", func() {
2445
			db, err := factories.DatabaseFactory(
2446
				config.Database{
2447
					Engine: "memory",
2448
				},
2449
			)
2450
2451
			Expect(err).ShouldNot(HaveOccurred())
2452
2453
			conf, err := newSchema(weekdaySchema)
2454
			Expect(err).ShouldNot(HaveOccurred())
2455
2456
			schemaWriter := factories.SchemaWriterFactory(db)
2457
			err = schemaWriter.WriteSchema(context.Background(), conf)
2458
2459
			Expect(err).ShouldNot(HaveOccurred())
2460
2461
			type check struct {
2462
				entity     string
2463
				subject    string
2464
				assertions map[string]base.CheckResult
2465
			}
2466
2467
			tests := struct {
2468
				relationships []string
2469
				attributes    []string
2470
				checks        []check
2471
			}{
2472
				relationships: []string{},
2473
				attributes: []string{
2474
					"repository:1$is_public|boolean:true",
2475
				},
2476
				checks: []check{
2477
					{
2478
						entity:  "repository:1",
2479
						subject: "user:1",
2480
						assertions: map[string]base.CheckResult{
2481
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2482
						},
2483
					},
2484
					{
2485
						entity:  "repository:1",
2486
						subject: "user:1",
2487
						assertions: map[string]base.CheckResult{
2488
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2489
						},
2490
					},
2491
				},
2492
			}
2493
2494
			schemaReader := factories.SchemaReaderFactory(db)
2495
			dataReader := factories.DataReaderFactory(db)
2496
2497
			// engines cache cache
2498
			var engineKeyCache pkgcache.Cache
2499
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
2500
			Expect(err).ShouldNot(HaveOccurred())
2501
2502
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
2503
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
2504
2505
			invoker := invoke.NewDirectInvoker(
2506
				schemaReader,
2507
				dataReader,
2508
				checkEngineWithCache,
2509
				nil,
2510
				nil,
2511
				nil,
2512
				telemetry.NewNoopMeter(),
2513
			)
2514
2515
			checkEngine.SetInvoker(invoker)
2516
2517
			var tuples []*base.Tuple
2518
			var attributes []*base.Attribute
2519
2520
			for _, relationship := range tests.relationships {
2521
				t, err := tuple.Tuple(relationship)
2522
				Expect(err).ShouldNot(HaveOccurred())
2523
				tuples = append(tuples, t)
2524
			}
2525
2526
			for _, attr := range tests.attributes {
2527
				t, err := attribute.Attribute(attr)
2528
				Expect(err).ShouldNot(HaveOccurred())
2529
				attributes = append(attributes, t)
2530
			}
2531
2532
			for _, check := range tests.checks {
2533
				entity, err := tuple.E(check.entity)
2534
				Expect(err).ShouldNot(HaveOccurred())
2535
2536
				ear, err := tuple.EAR(check.subject)
2537
				Expect(err).ShouldNot(HaveOccurred())
2538
2539
				subject := &base.Subject{
2540
					Type:     ear.GetEntity().GetType(),
2541
					Id:       ear.GetEntity().GetId(),
2542
					Relation: ear.GetRelation(),
2543
				}
2544
2545
				for permission, res := range check.assertions {
2546
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
2547
						TenantId:   "t1",
2548
						Entity:     entity,
2549
						Subject:    subject,
2550
						Permission: permission,
2551
						Metadata: &base.PermissionCheckRequestMetadata{
2552
							SnapToken:     token.NewNoopToken().Encode().String(),
2553
							SchemaVersion: "",
2554
							Depth:         20,
2555
						},
2556
						Context: &base.Context{
2557
							Attributes: attributes,
2558
						},
2559
					})
2560
2561
					Expect(err).ShouldNot(HaveOccurred())
2562
					Expect(res).Should(Equal(response.GetCan()))
2563
				}
2564
			}
2565
		})
2566
	})
2567
2568
	// IP RANGE SAMPLE
2569
	IpRangeSchema := `
2570
		entity user {}
2571
	
2572
		entity organization {
2573
	
2574
			relation admin @user
2575
	
2576
			attribute ip_range string[]
2577
	
2578
			permission view = check_ip_range(request.ip_address, ip_range) or admin
2579
		}
2580
	
2581
		rule check_ip_range(ip_address string, ip_range string[]) {
2582
			ip_address in ip_range
2583
		}
2584
		`
2585
2586
	Context("Ip Range Sample: Check", func() {
2587
		It("Ip Range Sample: Case 1", func() {
2588
			db, err := factories.DatabaseFactory(
2589
				config.Database{
2590
					Engine: "memory",
2591
				},
2592
			)
2593
2594
			Expect(err).ShouldNot(HaveOccurred())
2595
2596
			conf, err := newSchema(IpRangeSchema)
2597
			Expect(err).ShouldNot(HaveOccurred())
2598
2599
			schemaWriter := factories.SchemaWriterFactory(db)
2600
			err = schemaWriter.WriteSchema(context.Background(), conf)
2601
2602
			Expect(err).ShouldNot(HaveOccurred())
2603
2604
			type check struct {
2605
				entity     string
2606
				subject    string
2607
				context    map[string]interface{}
2608
				assertions map[string]base.CheckResult
2609
			}
2610
2611
			tests := struct {
2612
				relationships []string
2613
				attributes    []string
2614
				checks        []check
2615
			}{
2616
				relationships: []string{},
2617
				attributes: []string{
2618
					"organization:1$ip_range|string[]:18.216.238.147,94.176.248.171,61.49.24.70",
2619
				},
2620
				checks: []check{
2621
					{
2622
						entity:  "organization:1",
2623
						subject: "user:1",
2624
						context: map[string]interface{}{
2625
							"ip_address": "18.216.238.147",
2626
						},
2627
						assertions: map[string]base.CheckResult{
2628
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2629
						},
2630
					},
2631
					{
2632
						entity:  "organization:1",
2633
						subject: "user:1",
2634
						context: map[string]interface{}{
2635
							"ip_address": "18.216.238.147",
2636
						},
2637
						assertions: map[string]base.CheckResult{
2638
							"view": base.CheckResult_CHECK_RESULT_ALLOWED,
2639
						},
2640
					},
2641
				},
2642
			}
2643
2644
			schemaReader := factories.SchemaReaderFactory(db)
2645
			dataReader := factories.DataReaderFactory(db)
2646
			dataWriter := factories.DataWriterFactory(db)
2647
2648
			// engines cache cache
2649
			var engineKeyCache pkgcache.Cache
2650
			engineKeyCache, err = ristretto.New(ristretto.NumberOfCounters(1_000), ristretto.MaxCost("10MiB"))
2651
			Expect(err).ShouldNot(HaveOccurred())
2652
2653
			checkEngine := engines.NewCheckEngine(schemaReader, dataReader)
2654
			checkEngineWithCache := NewCheckEngineWithCache(checkEngine, schemaReader, engineKeyCache)
2655
2656
			invoker := invoke.NewDirectInvoker(
2657
				schemaReader,
2658
				dataReader,
2659
				checkEngineWithCache,
2660
				nil,
2661
				nil,
2662
				nil,
2663
				telemetry.NewNoopMeter(),
2664
			)
2665
2666
			checkEngine.SetInvoker(invoker)
2667
2668
			var tuples []*base.Tuple
2669
			var attributes []*base.Attribute
2670
2671
			for _, relationship := range tests.relationships {
2672
				t, err := tuple.Tuple(relationship)
2673
				Expect(err).ShouldNot(HaveOccurred())
2674
				tuples = append(tuples, t)
2675
			}
2676
2677
			for _, attr := range tests.attributes {
2678
				t, err := attribute.Attribute(attr)
2679
				Expect(err).ShouldNot(HaveOccurred())
2680
				attributes = append(attributes, t)
2681
			}
2682
2683
			_, err = dataWriter.Write(context.Background(), "t1", database.NewTupleCollection(tuples...), database.NewAttributeCollection(attributes...))
2684
			Expect(err).ShouldNot(HaveOccurred())
2685
2686
			for _, check := range tests.checks {
2687
				entity, err := tuple.E(check.entity)
2688
				Expect(err).ShouldNot(HaveOccurred())
2689
2690
				ear, err := tuple.EAR(check.subject)
2691
				Expect(err).ShouldNot(HaveOccurred())
2692
2693
				subject := &base.Subject{
2694
					Type:     ear.GetEntity().GetType(),
2695
					Id:       ear.GetEntity().GetId(),
2696
					Relation: ear.GetRelation(),
2697
				}
2698
2699
				for permission, res := range check.assertions {
2700
2701
					ctx := &base.Context{
2702
						Tuples:     []*base.Tuple{},
2703
						Attributes: []*base.Attribute{},
2704
						Data:       &structpb.Struct{},
2705
					}
2706
2707
					if check.context != nil {
2708
						value, err := structpb.NewStruct(check.context)
2709
						if err != nil {
2710
							fmt.Printf("Error creating struct: %v", err)
2711
						}
2712
						ctx.Data = value
2713
					}
2714
2715
					response, err := invoker.Check(context.Background(), &base.PermissionCheckRequest{
2716
						TenantId:   "t1",
2717
						Entity:     entity,
2718
						Subject:    subject,
2719
						Permission: permission,
2720
						Context:    ctx,
2721
						Metadata: &base.PermissionCheckRequestMetadata{
2722
							SnapToken:     token.NewNoopToken().Encode().String(),
2723
							SchemaVersion: "",
2724
							Depth:         20,
2725
						},
2726
					})
2727
2728
					Expect(err).ShouldNot(HaveOccurred())
2729
					Expect(res).Should(Equal(response.GetCan()))
2730
				}
2731
			}
2732
		})
2733
	})
2734
})
2735
2736
// newSchema -
2737
func newSchema(model string) ([]storage.SchemaDefinition, error) {
2738
	sch, err := parser.NewParser(model).Parse()
2739
	if err != nil {
2740
		return nil, err
2741
	}
2742
2743
	_, _, err = compiler.NewCompiler(false, sch).Compile()
2744
	if err != nil {
2745
		return nil, err
2746
	}
2747
2748
	version := xid.New().String()
2749
2750
	cnf := make([]storage.SchemaDefinition, 0, len(sch.Statements))
2751
	for _, st := range sch.Statements {
2752
		cnf = append(cnf, storage.SchemaDefinition{
2753
			TenantID:             "t1",
2754
			Version:              version,
2755
			Name:                 st.GetName(),
2756
			SerializedDefinition: []byte(st.String()),
2757
		})
2758
	}
2759
2760
	return cnf, err
2761
}
2762
2763
// isSameArray - check if two arrays are the same
2764
func isSameArray(a, b []string) bool {
2765
	if len(a) != len(b) {
2766
		return false
2767
	}
2768
2769
	sortedA := make([]string, len(a))
2770
	copy(sortedA, a)
2771
	sort.Strings(sortedA)
2772
2773
	sortedB := make([]string, len(b))
2774
	copy(sortedB, b)
2775
	sort.Strings(sortedB)
2776
2777
	for i := range sortedA {
2778
		if sortedA[i] != sortedB[i] {
2779
			return false
2780
		}
2781
	}
2782
2783
	return true
2784
}
2785