Skip to content

Commit

Permalink
Add integrity information to memdb datastore and start on datastore t…
Browse files Browse the repository at this point in the history
…ests
  • Loading branch information
josephschorr committed Jul 16, 2024
1 parent f6b1d81 commit 72fc585
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 1 deletion.
13 changes: 13 additions & 0 deletions internal/datastore/memdb/readwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (rwt *memdbReadWriteTx) write(tx *memdb.Txn, mutations ...*core.RelationTup
mutation.Tuple.Subject.ObjectId,
mutation.Tuple.Subject.Relation,
rwt.toCaveatReference(mutation),
rwt.toIntegrity(mutation),
}

found, err := tx.First(
Expand Down Expand Up @@ -119,6 +120,18 @@ func (rwt *memdbReadWriteTx) toCaveatReference(mutation *core.RelationTupleUpdat
return cr
}

func (rwt *memdbReadWriteTx) toIntegrity(mutation *core.RelationTupleUpdate) *relationshipIntegrity {
var ri *relationshipIntegrity
if mutation.Tuple.Integrity != nil {
ri = &relationshipIntegrity{
keyID: mutation.Tuple.Integrity.KeyId,
hash: mutation.Tuple.Integrity.Hash,
timestamp: mutation.Tuple.Integrity.HashedAt.AsTime(),
}
}
return ri
}

func (rwt *memdbReadWriteTx) DeleteRelationships(_ context.Context, filter *v1.RelationshipFilter, opts ...options.DeleteOptionsOption) (bool, error) {
rwt.mustLock()
defer rwt.Unlock()
Expand Down
28 changes: 27 additions & 1 deletion internal/datastore/memdb/schema.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package memdb

import (
"time"

v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
"github.com/hashicorp/go-memdb"
"github.com/jzelinskie/stringz"
"github.com/rs/zerolog"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/authzed/spicedb/pkg/datastore"
core "github.com/authzed/spicedb/pkg/proto/core/v1"
Expand Down Expand Up @@ -51,13 +54,32 @@ type relationship struct {
subjectObjectID string
subjectRelation string
caveat *contextualizedCaveat
integrity *relationshipIntegrity
}

type contextualizedCaveat struct {
caveatName string
context map[string]any
}

type relationshipIntegrity struct {
keyID string
hash []byte
timestamp time.Time
}

func (ri relationshipIntegrity) MarshalZerologObject(e *zerolog.Event) {
e.Str("keyID", ri.keyID).Bytes("hash", ri.hash).Time("timestamp", ri.timestamp)
}

func (ri relationshipIntegrity) RelationshipIntegrity() *core.RelationshipIntegrity {
return &core.RelationshipIntegrity{
KeyId: ri.keyID,
Hash: ri.hash,
HashedAt: timestamppb.New(ri.timestamp),
}
}

func (cr *contextualizedCaveat) ContextualizedCaveat() (*core.ContextualizedCaveat, error) {
if cr == nil {
return nil, nil
Expand Down Expand Up @@ -107,6 +129,9 @@ func (r relationship) RelationTuple() (*core.RelationTuple, error) {
if err != nil {
return nil, err
}

ig := r.integrity.RelationshipIntegrity()

return &core.RelationTuple{
ResourceAndRelation: &core.ObjectAndRelation{
Namespace: r.namespace,
Expand All @@ -118,7 +143,8 @@ func (r relationship) RelationTuple() (*core.RelationTuple, error) {
ObjectId: r.subjectObjectID,
Relation: r.subjectRelation,
},
Caveat: cr,
Caveat: cr,
Integrity: ig,
}, nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/datastore/test/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func AllWithExceptions(t *testing.T, tester DatastoreTester, except Categories)
t.Run("TestDeleteRelationshipsWithVariousFilters", func(t *testing.T) { DeleteRelationshipsWithVariousFiltersTest(t, tester) })
t.Run("TestTouchTypedAlreadyExistingWithoutCaveat", func(t *testing.T) { TypedTouchAlreadyExistingTest(t, tester) })
t.Run("TestTouchTypedAlreadyExistingWithCaveat", func(t *testing.T) { TypedTouchAlreadyExistingWithCaveatTest(t, tester) })
t.Run("TestRelationshipIntegrityInfo", func(t *testing.T) { RelationshipIntegrityInfoTest(t, tester) })

t.Run("TestMultipleReadsInRWT", func(t *testing.T) { MultipleReadsInRWTTest(t, tester) })
t.Run("TestConcurrentWriteSerialization", func(t *testing.T) { ConcurrentWriteSerializationTest(t, tester) })
Expand Down
49 changes: 49 additions & 0 deletions pkg/datastore/test/tuples.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/authzed/grpcutil"

Expand Down Expand Up @@ -1672,6 +1673,54 @@ func BulkDeleteRelationshipsTest(t *testing.T, tester DatastoreTester) {
require.Nil(iter.Next(), "expected no results")
}

func RelationshipIntegrityInfoTest(t *testing.T, tester DatastoreTester) {
require := require.New(t)

rawDS, err := tester.New(0, veryLargeGCInterval, veryLargeGCWindow, 1)
require.NoError(err)

ds, _ := testfixtures.StandardDatastoreWithSchema(rawDS, require)
ctx := context.Background()

// Write a relationship with integrity information.
timestamp := time.Now().UTC()

_, err = ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
tpl := tuple.MustParse("document:foo#viewer@user:tom")
tpl.Integrity = &core.RelationshipIntegrity{
KeyId: "key1",
Hash: []byte("hash1"),
HashedAt: timestamppb.New(timestamp),
}
return rwt.WriteRelationships(ctx, []*core.RelationTupleUpdate{
tuple.Create(tpl),
})
})
require.NoError(err)

// Read the relationship back and ensure the integrity information is present.
headRev, err := ds.HeadRevision(ctx)
require.NoError(err)

reader := ds.SnapshotReader(headRev)
iter, err := reader.QueryRelationships(ctx, datastore.RelationshipsFilter{
OptionalResourceType: "document",
OptionalResourceIds: []string{"foo"},
OptionalResourceRelation: "viewer",
})
require.NoError(err)

tpl := iter.Next()
require.NotNil(tpl)

require.NotNil(tpl.Integrity)
require.Equal("key1", tpl.Integrity.KeyId)
require.Equal([]byte("hash1"), tpl.Integrity.Hash)
require.Equal(timestamp, tpl.Integrity.HashedAt.AsTime())

iter.Close()
}

func onrToSubjectsFilter(onr *core.ObjectAndRelation) datastore.SubjectsFilter {
return datastore.SubjectsFilter{
SubjectType: onr.Namespace,
Expand Down

0 comments on commit 72fc585

Please sign in to comment.