diff --git a/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs b/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs index af4371a0..ca82aedf 100644 --- a/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs +++ b/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs @@ -374,7 +374,8 @@ public async Task DeleteWhereAsync(Expression> predi /// The number of documents deleted. public async Task DeleteWhereAsync(Expression> predicate, string key, bool tenantAgnostic = false, CancellationToken cancellationToken = default) { - var queryable = GetQueryableCollection(tenantAgnostic); + // Strict tenant scoping on delete: never match shared "*" entities under a concrete tenant. + var queryable = GetQueryableCollection(tenantAgnostic, includeTenantAgnostic: false); var documentsToDelete = await queryable.Where(predicate).ToListAsync(cancellationToken); var count = documentsToDelete.LongCount(); var filter = documentsToDelete.BuildIdFilterForList(key); @@ -417,7 +418,8 @@ public async Task DeleteWhereAsync(Func, IQueryable< /// The number of documents deleted. public async Task DeleteWhereAsync(Func, IQueryable> query, string key = nameof(Entity.Id), bool tenantAgnostic = false, CancellationToken cancellationToken = default) { - var queryable = GetQueryableCollection(tenantAgnostic); + // Strict tenant scoping on delete: never match shared "*" entities under a concrete tenant. + var queryable = GetQueryableCollection(tenantAgnostic, includeTenantAgnostic: false); var documentsToDelete = await query(queryable).ToListAsync(cancellationToken); var count = documentsToDelete.LongCount(); var filter = documentsToDelete.BuildIdFilterForList(key); @@ -426,7 +428,7 @@ public async Task DeleteWhereAsync(Func, IQueryable< return count; } - private IQueryable GetQueryableCollection(bool tenantAgnostic = false) + private IQueryable GetQueryableCollection(bool tenantAgnostic = false, bool includeTenantAgnostic = true) { var queryable = collection.AsQueryable(); @@ -437,7 +439,13 @@ private IQueryable GetQueryableCollection(bool tenantAgnostic = false { var tenant = tenantAccessor.Tenant; var tenantId = tenant?.Id.EmptyToNull(); - queryable = queryable.Where(x => (x as Entity)!.TenantId == tenantId); + // Reads include tenant-agnostic ("*") rows so global entities (e.g. CLR workflow + // definitions) remain visible under a specific tenant, matching the EFCore provider. + // 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); } return queryable; @@ -448,7 +456,8 @@ private void ApplyTenantId(TDocument document) var tenant = tenantAccessor.Tenant; var tenantId = tenant?.Id; - if (document is Entity tenantDocument) + // Don't overwrite tenant-agnostic ("*") entities; only stamp tenant-specific ones. + if (document is Entity tenantDocument && tenantDocument.TenantId != Tenant.AgnosticTenantId) tenantDocument.TenantId = tenantId.EmptyToNull(); } @@ -459,7 +468,8 @@ private void ApplyTenantId(IEnumerable documents) foreach (var document in documents) { - if (document is Entity tenantDocument) + // Don't overwrite tenant-agnostic ("*") entities; only stamp tenant-specific ones. + if (document is Entity tenantDocument && tenantDocument.TenantId != Tenant.AgnosticTenantId) tenantDocument.TenantId = tenantId.EmptyToNull(); } }