Hey everyone! Wanted to share something that I've been working on the side. I've released TenantGuard 1.0.0, an open-source set of packages for EF Core 10 and 11, licensed under Apache-2.0.
It adds checks around applications that use query filters for tenant isolation. Those filters handle query scoping, but a new entity can be missing its filter, a query can bypass one, and tracked writes need their own tenant checks.
For example, if another tenant's entity gets attached to the context, the query filter alone won't prevent an update or delete through SaveChanges.
TenantGuard checks for these problems in a few places:
At build time, Roslyn analyzers (TG001–TG005) flag filter bypasses without a stated reason, including IgnoreQueryFilters() and ABP's IDataFilter.Disable<IMultiTenant>(). They also check raw SQL through EF Core, Dapper and ADO.NET for missing tenant-column comparisons.
At startup, 14 model rules check for missing filters, filters that accidentally hold a fixed tenant value, missing tenant indexes, and unique indexes that span tenants.
During SaveChanges, the write guard checks tracked inserts, updates and deletes against the current tenant. It also rejects tenant reassignment. Version 1.0 adds checks for owned entities through their owner, including changes such as order.Shipping.Street.
In tests, the isolation probe seeds a row per tenant and checks reads, Find, updates, deletes and Include. Each check appears as a separate xUnit v3 or TUnit test.
Here's the configuration for the write guard and startup validation:
builder.Services.AddDbContext<AppDbContext>(options => options
.UseSqlServer(connectionString)
.UseTenantGuard(tg => tg.CurrentTenant(ctx => ((AppDbContext)ctx).TenantId)));
builder.Services.AddTenantGuardStartupValidation<AppDbContext>();
It works with custom query filters and reads Finbuckle.MultiTenant's IsMultiTenant() configuration without extra setup. The dotnet tenantguard validate command can also check the model in CI without a database.
GitHub · Docs
If you try it against an existing model, I'd appreciate examples of false positives or tenant isolation problems the checks miss.
I've released TenantGuard 1.0.0, an open-source set of packages for EF Core 10 and 11, licensed under Apache-2.0.
It adds checks around applications that use query filters for tenant isolation. Those filters handle query scoping, but a new entity can be missing its filter, a query can bypass one, and tracked writes need their own tenant checks.
For example, if another tenant's entity gets attached to the context, the query filter alone won't prevent an update or delete through SaveChanges.
TenantGuard checks for these problems in a few places:
At build time, Roslyn analyzers (TG001–TG005) flag filter bypasses without a stated reason, including IgnoreQueryFilters() and ABP's IDataFilter.Disable<IMultiTenant>(). They also check raw SQL through EF Core, Dapper and ADO.NET for missing tenant-column comparisons.
At startup, 14 model rules check for missing filters, filters that accidentally hold a fixed tenant value, missing tenant indexes, and unique indexes that span tenants.
During SaveChanges, the write guard checks tracked inserts, updates and deletes against the current tenant. It also rejects tenant reassignment. Version 1.0 adds checks for owned entities through their owner, including changes such as order.Shipping.Street.
In tests, the isolation probe seeds a row per tenant and checks reads, Find, updates, deletes and Include. Each check appears as a separate xUnit v3 or TUnit test.
Here's the configuration for the write guard and startup validation:
builder.Services.AddDbContext<AppDbContext>(options => options
.UseSqlServer(connectionString)
.UseTenantGuard(tg => tg.CurrentTenant(ctx => ((AppDbContext)ctx).TenantId)));
builder.Services.AddTenantGuardStartupValidation<AppDbContext>();
It works with custom query filters and reads Finbuckle.MultiTenant's IsMultiTenant() configuration without extra setup. The dotnet tenantguard validate command can also check the model in CI without a database.
GitHub · Docs
If you try it against an existing model, I'd appreciate examples of false positives or tenant isolation problems the checks miss. Open to any critical feedback that could help improve it!