Skip to content

Commit bb31fc5

Browse files
support for rel and namespace deprecation
Signed-off-by: Kartikay <[email protected]>
1 parent 145df27 commit bb31fc5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3229
-724
lines changed

internal/relationships/validation.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55

66
"github.com/authzed/spicedb/internal/namespace"
7+
"github.com/authzed/spicedb/internal/services/shared"
78
"github.com/authzed/spicedb/pkg/caveats"
89
caveattypes "github.com/authzed/spicedb/pkg/caveats/types"
910
"github.com/authzed/spicedb/pkg/datastore"
@@ -52,6 +53,19 @@ func ValidateRelationshipUpdates(
5253
}
5354
}
5455

56+
ts := schema.NewTypeSystem(schema.ResolverForDatastoreReader(reader))
57+
58+
// check if the relation is deprecated for create or touch operations
59+
var relsToCheck []tuple.Relationship
60+
for _, update := range updates {
61+
if update.Operation == tuple.UpdateOperationTouch || update.Operation == tuple.UpdateOperationCreate {
62+
relsToCheck = append(relsToCheck, update.Relationship)
63+
}
64+
}
65+
if err := CheckDeprecationsOnRelationships(ctx, relsToCheck, ts); err != nil {
66+
return err
67+
}
68+
5569
return nil
5670
}
5771

@@ -84,6 +98,13 @@ func ValidateRelationshipsForCreateOrTouch(
8498
}
8599
}
86100

101+
ts := schema.NewTypeSystem(schema.ResolverForDatastoreReader(reader))
102+
103+
// Validate if the resource, relation or subject is deprecated
104+
if err := CheckDeprecationsOnRelationships(ctx, rels, ts); err != nil {
105+
return err
106+
}
107+
87108
return nil
88109
}
89110

@@ -277,3 +298,23 @@ func hasNonEmptyCaveatContext(relationship tuple.Relationship) bool {
277298
relationship.OptionalCaveat.Context != nil &&
278299
len(relationship.OptionalCaveat.Context.GetFields()) > 0
279300
}
301+
302+
func CheckDeprecationsOnRelationships(
303+
ctx context.Context,
304+
relationships []tuple.Relationship,
305+
ts *schema.TypeSystem,
306+
) error {
307+
for _, rel := range relationships {
308+
if err := checkForDeprecatedRelationsAndObjects(ctx, rel, ts); err != nil {
309+
return err
310+
}
311+
}
312+
return nil
313+
}
314+
315+
func checkForDeprecatedRelationsAndObjects(ctx context.Context, rel tuple.Relationship, ts *schema.TypeSystem) error {
316+
if err := ts.CheckRelationshipDeprecation(ctx, rel); err != nil {
317+
return shared.NewDeprecationError(err)
318+
}
319+
return nil
320+
}

