| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package nosql |
| |
| import ( |
| "reflect" |
| "testing" |
| |
| "v.io/v23" |
| "v.io/v23/rpc" |
| "v.io/v23/security" |
| "v.io/v23/security/access" |
| _ "v.io/x/ref/runtime/factories/generic" |
| "v.io/x/ref/services/syncbase/clock" |
| "v.io/x/ref/services/syncbase/server/util" |
| "v.io/x/ref/services/syncbase/server/watchable" |
| "v.io/x/ref/services/syncbase/store" |
| "v.io/x/ref/services/syncbase/store/memstore" |
| "v.io/x/ref/test" |
| "v.io/x/ref/test/testutil" |
| ) |
| |
| type mockCall struct { |
| security.Call |
| b security.Blessings |
| } |
| |
| func (c *mockCall) Server() rpc.Server { return nil } |
| func (c *mockCall) GrantedBlessings() security.Blessings { return c.b } |
| func (c *mockCall) Security() security.Call { return c } |
| func (c *mockCall) LocalBlessings() security.Blessings { return c.b } |
| func (c *mockCall) RemoteBlessings() security.Blessings { return c.b } |
| |
| func putOp(st store.Store, key, permKey string, permVersion []byte) watchable.OpPut { |
| version, _ := watchable.GetVersion(nil, st, []byte(key)) |
| return watchable.OpPut{watchable.PutOp{ |
| Key: []byte(key), |
| Version: version, |
| PermKey: []byte(permKey), |
| PermVersion: permVersion, |
| }} |
| } |
| |
| // TestWatchLogPerms checks that the recorded prefix permissions object |
| // used to grant access to Put/Delete operations is correct. |
| func TestWatchLogPerms(t *testing.T) { |
| // Prepare V23. |
| ctx, shutdown := test.V23Init() |
| defer shutdown() |
| ctx, _ = v23.WithPrincipal(ctx, testutil.NewPrincipal("root")) |
| // Mock the service, store, db, table. |
| vClock := clock.NewVClockWithMockServices(memstore.New(), nil, nil) |
| st, _ := watchable.Wrap(memstore.New(), vClock, &watchable.Options{ |
| ManagedPrefixes: []string{util.RowPrefix, util.PermsPrefix}, |
| }) |
| db := &databaseReq{database: &database{name: "d", st: st}} |
| tb := &tableReq{name: "tb", d: db} |
| // Mock create the table. |
| perms := access.Permissions{} |
| for _, tag := range access.AllTypicalTags() { |
| perms.Add(security.BlessingPattern("root"), string(tag)) |
| } |
| util.Put(ctx, st, tb.stKey(), &tableData{ |
| Name: tb.name, |
| Perms: perms, |
| }) |
| util.Put(ctx, st, tb.prefixPermsKey(""), perms) |
| util.Put(ctx, st, tb.permsIndexStart(""), "") |
| util.Put(ctx, st, tb.permsIndexLimit(""), "") |
| call := &mockCall{b: v23.GetPrincipal(ctx).BlessingStore().Default()} |
| var expected []watchable.Op |
| resumeMarker, _ := watchable.GetResumeMarker(st) |
| // Generate Put/Delete events. |
| for i := 0; i < 5; i++ { |
| // Set initial prefix permissions. |
| if err := tb.SetPrefixPermissions(ctx, call, 0, "foo", perms); err != nil { |
| t.Fatalf("tb.SetPrefixPermissions failed: %v", err) |
| } |
| // Put. |
| row := &rowReq{key: "foobar", t: tb} |
| if err := row.Put(ctx, call, 0, []byte("value")); err != nil { |
| t.Fatalf("row.Put failed: %v", err) |
| } |
| permVersion, _ := watchable.GetVersion(ctx, st, []byte(tb.prefixPermsKey("foo"))) |
| expected = append(expected, putOp(st, row.stKey(), tb.prefixPermsKey("foo"), permVersion)) |
| // Delete. |
| if err := row.Delete(ctx, call, 0); err != nil { |
| t.Fatalf("row.Delete failed: %v", err) |
| } |
| deleteOp := watchable.OpDelete{watchable.DeleteOp{ |
| Key: []byte(row.stKey()), |
| PermKey: []byte(tb.prefixPermsKey("foo")), |
| PermVersion: permVersion, |
| }} |
| expected = append(expected, deleteOp) |
| // DeleteRange. |
| if err := row.Put(ctx, call, 0, []byte("value")); err != nil { |
| t.Fatalf("row.Put failed: %v", err) |
| } |
| if err := tb.DeleteRange(ctx, call, 0, []byte("foo"), nil); err != nil { |
| t.Fatalf("tb.DeleteRange failed: %v", err) |
| } |
| expected = append(expected, deleteOp) |
| // SetPrefixPermissions. |
| if err := tb.SetPrefixPermissions(ctx, call, 0, "foobaz", perms); err != nil { |
| t.Fatalf("tb.SetPrefixPermissions failed: %v", err) |
| } |
| expected = append(expected, putOp(st, tb.prefixPermsKey("foobaz"), tb.prefixPermsKey("foo"), permVersion)) |
| // SetPrefixPermissions again. |
| permVersion, _ = watchable.GetVersion(ctx, st, []byte(tb.prefixPermsKey("foobaz"))) |
| if err := tb.SetPrefixPermissions(ctx, call, 0, "foobaz", perms); err != nil { |
| t.Fatalf("tb.SetPrefixPermissions failed: %v", err) |
| } |
| expected = append(expected, putOp(st, tb.prefixPermsKey("foobaz"), tb.prefixPermsKey("foobaz"), permVersion)) |
| // DeletePrefixPermissions. |
| permVersion, _ = watchable.GetVersion(ctx, st, []byte(tb.prefixPermsKey("foobaz"))) |
| if err := tb.DeletePrefixPermissions(ctx, call, 0, "foobaz"); err != nil { |
| t.Fatalf("tb.DeletePrefixPermissions failed: %v", err) |
| } |
| expected = append(expected, watchable.OpDelete{watchable.DeleteOp{ |
| Key: []byte(tb.prefixPermsKey("foobaz")), |
| PermKey: []byte(tb.prefixPermsKey("foobaz")), |
| PermVersion: permVersion, |
| }}) |
| } |
| expectedIndex := 0 |
| for { |
| var logs []*watchable.LogEntry |
| if logs, resumeMarker, _ = watchable.ReadBatchFromLog(st, resumeMarker); logs == nil { |
| break |
| } |
| for _, logRecord := range logs { |
| if expectedIndex < len(expected) && reflect.DeepEqual(logRecord.Op, expected[expectedIndex]) { |
| expectedIndex++ |
| } |
| } |
| } |
| if expectedIndex != len(expected) { |
| t.Fatalf("only %d out of %d record were found", expectedIndex, len(expected)) |
| } |
| } |