| | | 1 | | using Elsa.Common.Entities; |
| | | 2 | | |
| | | 3 | | namespace Elsa.Common.Multitenancy; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Memory-store counterpart of EF Core <c>SetTenantIdFilter</c>. |
| | | 7 | | /// A row is visible when its <see cref="Entity.TenantId"/> matches the ambient tenant, |
| | | 8 | | /// is <see cref="Tenant.AgnosticTenantId"/>, or is null while the ambient tenant is the default. |
| | | 9 | | /// </summary> |
| | | 10 | | public static class TenantVisibility |
| | | 11 | | { |
| | | 12 | | /// <summary> |
| | | 13 | | /// Returns whether <paramref name="entityTenantId"/> is visible under <paramref name="ambientTenantId"/>. |
| | | 14 | | /// </summary> |
| | | 15 | | public static bool IsVisible(string? entityTenantId, string ambientTenantId) => |
| | 8 | 16 | | entityTenantId == ambientTenantId |
| | 8 | 17 | | || entityTenantId == Tenant.AgnosticTenantId |
| | 8 | 18 | | || entityTenantId is null && ambientTenantId == Tenant.DefaultTenantId; |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// Write counterpart of <see cref="IsVisible"/>. <c>*</c> is visible to every tenant, but only an |
| | | 22 | | /// agnostic writer may replace it. Named tenants may replace their own rows (and the default tenant |
| | | 23 | | /// may replace a null <see cref="Entity.TenantId"/>). |
| | | 24 | | /// </summary> |
| | | 25 | | public static bool CanReplace(string? existingTenantId, string writerTenantId) => |
| | 14 | 26 | | existingTenantId == writerTenantId |
| | 14 | 27 | | || existingTenantId is null && writerTenantId == Tenant.DefaultTenantId; |
| | | 28 | | |
| | | 29 | | /// <summary> |
| | | 30 | | /// CAS match for an upsert. Named rows are owned by the <paramref name="ambientTenantId"/> |
| | | 31 | | /// (Memory <see cref="CanReplace"/>), not by a forged source <c>TenantId</c>. A <c>*</c> row |
| | | 32 | | /// is replaceable only when both the incoming entity and the ambient writer are <c>*</c>. |
| | | 33 | | /// </summary> |
| | | 34 | | public static bool CanReplaceOwnedRow(string? existingTenantId, string? sourceTenantId, string ambientTenantId) |
| | | 35 | | { |
| | 10 | 36 | | if (existingTenantId == Tenant.AgnosticTenantId) |
| | 4 | 37 | | return sourceTenantId == Tenant.AgnosticTenantId && ambientTenantId == Tenant.AgnosticTenantId; |
| | | 38 | | |
| | 6 | 39 | | return CanReplace(existingTenantId, ambientTenantId); |
| | | 40 | | } |
| | | 41 | | |
| | | 42 | | /// <summary> |
| | | 43 | | /// Restricts <paramref name="queryable"/> to rows visible to <paramref name="ambientTenantId"/>, |
| | | 44 | | /// unless <paramref name="tenantAgnostic"/> is set (EF <c>IgnoreQueryFilters</c>). |
| | | 45 | | /// </summary> |
| | | 46 | | public static IQueryable<T> WhereVisibleToTenant<T>(this IQueryable<T> queryable, string ambientTenantId, bool tenan |
| | | 47 | | where T : Entity |
| | | 48 | | { |
| | 3 | 49 | | if (tenantAgnostic) |
| | 1 | 50 | | return queryable; |
| | | 51 | | |
| | 2 | 52 | | return queryable.Where(entity => |
| | 2 | 53 | | entity.TenantId == ambientTenantId |
| | 2 | 54 | | || entity.TenantId == Tenant.AgnosticTenantId |
| | 2 | 55 | | || entity.TenantId == null && ambientTenantId == Tenant.DefaultTenantId); |
| | | 56 | | } |
| | | 57 | | } |