internal/relationships/validation_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,200 @@ func TestValidateRelationshipOperations(t *testing.T) {
306306
core.RelationTupleUpdate_CREATE,
307307
"subjects of type `user with somecaveat and expiration` are not allowed on relation `resource#viewer`",
308308
},
309+
{
310+
"deprecation relation test",
311+
`use deprecation
312+
definition testuser {}
313+
definition user {}
314+
315+
definition document {
316+
@deprecated(error,"deprecated, migrate away")
317+
relation editor: testuser
318+
relation viewer: user
319+
}`,
320+
"document:foo#editor@testuser:tom",
321+
core.RelationTupleUpdate_CREATE,
322+
"relation document#editor is deprecated: deprecated, migrate away",
323+
},
324+
{
325+
"deprecated namespace test",
326+
`use deprecation
327+
@deprecated(error, "deprecated, migrate away")
328+
definition testuser {}
329+
definition user {}
330+
331+
definition document {
332+
relation editor: testuser
333+
}`,
334+
"document:foo#editor@testuser:tom",
335+
core.RelationTupleUpdate_CREATE,
336+
"object type testuser is deprecated: deprecated, migrate away",
337+
},
338+
{
339+
"deprecated relation subject type",
340+
`use deprecation
341+
definition user {}
342+
definition testuser {}
343+
344+
definition platform {
345+
relation viewer: user | @deprecated(warn, "comments") testuser
346+
relation auditor: user | @deprecated(error, "test") testuser
347+
}`,
348+
"platform:foo#auditor@testuser:test",
349+
core.RelationTupleUpdate_CREATE,
350+
"object type testuser is deprecated: test",
351+
},
352+
{
353+
"deprecated relation same subject type with wildcard",
354+
`use deprecation
355+
definition user {}
356+
definition testuser {}
357+
358+
definition platform {
359+
relation viewer: user | @deprecated(warn, "comments") testuser
360+
relation auditor: testuser | @deprecated(error, "no wildcard please") testuser:*
361+
}`,
362+
"platform:foo#auditor@testuser:*",
363+
core.RelationTupleUpdate_CREATE,
364+
"wildcard allowed type testuser:* is deprecated: no wildcard please",
365+
},
366+
{
367+
"deprecated subject without expiration when expiration required",
368+
`use expiration
369+
use deprecation
370+
371+
@deprecated(error, "do not use testuser")
372+
definition testuser {}
373+
374+
definition document {
375+
relation viewer: testuser with expiration
376+
}`,
377+
"document:foo#viewer@testuser:tom",
378+
core.RelationTupleUpdate_CREATE,
379+
"subjects of type `testuser` are not allowed on relation `document#viewer`; did you mean `testuser with expiration`?",
380+
},
381+
{
382+
"deprecated subject with expiration",
383+
`use expiration
384+
use deprecation
385+
386+
@deprecated(error, "do not use testuser")
387+
definition testuser {}
388+
389+
definition document {
390+
relation viewer: testuser with expiration
391+
}`,
392+
"document:foo#viewer@testuser:tom[expiration:2021-01-01T00:00:00Z]",
393+
core.RelationTupleUpdate_CREATE,
394+
"object type testuser is deprecated: do not use testuser",
395+
},
396+
{
397+
"deprecated subject with wrong caveat",
398+
`use deprecation
399+
400+
caveat somecaveat(somecondition int) {
401+
somecondition == 42
402+
}
403+
404+
caveat anothercaveat(somecondition int) {
405+
somecondition == 42
406+
}
407+
408+
@deprecated(error, "do not use testuser")
409+
definition testuser {}
410+
411+
definition document {
412+
relation viewer: testuser with somecaveat
413+
}`,
414+
"document:foo#viewer@testuser:tom[anothercaveat]",
415+
core.RelationTupleUpdate_CREATE,
416+
"subjects of type `testuser with anothercaveat` are not allowed on relation `document#viewer`",
417+
},
418+
{
419+
"deprecated subject with correct caveat",
420+
`use deprecation
421+
422+
caveat somecaveat(somecondition int) {
423+
somecondition == 42
424+
}
425+
426+
@deprecated(error, "do not use testuser")
427+
definition testuser {}
428+
429+
definition document {
430+
relation viewer: testuser with somecaveat
431+
}`,
432+
"document:foo#viewer@testuser:tom[somecaveat]",
433+
core.RelationTupleUpdate_CREATE,
434+
"object type testuser is deprecated: do not use testuser",
435+
},
436+
{
437+
"deprecated subject without caveat when required",
438+
`use deprecation
439+
440+
caveat somecaveat(somecondition int) {
441+
somecondition == 42
442+
}
443+
444+
@deprecated(error, "do not use testuser")
445+
definition testuser {}
446+
447+
definition document {
448+
relation viewer: testuser with somecaveat
449+
}`,
450+
"document:foo#viewer@testuser:tom",
451+
core.RelationTupleUpdate_CREATE,
452+
"subjects of type `testuser` are not allowed on relation `document#viewer` without one of the following caveats: somecaveat",
453+
},
454+
{
455+
"deprecated user with caveat and expiration",
456+
`use expiration
457+
use deprecation
458+
459+
caveat somecaveat(somecondition int) {
460+
somecondition == 42
461+
}
462+
463+
definition user {}
464+
465+
definition document {
466+
relation viewer: user | @deprecated(error, "don't use this") user with somecaveat and expiration
467+
}`,
468+
"document:foo#viewer@user:tom[somecaveat][expiration:2021-01-01T00:00:00Z]",
469+
core.RelationTupleUpdate_CREATE,
470+
"object type user with caveat `somecaveat` and expiration is deprecated: don't use this",
471+
},
472+
{
473+
"deprecated user with caveat only",
474+
`use deprecation
475+
476+
caveat somecaveat(somecondition int) {
477+
somecondition == 42
478+
}
479+
480+
definition user {}
481+
482+
definition document {
483+
relation viewer: user | @deprecated(error, "caveated access deprecated") user with somecaveat
484+
}`,
485+
"document:foo#viewer@user:tom[somecaveat]",
486+
core.RelationTupleUpdate_CREATE,
487+
"object type user with caveat `somecaveat` is deprecated: caveated access deprecated",
488+
},
489+
{
490+
"deprecated user with expiration only",
491+
`use expiration
492+
use deprecation
493+
494+
definition user {}
495+
496+
definition document {
497+
relation viewer: user | @deprecated(error, "expiring access deprecated") user with expiration
498+
}`,
499+
"document:foo#viewer@user:tom[expiration:2021-01-01T00:00:00Z]",
500+
core.RelationTupleUpdate_CREATE,
501+
"object type user with expiration is deprecated: expiring access deprecated",
502+
},
309503
}
310504

