feat: Require scopeId on domain tables to ensure user isolation (#73)

This commit is contained in:
Marcelo Mendonça Soares 2026-06-18 01:05:24 -03:00 • committed by GitHub
commit 84cfbadc45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 4258 additions and 770 deletions

View file

@ -10,8 +10,8 @@ extends: BaseHlc
table: crdt_data_rows
database: all
fields:
### Identifier for the user or client that owns the data.
user: CrdtUser?, relation(onDelete=Cascade)
### Identifier for the scope that owns the data.
scope: CrdtScope?, relation(onDelete=Cascade)
### Reference to the table this row belongs to.
tbl: CrdtSchemaTable?, relation(onDelete=Cascade)
@ -38,6 +38,6 @@ fields:
fields: List<CrdtDataField>?, relation(name=row_fields)
indexes:
crdt_data_rows_user_tbl_row_idx:
fields: userId, tblId, uuidRowId
crdt_data_rows_scope_tbl_row_idx:
fields: scopeId, tblId, uuidRowId
unique: true

View file

@ -5,8 +5,8 @@ class: CrdtNode
table: crdt_nodes
database: all
fields:
### The user that owns the node.
user: CrdtUser?, relation(name=user_nodes, onDelete=Cascade)
### The scope that owns the node.
scope: CrdtScope?, relation(name=scope_nodes, onDelete=Cascade)
### Unique identifier for the node.
uuidNodeId: UuidValue, default=random_v7, unique

View file

@ -0,0 +1,16 @@
### CRDT scopes table.
###
### Normalized storage for scope IDs. The normalized data table references this
### by integer id instead of storing the scope id string.
class: CrdtScope
table: crdt_scopes
database: all
fields:
### Scope identifier string.
uuidScopeId: UuidValue, default=random_v7, unique
### The current node id of the scope.
currentNode: CrdtNode?, relation(optional)
### The nodes associated with the scope.
nodes: List<CrdtNode>?, relation(name=scope_nodes)

View file

@ -1,16 +0,0 @@
### CRDT users table.
###
### Normalized storage for user IDs. The normalized data table references this
### by integer id instead of storing the user id string.
class: CrdtUser
table: crdt_users
database: all
fields:
### User identifier string.
uuidUserId: UuidValue, default=random_v7, unique
### The current node id of the user.
currentNode: CrdtNode?, relation(optional)
### The nodes associated with the user.
nodes: List<CrdtNode>?, relation(name=user_nodes)

View file

@ -0,0 +1,55 @@
### Durable record of a terminal CRDT sync integrity violation.
###
### These rows are sparse diagnostic state. They record observed corruption or
### hostile input without duplicating hot CRDT indexes.
###
### All identifiers are denormalized UUIDs and names so the record remains
### useful after merge rollbacks and across databases that do not share the
### same local CRDT metadata rows.
class: CrdtSyncIntegrityViolation
table: crdt_sync_integrity_violations
database: all
fields:
### High-level integrity violation category.
type: CrdtSyncViolationType
### Durable table name for the violated domain row.
domainTableName: String
### Domain row UUID involved in the violation.
uuidRowId: UuidValue
### Global scope UUID that owns the physical domain row, when known.
ownerScopeUuid: UuidValue?
### Global scope UUID that attempted to operate on the row.
incomingScopeUuid: UuidValue
### Merge or sync operation that observed the violation.
operation: CrdtSyncViolationOperation
### UUID of the node that authored the rejected change, when known.
uuidNodeId: UuidValue?
### Local CRDT metadata row id observed for the rejected operation, when known.
crdtDataRowId: int?
### Last observed HLC datetime for the rejected change, when known.
hlcDatetime: DateTime?
### Last observed HLC counter for the rejected change, when known.
hlcCounter: int?
### First time this violation key was observed.
firstSeenAt: DateTime
### Most recent time this violation key was observed.
lastSeenAt: DateTime
### Number of times this violation key has been observed.
occurrences: int
indexes:
crdt_sync_integrity_violations_key_idx:
fields: type, operation, domainTableName, uuidRowId, ownerScopeUuid, incomingScopeUuid
unique: true

View file

@ -0,0 +1,10 @@
### Merge or sync operation that observed an integrity violation.
enum: CrdtSyncViolationOperation
serialized: byName
values:
- mergeInsert
- mergeUpdate
- mergeDelete
- outboundInsert
- outboundUpdate
- outboundDelete

View file

@ -0,0 +1,6 @@
### High-level category of a durable CRDT sync integrity violation.
enum: CrdtSyncViolationType
serialized: byName
values:
- ownershipCollision
- missingDomainRow

View file

@ -10,12 +10,12 @@ resolution: workspace
dependencies:
meta: ^1.16.0
serverpod: 3.5.0-beta.9
serverpod_database: 3.5.0-beta.9
serverpod: 3.5.0-beta.10
serverpod_database: 3.5.0-beta.10
serverpod_offline_sync_client: 0.0.1
serverpod_offline_sync_shared: 0.0.1
dev_dependencies:
lints: '>=3.0.0 <7.0.0'
serverpod_test: 3.5.0-beta.9
serverpod_test: 3.5.0-beta.10
test: ^1.25.5