Global query filter
Global Query Filter¶
This Implementation creates a dynamic security filter that automatically applies access control rules to all entities implementing IAccessControlledEntity. It ensures users can only see data they own or have explicit permission to access.
flowchart TD
A[Start Filter] --> B{Access Controlled?}
B -->|No| C[No Security]
B -->|Yes| D{User Logged In?}
D -->|No| E[Block Access]
D -->|Yes| F{Is Owner?}
F -->|Yes| G[Allow Access]
F -->|No| H{Has Valid Grant?}
H -->|Yes| G
H -->|No| E Flow Description¶
-
Initial Filter Check
If entity doesn't implement
IAccessControlledEntity, then it should return base filter.if (!typeof(IAccessControlledEntity).IsAssignableFrom(typeof(TEntity))) { return baseExpression; } -
Dynamic Primary Key Resolution
Handle entities with different primary key names
var entityId = modelBuilder.Entity<TEntity>().Metadata.FindPrimaryKey()?.Properties.First().Name ?? "Id"; -
Access Control Logic Construction
The core security logic follows a two-path access model:
Path A: Ownership Access
Users automatically access entities they own, Ownership bypasses all grant checks for performance
var entityId = modelBuilder.Entity<TEntity>().Metadata.FindPrimaryKey()?.Properties.First().Name ?? "Id";Path B: Grant-Based Access
Grant Validity Checks:
-
Expiration Check:
(ag.ExpiresAt == null || ag.ExpiresAt > NowUtc)- Grant not expired -
Entity Matching:
ag.EntityId == EF.Property<Guid>(e, entityId)- Matches target entityag.EntityType == typeof(TEntity).Name- Matches entity type
-
Tenant Isolation:
ag.TenantId == ScopeTenantId- Prevents cross-tenant data leaks -
Permission Verification:
((int)ag.Permissions & (int)PermissionFlags.Read) == (int)PermissionFlags.Read- Bitwise Read permission check
Grant Assignment Checks:
-
User Assignment:
ag.UserId != null && ag.UserId == ScopeUserId- Direct user grant OR -
OU Assignment:
ag.OrganizationUnitId != null- OU-based grantScopeOrganizationUnitIds.Count > 0- User has OUsScopeOrganizationUnitIds.Contains(ag.OrganizationUnitId.Value)- User in granted OU
-
Security Principles Implemented¶
-
Defense in Depth
-
Multiple validation checks
- Tenant isolation
-
Type safety through entity type matching
-
Principle of Least Privilege
-
Explicit grants required for non-owners
- Permission granularity through bitwise flags
-
Temporal access control via expiration
-
Performance Optimization
-
Ownership shortcut avoids grant queries
- Combined filter execution at database level
- Dynamic PK handling prevents reflection overhead
Query Translation¶
The Expression Tree gets translated to SQL WHERE clauses:
WHERE (
[OwnerUserId] = @userid
OR EXISTS (
SELECT 1 FROM AccessGrants
WHERE (ExpiresAt IS NULL OR ExpiresAt > GETUTCDATE())
AND EntityId = [Id]
AND EntityType = 'EntityName'
AND TenantId = @tenantid
AND (Permissions & 1) = 1
AND (
UserId = @userid
OR (
OrganizationUnitId IS NOT NULL
AND OrganizationUnitId IN (user_ou_list)
)
)
)
)
Multi-Tenant Safety¶
- Explicit tenant ID matching prevents data leakage between tenants
- All access checks include tenant context