311505
for _, tc := range tcs {

internal/services/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func RegisterGrpcServices(
7373
CaveatTypeSet: permSysConfig.CaveatTypeSet,
7474
AdditiveOnly: schemaServiceOption == V1SchemaServiceAdditiveOnly,
7575
ExpiringRelsEnabled: permSysConfig.ExpiringRelationshipsEnabled,
76+
FeatureDeprecationEnabled: permSysConfig.DeprecatedRelationshipsAndObjectsEnabled,
7677
PerformanceInsightMetricsEnabled: permSysConfig.PerformanceInsightMetricsEnabled,
7778
}
7879
v1.RegisterSchemaServiceServer(srv, v1svc.NewSchemaServer(schemaConfig))

internal/services/shared/errors.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ func NewSchemaWriteDataValidationError(message string, args ...any) SchemaWriteD
4747
}
4848
}
4949

50+
// DeprecationError is an error returned when a schema object or relation is deprecated.
51+
type DeprecationError struct {
52+
error
53+
}
54+
55+
func (err DeprecationError) GRPCStatus() *status.Status {
56+
return spiceerrors.WithCodeAndDetails(
57+
err,
58+
codes.Aborted,
59+
spiceerrors.ForReason(
60+
// TODO: replace with a deprecation type error reason
61+
v1.ErrorReason_ERROR_REASON_SCHEMA_TYPE_ERROR,
62+
map[string]string{},
63+
),
64+
)
65+
}
66+
67+
func NewDeprecationError(err error) DeprecationError {
68+
return DeprecationError{
69+
error: err,
70+
}
71+
}
72+
5073
// SchemaWriteDataValidationError occurs when a schema cannot be applied due to leaving data unreferenced.
5174
type SchemaWriteDataValidationError struct {
5275
error

internal/services/v1/relationships.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type PermissionsServerConfig struct {
104104
// ExpiringRelationshipsEnabled defines whether or not expiring relationships are enabled.
105105
ExpiringRelationshipsEnabled bool
106106

107+
// DeprecatedRelationshipsEnabled defines whether or not deprecated relationships are enabled.
108+
DeprecatedRelationshipsAndObjectsEnabled bool
109+
107110
// CaveatTypeSet is the set of caveat types to use for caveats. If not specified,
108111
// the default type set is used.
109112
CaveatTypeSet *caveattypes.TypeSet
@@ -118,22 +121,23 @@ func NewPermissionsServer(
118121
config PermissionsServerConfig,
119122
) v1.PermissionsServiceServer {
120123
configWithDefaults := PermissionsServerConfig{
121-
MaxPreconditionsCount: defaultIfZero(config.MaxPreconditionsCount, 1000),
122-
MaxUpdatesPerWrite: defaultIfZero(config.MaxUpdatesPerWrite, 1000),
123-
MaximumAPIDepth: defaultIfZero(config.MaximumAPIDepth, 50),
124-
StreamingAPITimeout: defaultIfZero(config.StreamingAPITimeout, 30*time.Second),
125-
MaxCaveatContextSize: defaultIfZero(config.MaxCaveatContextSize, 4096),
126-
MaxRelationshipContextSize: defaultIfZero(config.MaxRelationshipContextSize, 25_000),
127-
MaxDatastoreReadPageSize: defaultIfZero(config.MaxDatastoreReadPageSize, 1_000),
128-
MaxReadRelationshipsLimit: defaultIfZero(config.MaxReadRelationshipsLimit, 1_000),
129-
MaxDeleteRelationshipsLimit: defaultIfZero(config.MaxDeleteRelationshipsLimit, 1_000),
130-
MaxLookupResourcesLimit: defaultIfZero(config.MaxLookupResourcesLimit, 1_000),
131-
MaxBulkExportRelationshipsLimit: defaultIfZero(config.MaxBulkExportRelationshipsLimit, 100_000),
132-
DispatchChunkSize: defaultIfZero(config.DispatchChunkSize, 100),
133-
MaxCheckBulkConcurrency: defaultIfZero(config.MaxCheckBulkConcurrency, 50),
134-
CaveatTypeSet: caveattypes.TypeSetOrDefault(config.CaveatTypeSet),
135-
ExpiringRelationshipsEnabled: config.ExpiringRelationshipsEnabled,
136-
PerformanceInsightMetricsEnabled: config.PerformanceInsightMetricsEnabled,
124+
MaxPreconditionsCount: defaultIfZero(config.MaxPreconditionsCount, 1000),
125+
MaxUpdatesPerWrite: defaultIfZero(config.MaxUpdatesPerWrite, 1000),
126+
MaximumAPIDepth: defaultIfZero(config.MaximumAPIDepth, 50),
127+
StreamingAPITimeout: defaultIfZero(config.StreamingAPITimeout, 30*time.Second),
128+
MaxCaveatContextSize: defaultIfZero(config.MaxCaveatContextSize, 4096),
129+
MaxRelationshipContextSize: defaultIfZero(config.MaxRelationshipContextSize, 25_000),
130+
MaxDatastoreReadPageSize: defaultIfZero(config.MaxDatastoreReadPageSize, 1_000),
131+
MaxReadRelationshipsLimit: defaultIfZero(config.MaxReadRelationshipsLimit, 1_000),
132+
MaxDeleteRelationshipsLimit: defaultIfZero(config.MaxDeleteRelationshipsLimit, 1_000),
133+
MaxLookupResourcesLimit: defaultIfZero(config.MaxLookupResourcesLimit, 1_000),
134+
MaxBulkExportRelationshipsLimit: defaultIfZero(config.MaxBulkExportRelationshipsLimit, 100_000),
135+
DispatchChunkSize: defaultIfZero(config.DispatchChunkSize, 100),
136+
MaxCheckBulkConcurrency: defaultIfZero(config.MaxCheckBulkConcurrency, 50),
137+
CaveatTypeSet: caveattypes.TypeSetOrDefault(config.CaveatTypeSet),
138+
ExpiringRelationshipsEnabled: config.ExpiringRelationshipsEnabled,
139+
DeprecatedRelationshipsAndObjectsEnabled: config.DeprecatedRelationshipsAndObjectsEnabled,
140+
PerformanceInsightMetricsEnabled: config.PerformanceInsightMetricsEnabled,
137141
}
138142

139143
return &permissionServer{
@@ -324,6 +328,7 @@ func (ps *permissionServer) WriteRelationships(ctx context.Context, req *v1.Writ
324328
updateRelationshipSet := mapz.NewSet[string]()
325329
for _, update := range req.Updates {
326330
// TODO(jschorr): Change to struct-based keys.
331+
327332
tupleStr := tuple.V1StringRelationshipWithoutCaveatOrExpiration(update.Relationship)
328333
if !updateRelationshipSet.Add(tupleStr) {
329334
return nil, ps.rewriteError(

0 commit comments

Comments
 (0)