diff --git a/e2e/go.mod b/e2e/go.mod index 2b49fc0dc4..010a06504d 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -3,7 +3,7 @@ module github.com/authzed/spicedb/e2e go 1.19 require ( - github.com/authzed/authzed-go v0.9.0 + github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322 github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5 github.com/authzed/spicedb v1.23.1 github.com/brianvoe/gofakeit/v6 v6.23.0 @@ -31,7 +31,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jzelinskie/stringz v0.0.1 // indirect + github.com/jzelinskie/stringz v0.0.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 65860603fa..f2bdb4a87b 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -5,8 +5,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= -github.com/authzed/authzed-go v0.9.0 h1:FBWWwYiZrreGN94R9EEIy1S2s0UAH0Hn7MWBRtbtF+w= -github.com/authzed/authzed-go v0.9.0/go.mod h1:9Pl5jDQJHrjbMDuCrsa+Q6Tqmi1f2pDdIn/qNGI++vA= +github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322 h1:xRXaLKkAQGF6aZv7KeoYCrxsHh1y9os6wFUUQ0TnMuE= +github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322/go.mod h1:9Pl5jDQJHrjbMDuCrsa+Q6Tqmi1f2pDdIn/qNGI++vA= github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5 h1:Fg92G8sNNODbNe2ckJoLeMEPeDqSfygmXnpEXDnVifU= github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5/go.mod h1:qx105brQubHFYLRja6wlHA+JB8DSK+yhb8uc8aFA5NQ= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -68,8 +68,8 @@ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.4.2 h1:u1gmGDwbdRUZiwisBm/Ky2M14uQyUP65bG8+20nnyrg= github.com/jackc/pgx/v5 v5.4.2/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY= -github.com/jzelinskie/stringz v0.0.1 h1:IahR+y8ct2nyj7B6i8UtFsGFj4ex1SX27iKFYsAheLk= -github.com/jzelinskie/stringz v0.0.1/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= +github.com/jzelinskie/stringz v0.0.2 h1:OSjMEYvz8tjhovgZ/6cGcPID736ubeukr35mu6RYAmg= +github.com/jzelinskie/stringz v0.0.2/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/go.mod b/go.mod index f066e2200e..0b204fe118 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( cloud.google.com/go/spanner v1.47.0 github.com/IBM/pgxpoolprometheus v1.1.1 github.com/Masterminds/squirrel v1.5.4 - github.com/authzed/authzed-go v0.9.0 + github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322 github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5 github.com/aws/aws-sdk-go v1.44.314 github.com/benbjohnson/clock v1.3.5 @@ -44,7 +44,7 @@ require ( github.com/jackc/pgx/v5 v5.4.2 github.com/johannesboyne/gofakes3 v0.0.0-20230506070712-04da935ef877 github.com/jzelinskie/cobrautil/v2 v2.0.0-20230714172849-80717639cec5 - github.com/jzelinskie/stringz v0.0.1 + github.com/jzelinskie/stringz v0.0.2 github.com/lib/pq v1.10.9 github.com/lthibault/jitterbug v2.0.0+incompatible github.com/magefile/mage v1.15.0 diff --git a/go.sum b/go.sum index 4ec13f51ea..84945cdcc1 100644 --- a/go.sum +++ b/go.sum @@ -453,8 +453,8 @@ github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8ger github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= -github.com/authzed/authzed-go v0.9.0 h1:FBWWwYiZrreGN94R9EEIy1S2s0UAH0Hn7MWBRtbtF+w= -github.com/authzed/authzed-go v0.9.0/go.mod h1:9Pl5jDQJHrjbMDuCrsa+Q6Tqmi1f2pDdIn/qNGI++vA= +github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322 h1:xRXaLKkAQGF6aZv7KeoYCrxsHh1y9os6wFUUQ0TnMuE= +github.com/authzed/authzed-go v0.9.1-0.20230808160157-67ca5a9f8322/go.mod h1:9Pl5jDQJHrjbMDuCrsa+Q6Tqmi1f2pDdIn/qNGI++vA= github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5 h1:Fg92G8sNNODbNe2ckJoLeMEPeDqSfygmXnpEXDnVifU= github.com/authzed/grpcutil v0.0.0-20230703173955-bdd0ac3f16a5/go.mod h1:qx105brQubHFYLRja6wlHA+JB8DSK+yhb8uc8aFA5NQ= github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= @@ -903,8 +903,8 @@ github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/jzelinskie/cobrautil/v2 v2.0.0-20230714172849-80717639cec5 h1:X7vo4fEmuKLS4YQ+WkQ9xEtnR93tqkcYnH4rDDkfzhI= github.com/jzelinskie/cobrautil/v2 v2.0.0-20230714172849-80717639cec5/go.mod h1:954benQgK9Oi403yRaIot4TgRM0dDLKrBj48K7J8NZg= -github.com/jzelinskie/stringz v0.0.1 h1:IahR+y8ct2nyj7B6i8UtFsGFj4ex1SX27iKFYsAheLk= -github.com/jzelinskie/stringz v0.0.1/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= +github.com/jzelinskie/stringz v0.0.2 h1:OSjMEYvz8tjhovgZ/6cGcPID736ubeukr35mu6RYAmg= +github.com/jzelinskie/stringz v0.0.2/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= diff --git a/internal/services/integrationtesting/testconfigs/multipleslashes.yaml b/internal/services/integrationtesting/testconfigs/multipleslashes.yaml new file mode 100644 index 0000000000..94f3c83fcb --- /dev/null +++ b/internal/services/integrationtesting/testconfigs/multipleslashes.yaml @@ -0,0 +1,37 @@ +--- +schema: |+ + definition org/identity_team/user {} + + definition org/identity_team/group { + relation member: org/identity_team/user | org/identity_team/group#member + } + + definition org/docs_team/resource { + relation host: org/infra_team/resource + + relation viewer: org/identity_team/group#member | org/identity_team/group#member with foo/bar/only_on_tuesday + } + + definition org/infra_team/resource { + relation viewer: org/identity_team/group#member | org/identity_team/group#member with foo/bar/only_on_tuesday + } + + caveat foo/bar/only_on_tuesday(day_of_week string) { + day_of_week == 'tuesday' + } + +relationships: >- + org/identity_team/group:ultragroup#member@org/identity_team/user:someguy#... + + org/identity_team/group:megagroup#member@org/identity_team/group:ultragroup#member + + org/identity_team/group:supergroup#member@org/identity_team/group:megagroup#member + + org/identity_team/group:subgroup#member@org/identity_team/group:supergroup#member + + org/docs_team/resource:someresource#viewer@org/identity_team/group:subgroup#member + + org/docs_team/resource:someresource#host@org/infra_team/resource:prod-box +assertions: + assertTrue: [] + assertFalse: [] diff --git a/pkg/development/wasm/operations_test.go b/pkg/development/wasm/operations_test.go index 2799702d14..de010f3bcc 100644 --- a/pkg/development/wasm/operations_test.go +++ b/pkg/development/wasm/operations_test.go @@ -77,7 +77,7 @@ func TestCheckOperation(t *testing.T) { tuple.MustParse("somenamespace:someobj#anotherrel@user:foo"), nil, &devinterface.DeveloperError{ - Message: "error in object definition fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + Message: "error in object definition fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", Kind: devinterface.DeveloperError_SCHEMA_ISSUE, Source: devinterface.DeveloperError_SCHEMA, Line: 1, diff --git a/pkg/proto/core/v1/core.pb.go b/pkg/proto/core/v1/core.pb.go index 52c2f108f6..04ac6690fe 100644 --- a/pkg/proto/core/v1/core.pb.go +++ b/pkg/proto/core/v1/core.pb.go @@ -2629,7 +2629,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, - 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, + 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -2646,7 +2646,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, @@ -2733,7 +2733,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x12, 0x5c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, + 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, @@ -2860,7 +2860,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, - 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x3f, 0x5b, 0x61, 0x2d, + 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, diff --git a/pkg/proto/core/v1/core.pb.validate.go b/pkg/proto/core/v1/core.pb.validate.go index f6f3ecf2a4..e956896c51 100644 --- a/pkg/proto/core/v1/core.pb.validate.go +++ b/pkg/proto/core/v1/core.pb.validate.go @@ -834,7 +834,7 @@ func (m *ObjectAndRelation) validate(all bool) error { if !_ObjectAndRelation_Namespace_Pattern.MatchString(m.GetNamespace()) { err := ObjectAndRelationValidationError{ field: "Namespace", - reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", } if !all { return err @@ -966,7 +966,7 @@ var _ interface { ErrorName() string } = ObjectAndRelationValidationError{} -var _ObjectAndRelation_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$") +var _ObjectAndRelation_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$") var _ObjectAndRelation_ObjectId_Pattern = regexp.MustCompile("^(([a-zA-Z0-9/_|\\-=+]{1,})|\\*)$") @@ -1008,7 +1008,7 @@ func (m *RelationReference) validate(all bool) error { if !_RelationReference_Namespace_Pattern.MatchString(m.GetNamespace()) { err := RelationReferenceValidationError{ field: "Namespace", - reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", } if !all { return err @@ -1118,7 +1118,7 @@ var _ interface { ErrorName() string } = RelationReferenceValidationError{} -var _RelationReference_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$") +var _RelationReference_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$") var _RelationReference_Relation_Pattern = regexp.MustCompile("^(\\.\\.\\.|[a-z][a-z0-9_]{1,62}[a-z0-9])$") @@ -2243,7 +2243,7 @@ func (m *NamespaceDefinition) validate(all bool) error { if !_NamespaceDefinition_Name_Pattern.MatchString(m.GetName()) { err := NamespaceDefinitionValidationError{ field: "Name", - reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", } if !all { return err @@ -2423,7 +2423,7 @@ var _ interface { ErrorName() string } = NamespaceDefinitionValidationError{} -var _NamespaceDefinition_Name_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,62}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$") +var _NamespaceDefinition_Name_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$") // Validate checks the field values on Relation with the rules defined in the // proto definition for this message. If any rules are violated, the first @@ -3336,7 +3336,7 @@ func (m *AllowedRelation) validate(all bool) error { if !_AllowedRelation_Namespace_Pattern.MatchString(m.GetNamespace()) { err := AllowedRelationValidationError{ field: "Namespace", - reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + reason: "value does not match regex pattern \"^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", } if !all { return err @@ -3560,7 +3560,7 @@ var _ interface { ErrorName() string } = AllowedRelationValidationError{} -var _AllowedRelation_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$") +var _AllowedRelation_Namespace_Pattern = regexp.MustCompile("^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$") var _AllowedRelation_Relation_Pattern = regexp.MustCompile("^(\\.\\.\\.|[a-z][a-z0-9_]{1,62}[a-z0-9])$") diff --git a/pkg/schemadsl/compiler/compiler_test.go b/pkg/schemadsl/compiler/compiler_test.go index f354136bc3..7df511e500 100644 --- a/pkg/schemadsl/compiler/compiler_test.go +++ b/pkg/schemadsl/compiler/compiler_test.go @@ -485,7 +485,7 @@ func TestCompile(t *testing.T) { "invalid definition name", nil, `definition someTenant/fo {}`, - "parse error in `invalid definition name`, line 1, column 1: error in object definition someTenant/fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + "parse error in `invalid definition name`, line 1, column 1: error in object definition someTenant/fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", []SchemaDefinition{}, }, { @@ -801,6 +801,46 @@ func TestCompile(t *testing.T) { ), }, }, + { + "wrong tenant is not translated", + &someTenant, + `definition someothertenant/simple { + permission foos = (first + second) + (third + fourth) + }`, + "", + []SchemaDefinition{ + namespace.Namespace("someothertenant/simple", + namespace.MustRelation("foos", + namespace.Union( + namespace.ComputedUserset("first"), + namespace.ComputedUserset("second"), + namespace.ComputedUserset("third"), + namespace.ComputedUserset("fourth"), + ), + ), + ), + }, + }, + { + "multiple-segment tenant", + &someTenant, + `definition sometenant/some_team/simple { + permission foos = (first + second) + (third + fourth) + }`, + "", + []SchemaDefinition{ + namespace.Namespace("sometenant/some_team/simple", + namespace.MustRelation("foos", + namespace.Union( + namespace.ComputedUserset("first"), + namespace.ComputedUserset("second"), + namespace.ComputedUserset("third"), + namespace.ComputedUserset("fourth"), + ), + ), + ), + }, + }, { "multiple levels of compressed nesting", &someTenant, diff --git a/pkg/schemadsl/compiler/translator.go b/pkg/schemadsl/compiler/translator.go index 5e9b9455be..a46b4bc581 100644 --- a/pkg/schemadsl/compiler/translator.go +++ b/pkg/schemadsl/compiler/translator.go @@ -24,7 +24,7 @@ type translationContext struct { func (tctx translationContext) prefixedPath(definitionName string) (string, error) { var prefix, name string - if err := stringz.SplitExact(definitionName, "/", &prefix, &name); err != nil { + if err := stringz.SplitInto(definitionName, "/", &prefix, &name); err != nil { if tctx.objectTypePrefix == nil { return "", fmt.Errorf("found reference `%s` without prefix", definitionName) } diff --git a/pkg/schemadsl/compiler/translator_test.go b/pkg/schemadsl/compiler/translator_test.go new file mode 100644 index 0000000000..f1c78d14be --- /dev/null +++ b/pkg/schemadsl/compiler/translator_test.go @@ -0,0 +1,45 @@ +package compiler + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPrefixedPath(t *testing.T) { + fooPrefix := "foo" + barPrefix := "bar" + noPrefix := "" + + testCases := []struct { + input string + prefix *string + expectedError bool + expected string + }{ + {"bar", &fooPrefix, false, "foo/bar"}, + {"bar", &barPrefix, false, "bar/bar"}, + {"bar", &noPrefix, false, "bar"}, + {"foo/bar", &fooPrefix, false, "foo/bar"}, + {"foo/bar", &barPrefix, false, "foo/bar"}, + {"foo/bar", &noPrefix, false, "foo/bar"}, + {"bar", nil, true, ""}, + } + + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + require := require.New(t) + + tctx := translationContext{ + objectTypePrefix: tc.prefix, + } + output, err := tctx.prefixedPath(tc.input) + if tc.expectedError { + require.Error(err) + } else { + require.NoError(err) + require.Equal(tc.expected, output) + } + }) + } +} diff --git a/pkg/schemadsl/lexer/lex_test.go b/pkg/schemadsl/lexer/lex_test.go index 645fb708cf..c310e74083 100644 --- a/pkg/schemadsl/lexer/lex_test.go +++ b/pkg/schemadsl/lexer/lex_test.go @@ -66,6 +66,19 @@ var lexerTests = []lexerTest{ tEOF, }}, + {"multiple slash path", "foo/bar/baz/bang/zoom", []Lexeme{ + {TokenTypeIdentifier, 0, "foo", ""}, + {TokenTypeDiv, 0, "/", ""}, + {TokenTypeIdentifier, 0, "bar", ""}, + {TokenTypeDiv, 0, "/", ""}, + {TokenTypeIdentifier, 0, "baz", ""}, + {TokenTypeDiv, 0, "/", ""}, + {TokenTypeIdentifier, 0, "bang", ""}, + {TokenTypeDiv, 0, "/", ""}, + {TokenTypeIdentifier, 0, "zoom", ""}, + tEOF, + }}, + {"type star", "foo:*", []Lexeme{ {TokenTypeIdentifier, 0, "foo", ""}, {TokenTypeColon, 0, ":", ""}, diff --git a/pkg/schemadsl/parser/parser.go b/pkg/schemadsl/parser/parser.go index 3f528d2fb3..ca27e9217d 100644 --- a/pkg/schemadsl/parser/parser.go +++ b/pkg/schemadsl/parser/parser.go @@ -2,6 +2,8 @@ package parser import ( + "strings" + "github.com/authzed/spicedb/pkg/schemadsl/dslshape" "github.com/authzed/spicedb/pkg/schemadsl/input" "github.com/authzed/spicedb/pkg/schemadsl/lexer" @@ -392,22 +394,23 @@ func (p *sourceParser) consumeSpecificType() AstNode { } func (p *sourceParser) consumeTypePath() (string, bool) { - typeNameOrNamespace, ok := p.consumeIdentifier() - if !ok { - return "", false - } + var segments []string - _, ok = p.tryConsume(lexer.TokenTypeDiv) - if !ok { - return typeNameOrNamespace, true - } + for { + segment, ok := p.consumeIdentifier() + if !ok { + return "", false + } - typeName, ok := p.consumeIdentifier() - if !ok { - return "", false + segments = append(segments, segment) + + _, ok = p.tryConsume(lexer.TokenTypeDiv) + if !ok { + break + } } - return typeNameOrNamespace + "/" + typeName, true + return strings.Join(segments, "/"), true } // consumePermission consumes a permission. diff --git a/pkg/schemadsl/parser/parser_test.go b/pkg/schemadsl/parser/parser_test.go index 26b7d5109f..75eb75ce7c 100644 --- a/pkg/schemadsl/parser/parser_test.go +++ b/pkg/schemadsl/parser/parser_test.go @@ -104,6 +104,7 @@ func TestParser(t *testing.T) { {"indented comments test", "indentedcomments"}, {"parens test", "parens"}, {"multiple parens test", "multiparen"}, + {"multiple slashes in object type", "multipleslashes"}, {"wildcard test", "wildcard"}, {"broken wildcard test", "brokenwildcard"}, {"nil test", "nil"}, diff --git a/pkg/schemadsl/parser/tests/multipleslashes.zed b/pkg/schemadsl/parser/tests/multipleslashes.zed new file mode 100644 index 0000000000..e95f6c4d54 --- /dev/null +++ b/pkg/schemadsl/parser/tests/multipleslashes.zed @@ -0,0 +1,9 @@ +definition org/team/user {} + +definition foo/bar/baz { + relation viewer: org/team/user with foo/bar/only_on_tuesday +} + +caveat foo/bar/only_on_tuesday(day_of_week string) { + day_of_week == 'tuesday' +} diff --git a/pkg/schemadsl/parser/tests/multipleslashes.zed.expected b/pkg/schemadsl/parser/tests/multipleslashes.zed.expected new file mode 100644 index 0000000000..f344af383c --- /dev/null +++ b/pkg/schemadsl/parser/tests/multipleslashes.zed.expected @@ -0,0 +1,61 @@ +NodeTypeFile + end-rune = 202 + input-source = multiple slashes in object type + start-rune = 0 + child-node => + NodeTypeDefinition + definition-name = org/team/user + end-rune = 26 + input-source = multiple slashes in object type + start-rune = 0 + NodeTypeDefinition + definition-name = foo/bar/baz + end-rune = 118 + input-source = multiple slashes in object type + start-rune = 29 + child-node => + NodeTypeRelation + end-rune = 116 + input-source = multiple slashes in object type + relation-name = viewer + start-rune = 58 + allowed-types => + NodeTypeTypeReference + end-rune = 116 + input-source = multiple slashes in object type + start-rune = 75 + type-ref-type => + NodeTypeSpecificTypeReference + end-rune = 87 + input-source = multiple slashes in object type + start-rune = 75 + type-name = org/team/user + caveat => + NodeTypeCaveatReference + caveat-name = foo/bar/only_on_tuesday + end-rune = 116 + input-source = multiple slashes in object type + start-rune = 89 + NodeTypeCaveatDefinition + caveat-definition-name = foo/bar/only_on_tuesday + end-rune = 201 + input-source = multiple slashes in object type + start-rune = 121 + caveat-definition-expression => + NodeTypeCaveatExpession + caveat-expression-expressionstr = day_of_week == 'tuesday' + end-rune = 199 + input-source = multiple slashes in object type + start-rune = 176 + parameters => + NodeTypeCaveatParameter + caveat-parameter-name = day_of_week + end-rune = 169 + input-source = multiple slashes in object type + start-rune = 152 + caveat-parameter-type => + NodeTypeCaveatTypeReference + end-rune = 169 + input-source = multiple slashes in object type + start-rune = 164 + type-name = string \ No newline at end of file diff --git a/pkg/tuple/tuple.go b/pkg/tuple/tuple.go index 1dbc1e1b3d..283982eeca 100644 --- a/pkg/tuple/tuple.go +++ b/pkg/tuple/tuple.go @@ -25,11 +25,11 @@ const ( ) const ( - namespaceNameExpr = "([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]" + namespaceNameExpr = "([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]" resourceIDExpr = "([a-zA-Z0-9/_|\\-=+]{1,})" subjectIDExpr = "([a-zA-Z0-9/_|\\-=+]{1,})|\\*" relationExpr = "[a-z][a-z0-9_]{1,62}[a-z0-9]" - caveatNameExpr = "([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]" + caveatNameExpr = "([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]" ) var onrExpr = fmt.Sprintf( diff --git a/pkg/tuple/tuple_test.go b/pkg/tuple/tuple_test.go index fed1f3c07d..23774f7993 100644 --- a/pkg/tuple/tuple_test.go +++ b/pkg/tuple/tuple_test.go @@ -119,6 +119,15 @@ var testCases = []struct { ), relFormat: rel("tenant/testns", "testobj", "testrel", "tenant/user", "testusr", "somerel"), }, + { + input: "org/division/team/testns:testobj#testrel@org/division/identity_team/user:testusr#somerel", + expectedOutput: "org/division/team/testns:testobj#testrel@org/division/identity_team/user:testusr#somerel", + tupleFormat: makeTuple( + ObjectAndRelation("org/division/team/testns", "testobj", "testrel"), + ObjectAndRelation("org/division/identity_team/user", "testusr", "somerel"), + ), + relFormat: rel("org/division/team/testns", "testobj", "testrel", "org/division/identity_team/user", "testusr", "somerel"), + }, { input: "tenant/testns:testobj#testrel@tenant/user:testusr something", expectedOutput: "tenant/testns:testobj#testrel@tenant/user:testusr", @@ -194,6 +203,18 @@ var testCases = []struct { ), relFormat: crel("document", "foo", "viewer", "user", "tom", "", "tenant/somecaveat", nil), }, + { + input: "document:foo#viewer@user:tom[tenant/division/somecaveat]", + expectedOutput: "document:foo#viewer@user:tom[tenant/division/somecaveat]", + tupleFormat: MustWithCaveat( + makeTuple( + ObjectAndRelation("document", "foo", "viewer"), + ObjectAndRelation("user", "tom", "..."), + ), + "tenant/division/somecaveat", + ), + relFormat: crel("document", "foo", "viewer", "user", "tom", "", "tenant/division/somecaveat", nil), + }, { input: "document:foo#viewer@user:tom[somecaveat", expectedOutput: "", diff --git a/proto/internal/core/v1/core.proto b/proto/internal/core/v1/core.proto index 6d084c5ed6..5cebc9082a 100644 --- a/proto/internal/core/v1/core.proto +++ b/proto/internal/core/v1/core.proto @@ -70,7 +70,7 @@ message CaveatTypeReference { message ObjectAndRelation { /** namespace is the full namespace path for the referenced object */ string namespace = 1 [(validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$", + pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$", max_bytes: 128, }]; @@ -90,7 +90,7 @@ message ObjectAndRelation { message RelationReference { /** namespace is the full namespace path */ string namespace = 1 [(validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$", + pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$", max_bytes: 128, }]; @@ -174,7 +174,7 @@ message Metadata { message NamespaceDefinition { /** name is the unique for the namespace, including prefixes (which are optional) */ string name = 1 [(validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$", + pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$", max_bytes: 128, }]; @@ -369,7 +369,7 @@ message AllowedRelation { /** namespace is the full namespace path of the allowed object type */ string namespace = 1 [(validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$", + pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$", max_bytes: 128, }];