fix(mongodb): preserve and resolve tenant-agnostic ("*") entities - #155
Conversation
|
@dotnet-policy-service agree company="ContraForce" |
|
@greptile review |
There was a problem hiding this comment.
Pull request overview
Fixes MongoDB multitenancy behavior so tenant-agnostic ("*") entities are preserved on write and resolvable from within a specific tenant on read, aligning MongoDB persistence behavior with the EFCore provider and the documented tenant-agnostic contract.
Changes:
- Update tenant-scoped query filtering to also include tenant-agnostic (
TenantId == "*") documents. - Prevent
ApplyTenantIdfrom overwriting documents already marked as tenant-agnostic ("*"), preserving the marker during inserts/updates.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR fixes MongoDB multi-tenancy handling to align with the EFCore provider's treatment of tenant-agnostic (
Confidence Score: 5/5Safe to merge — the two private methods are changed consistently, all delete overloads protect global entities, and the fix matches documented EFCore provider behaviour. The write fix, read fix, and delete guard are all internally consistent. The second commit in this PR directly addresses the delete-path concern raised in the prior review. Every DeleteWhereAsync call chain routes through one of the two patched terminal overloads, so no delete path regressed. Read and count operations correctly include '*' entities, keeping counts in sync with result sets. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs | Three targeted fixes: ApplyTenantId now skips stamping entities already marked ""; GetQueryableCollection includes TenantId == "" in tenant-scoped reads (via new includeTenantAgnostic param, default true); both DeleteWhereAsync implementations explicitly pass includeTenantAgnostic: false so global entities cannot be destroyed by a tenant-scoped delete. All delete call chains route through the two patched overloads. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[GetQueryableCollection called] --> B{tenantAgnostic == true?}
B -- Yes --> C[Return unfiltered queryable\nall tenants + all '*' entities]
B -- No --> D{TDocument is Entity?}
D -- No --> E[Return unfiltered queryable]
D -- Yes --> F{includeTenantAgnostic?}
F -- true\ndefault reads --> G["Filter: TenantId == tenantId OR TenantId == '*'"]
F -- false\ndeletes --> H["Filter: TenantId == tenantId only"]
subgraph Callers
I[FindAsync / FindManyAsync / CountAsync / AnyAsync / ListAsync] --> |includeTenantAgnostic=true| F
J[DeleteWhereAsync both overloads] --> |includeTenantAgnostic=false| F
end
subgraph Write path
K[ApplyTenantId] --> L{entity.TenantId == '*'?}
L -- Yes --> M[Skip: preserve agnostic marker]
L -- No --> N[Stamp with tenantId.EmptyToNull]
end
Reviews (2): Last reviewed commit: "fix(mongodb): exclude tenant-agnostic ("..." | Re-trigger Greptile
|
Addressed the delete-path concern in 362c5d1.
Fix: added an |
|
@greptile review |
|
@sfmskywalker Just checking in to see if there are any further changes that are required for either this PR or #159 |
The MongoDb store overwrote the tenant-agnostic marker on write and excluded agnostic rows on read, so tenant-agnostic entities (e.g. CLR workflow definitions stamped by ClrWorkflowsProvider with "*") were persisted with a tenant-specific id and became unreachable when resolved under a specific tenant, throwing WorkflowDefinitionNotFoundException. Bring MongoDbStore to parity with the EFCore provider: - ApplyTenantId: don't overwrite TenantId when it is already "*" - GetQueryableCollection: include TenantId == "*" in the tenant filter Refs elsa-workflows/elsa-core#7691
…ed deletes GetQueryableCollection is shared by the read and delete paths. The read fix added an OR-branch for TenantId == "*", which also widened DeleteWhereAsync: a tenant-scoped delete could match and remove a shared "*" entity for every tenant (e.g. a CLR workflow definition deleted under one tenant). Add an includeTenantAgnostic flag (default true). Reads keep including "*" rows; both DeleteWhereAsync terminals pass includeTenantAgnostic: false so a tenant-scoped delete stays strict to the current tenant, matching the Dapper store. An explicit tenantAgnostic: true delete is unchanged, and the agnostic context (Tenant.Id == "*") can still manage "*" entities.
362c5d1 to
5b94ad1
Compare
|
PR author is not in the allowed authors list. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs:448
- When
includeTenantAgnosticisfalse(delete paths), this filter excludesTenantId == "*"even when there is no current tenant (tenantIdis null). Since reads in the same method include"*"whentenantIdis null, this can make"*"entities visible but not deletable in tenant-less scenarios. Consider only excluding"*"when a concrete tenant id is present.
// Deletes pass includeTenantAgnostic: false so a tenant-scoped delete cannot remove a
// shared "*" entity (the Dapper store keeps tenant-scoped deletes strict).
queryable = includeTenantAgnostic
? queryable.Where(x => (x as Entity)!.TenantId == tenantId || (x as Entity)!.TenantId == Tenant.AgnosticTenantId)
: queryable.Where(x => (x as Entity)!.TenantId == tenantId);
|
@sfmskywalker rebased the branch. no other changes, but it looks like the PR workflow may require another review. |
sfmskywalker
left a comment
There was a problem hiding this comment.
Reviewed current head d489a75 against #158 and current main. Explicit and agnostic tenant IDs are preserved, only null IDs are stamped, tenant reads include shared rows, and both delete paths retain a final tenant predicate. Container-backed validation passes all 8 MongoDB unit tests; the persistence module builds for .NET 8, 9, and 10. No actionable root-review findings remain.
Summary
MongoDB multitenancy handling was inconsistent: tenant-agnostic (
TenantId == "*") workflow definitions could be overwritten on write and excluded from tenant-scoped reads. This made CLR workflows unreachable under concrete tenants.This change now:
"*", and stamps only documents whose tenant ID isnullnullmigration behaviorMigration note
Existing records whose intended agnostic marker was already clobbered to
nullare not changed automatically. Deployments should migrate affected records toTenantId = "*"or repopulate the workflow definitions after deployment. New writes preserve the marker.Validation
Elsa.MongoDb.UnitTestson .NET 10: 8 passedElsa.Persistence.MongoDbRelease build: .NET 8, 9, and 10 succeededgit diff --check: cleanThe build still reports repository-existing dependency/nullability/trimming warnings; this PR introduces no new package dependency.
Fixes #158