github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/relationships/validation_test.go (about)

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