Skip to content

Commit

Permalink
adjust naming and use structpb
Browse files Browse the repository at this point in the history
instead of using protobuf.Any, we use
protobuf.Struct that better maps a
map[string]any

we move away from the concept of caveat
digest and will reference them by name.
Anonymous caveats will have a globally-unique
name that may likely be the result of performing
a digest of the signature and payload

we replace "caveat logic" with "caveat expression"

we also change the term "predefined variables" with "caveat
context" to refer to the state persisted alongside the tuple.
these are variables that will be injected at caveat evaluation time

as a minor inconvenience the relationship.RelationTuple method now has to
return an error, propagated from the call to structpb.NewStruct.
That led to changes in all callsites using it.
  • Loading branch information
vroldanbet committed Sep 13, 2022
1 parent 9f0d6b6 commit 4edd111
Show file tree
Hide file tree
Showing 9 changed files with 568 additions and 559 deletions.
22 changes: 11 additions & 11 deletions internal/datastore/memdb/caveat.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@ import (
const tableCaveats = "caveats"

type caveat struct {
digest string
logic []byte
name string
expression []byte
caveatType core.Caveat_Type
}

func (c *caveat) CoreCaveat() *core.Caveat {
return &core.Caveat{
Digest: c.digest,
Logic: c.logic,
Type: c.caveatType,
Name: c.name,
Expression: c.expression,
Type: c.caveatType,
}
}

func (r *memdbReader) ReadCaveat(digest string) (datastore.CaveatIterator, error) {
func (r *memdbReader) ReadCaveat(name string) (datastore.CaveatIterator, error) {
r.lockOrPanic()
defer r.Unlock()

tx, err := r.txSource()
if err != nil {
return nil, err
}
return r.readCaveat(tx, digest)
return r.readCaveat(tx, name)
}

func (r *memdbReader) readCaveat(tx *memdb.Txn, digest string) (datastore.CaveatIterator, error) {
it, err := tx.Get(tableCaveats, indexID, digest)
func (r *memdbReader) readCaveat(tx *memdb.Txn, name string) (datastore.CaveatIterator, error) {
it, err := tx.Get(tableCaveats, indexID, name)
if err != nil {
return nil, err
}
Expand All @@ -55,8 +55,8 @@ func (rwt *memdbReadWriteTx) WriteCaveat(caveats []*core.Caveat) error {
func (rwt *memdbReadWriteTx) writeCaveat(tx *memdb.Txn, caveats []*core.Caveat) error {
for _, coreCaveat := range caveats {
c := caveat{
digest: coreCaveat.Digest,
logic: coreCaveat.Logic,
name: coreCaveat.Name,
expression: coreCaveat.Expression,
caveatType: coreCaveat.Type,
}
if err := tx.Insert(tableCaveats, &c); err != nil {
Expand Down
35 changes: 20 additions & 15 deletions internal/datastore/memdb/caveat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/authzed/spicedb/pkg/tuple"

"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb"
)

func TestWriteReadCaveat(t *testing.T) {
Expand Down Expand Up @@ -47,12 +48,7 @@ func TestWriteTupleWithNamedCaveat(t *testing.T) {
ds, err := NewMemdbDatastore(0, 1*time.Hour, 1*time.Hour)
req.NoError(err)
sds, _ := testfixtures.StandardDatastoreWithSchema(ds, req)
tpl := tuple.MustParse("document:companyplan#parent@folder:company#...")
c := createCompiledCaveat(t)
coreCaveat := createCoreCaveat(t, c, false)
tpl.Caveat = &core.CaveatReference{
Caveat: coreCaveat,
}
tpl, coreCaveat := createTestCaveatedTuple(t, "document:companyplan#parent@folder:company#...", false)
_, err = common.WriteTuples(ctx, sds, core.RelationTupleUpdate_CREATE, tpl)
// should fail because the name caveat is not present in the datastore
req.Error(err)
Expand All @@ -77,12 +73,7 @@ func TestWriteTupleWithAnonymousCaveat(t *testing.T) {
ds, err := NewMemdbDatastore(0, 1*time.Hour, 1*time.Hour)
req.NoError(err)
sds, _ := testfixtures.StandardDatastoreWithSchema(ds, req)
tpl := tuple.MustParse("document:companyplan#parent@folder:company#...")
c := createCompiledCaveat(t)
coreCaveat := createCoreCaveat(t, c, true)
tpl.Caveat = &core.CaveatReference{
Caveat: coreCaveat,
}
tpl, _ := createTestCaveatedTuple(t, "document:companyplan#parent@folder:company#...", true)
rev, err := common.WriteTuples(ctx, sds, core.RelationTupleUpdate_CREATE, tpl)
// the caveat is anonymous and is created alongside the tuple
req.NoError(err)
Expand All @@ -95,6 +86,20 @@ func TestWriteTupleWithAnonymousCaveat(t *testing.T) {
req.Equal(tpl, readTpl)
}

func createTestCaveatedTuple(t *testing.T, tplString string, anonymous bool) (*core.RelationTuple, *core.Caveat) {
tpl := tuple.MustParse(tplString)
c := createCompiledCaveat(t)
coreCaveat := createCoreCaveat(t, c, anonymous)

st, err := structpb.NewStruct(map[string]interface{}{"a": 1, "b": "test"})
require.NoError(t, err)
tpl.Caveat = &core.CaveatReference{
Caveat: coreCaveat,
Context: st,
}
return tpl, coreCaveat
}

func writeCaveat(ctx context.Context, ds datastore.Datastore, coreCaveat *core.Caveat) (datastore.Revision, error) {
return ds.ReadWriteTx(ctx, func(ctx context.Context, tx datastore.ReadWriteTransaction) error {
cs, ok := tx.(datastore.CaveatStorer)
Expand All @@ -114,9 +119,9 @@ func createCoreCaveat(t *testing.T, c *caveats.CompiledCaveat, anonymous bool) *
ty = core.Caveat_ANONYMOUS
}
coreCaveat := &core.Caveat{
Digest: c.Name(),
Logic: cBytes,
Type: ty,
Name: c.Name(),
Expression: cBytes,
Type: ty,
}
require.NoError(t, err)
return coreCaveat
Expand Down
12 changes: 10 additions & 2 deletions internal/datastore/memdb/memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,23 @@ func (mdb *memdbDatastore) ReadWriteTx(
for _, change := range tx.Changes() {
if change.Table == tableRelationship {
if change.After != nil {
rt, err := change.After.(*relationship).RelationTuple()
if err != nil {
return datastore.NoRevision, err
}
newChanges.Changes = append(newChanges.Changes, &corev1.RelationTupleUpdate{
Operation: corev1.RelationTupleUpdate_TOUCH,
Tuple: change.After.(*relationship).RelationTuple(),
Tuple: rt,
})
}
if change.After == nil && change.Before != nil {
rt, err := change.Before.(*relationship).RelationTuple()
if err != nil {
return datastore.NoRevision, err
}
newChanges.Changes = append(newChanges.Changes, &corev1.RelationTupleUpdate{
Operation: corev1.RelationTupleUpdate_DELETE,
Tuple: change.Before.(*relationship).RelationTuple(),
Tuple: rt,
})
}
}
Expand Down
10 changes: 8 additions & 2 deletions internal/datastore/memdb/readonly.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ type memdbTupleIterator struct {
it memdb.ResultIterator
limit *uint64
count uint64
err error
}

func (mti *memdbTupleIterator) Next() *core.RelationTuple {
Expand All @@ -293,11 +294,16 @@ func (mti *memdbTupleIterator) Next() *core.RelationTuple {
}
mti.count++

return foundRaw.(*relationship).RelationTuple()
rt, err := foundRaw.(*relationship).RelationTuple()
if err != nil {
mti.err = err
return nil
}
return rt
}

func (mti *memdbTupleIterator) Err() error {
return nil
return mti.err
}

func (mti *memdbTupleIterator) Close() {
Expand Down
21 changes: 15 additions & 6 deletions internal/datastore/memdb/readwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ func (rwt *memdbReadWriteTx) write(tx *memdb.Txn, mutations ...*core.RelationTup
switch mutation.Operation {
case core.RelationTupleUpdate_CREATE:
if existing != nil {
return common.NewCreateRelationshipExistsError(existing.RelationTuple())
rt, err := existing.RelationTuple()
if err != nil {
return err
}
return common.NewCreateRelationshipExistsError(rt)
}
fallthrough
case core.RelationTupleUpdate_TOUCH:
Expand All @@ -97,23 +101,24 @@ func (rwt *memdbReadWriteTx) prepareCaveat(tx *memdb.Txn, mutation *core.Relatio
if mutation.Tuple.Caveat != nil {
cr = &caveatReference{
caveat: &caveat{
digest: mutation.Tuple.Caveat.Caveat.Digest,
logic: mutation.Tuple.Caveat.Caveat.Logic,
name: mutation.Tuple.Caveat.Caveat.Name,
expression: mutation.Tuple.Caveat.Caveat.Expression,
caveatType: mutation.Tuple.Caveat.Caveat.Type,
},
context: mutation.Tuple.Caveat.Context.AsMap(),
}
switch mutation.Tuple.Caveat.Caveat.Type {
case core.Caveat_ANONYMOUS:
if err := rwt.writeCaveat(tx, []*core.Caveat{mutation.Tuple.Caveat.Caveat}); err != nil {
return nil, err
}
case core.Caveat_NAMED:
it, err := rwt.readCaveat(tx, mutation.Tuple.Caveat.Caveat.Digest)
it, err := rwt.readCaveat(tx, mutation.Tuple.Caveat.Caveat.Name)
if err != nil {
return nil, err
}
if it.Next() == nil {
return nil, fmt.Errorf("tuple referenced a non-existing named caveat %s", mutation.Tuple.Caveat.Caveat.Digest)
return nil, fmt.Errorf("tuple referenced a non-existing named caveat %s", mutation.Tuple.Caveat.Caveat.Name)
}
default:
return nil, fmt.Errorf("unknown caveat type")
Expand Down Expand Up @@ -146,7 +151,11 @@ func (rwt *memdbReadWriteTx) deleteWithLock(tx *memdb.Txn, filter *v1.Relationsh
// Collect the tuples into a slice of mutations for the changelog
var mutations []*core.RelationTupleUpdate
for row := filteredIter.Next(); row != nil; row = filteredIter.Next() {
mutations = append(mutations, tuple.Delete(row.(*relationship).RelationTuple()))
rt, err := row.(*relationship).RelationTuple()
if err != nil {
return err
}
mutations = append(mutations, tuple.Delete(rt))
}

return rwt.write(tx, mutations...)
Expand Down
32 changes: 22 additions & 10 deletions internal/datastore/memdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/go-memdb"
"github.com/jzelinskie/stringz"
"github.com/rs/zerolog"
"google.golang.org/protobuf/types/known/structpb"

"github.com/authzed/spicedb/pkg/datastore"
core "github.com/authzed/spicedb/pkg/proto/core/v1"
Expand Down Expand Up @@ -49,15 +50,22 @@ type relationship struct {
}

type caveatReference struct {
predefined map[string]any
caveat *caveat
context map[string]any
caveat *caveat
}

func (cr *caveatReference) CaveatReference() *core.CaveatReference {
return &core.CaveatReference{
Caveat: cr.caveat.CoreCaveat(),
//Predefined: cr.predefined // TODO vroldanbet type mismatch
func (cr *caveatReference) CaveatReference() (*core.CaveatReference, error) {
if cr == nil {
return nil, nil
}
v, err := structpb.NewStruct(cr.context)
if err != nil {
return nil, err
}
return &core.CaveatReference{
Caveat: cr.caveat.CoreCaveat(),
Context: v,
}, nil
}

func (r relationship) MarshalZerologObject(e *zerolog.Event) {
Expand Down Expand Up @@ -89,7 +97,11 @@ func (r relationship) Relationship() *v1.Relationship {
}
}

func (r relationship) RelationTuple() *core.RelationTuple {
func (r relationship) RelationTuple() (*core.RelationTuple, error) {
cr, err := r.caveat.CaveatReference()
if err != nil {
return nil, err
}
return &core.RelationTuple{
ResourceAndRelation: &core.ObjectAndRelation{
Namespace: r.namespace,
Expand All @@ -101,8 +113,8 @@ func (r relationship) RelationTuple() *core.RelationTuple {
ObjectId: r.subjectObjectID,
Relation: r.subjectRelation,
},
Caveat: r.caveat.CaveatReference(),
}
Caveat: cr,
}, nil
}

type changelog struct {
Expand Down Expand Up @@ -198,7 +210,7 @@ var schema = &memdb.DBSchema{
indexID: {
Name: indexID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "digest"},
Indexer: &memdb.StringFieldIndex{Field: "name"},
},
},
},
Expand Down
Loading

0 comments on commit 4edd111

Please sign in to comment.