feat(auth): add Redis authorization caching
This commit is contained in:
87
Tiku.Application/Security/AuthorizationCacheModels.cs
Normal file
87
Tiku.Application/Security/AuthorizationCacheModels.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Tenancy;
|
||||
using Tiku.Application.Auth;
|
||||
|
||||
namespace Tiku.Application.Security;
|
||||
|
||||
public enum AuthorizationCacheMode { Disabled, Shadow, Active }
|
||||
|
||||
public sealed class AuthorizationCacheOptions
|
||||
{
|
||||
public const string SectionName = "Security:AuthorizationCache";
|
||||
public AuthorizationCacheMode Mode { get; set; } = AuthorizationCacheMode.Disabled;
|
||||
public int LocalSnapshotSeconds { get; set; } = 60;
|
||||
public int DistributedStateSeconds { get; set; } = 60;
|
||||
public int DistributedSnapshotSeconds { get; set; } = 300;
|
||||
public int JitterPercent { get; set; } = 20;
|
||||
}
|
||||
|
||||
public sealed record CachedSessionSecurityState(
|
||||
Guid SessionId, Guid UserId, AuthRealm Realm, Guid? TenantId,
|
||||
string SecurityStamp, DateTimeOffset ExpiresAt, bool Revoked);
|
||||
public sealed record CachedUserSecurityState(Guid UserId, UserStatus Status, string SecurityStamp);
|
||||
public sealed record CachedTenantSecurityState(Guid TenantId, TenantStatus Status);
|
||||
public sealed record CachedMembershipSecurityState(Guid TenantId, Guid UserId, MembershipStatus Status);
|
||||
public sealed record CachedPlatformAccessState(Guid UserId, long AuthorizationVersion, bool Allowed);
|
||||
public sealed record CachedAuthorizationVersion(AuthRealm Realm, Guid? TenantId, long Version);
|
||||
|
||||
public sealed record AccessSecurityCacheLookup(Guid SessionId, Guid UserId, AuthRealm Realm, Guid? TenantId);
|
||||
public sealed record AccessSecurityCacheState(
|
||||
CachedSessionSecurityState? Session,
|
||||
CachedUserSecurityState? User,
|
||||
CachedTenantSecurityState? Tenant,
|
||||
CachedMembershipSecurityState? Membership,
|
||||
CachedPlatformAccessState? PlatformAccess,
|
||||
CachedAuthorizationVersion? AuthorizationVersion)
|
||||
{
|
||||
public bool Complete => Session is not null && User is not null && AuthorizationVersion is not null &&
|
||||
(Session.Realm == AuthRealm.Platform && PlatformAccess is not null ||
|
||||
Session.Realm == AuthRealm.Tenant && Tenant is not null && Membership is not null);
|
||||
}
|
||||
|
||||
public sealed record CachedAuthorizationSnapshot(long Version, CurrentAccessSnapshot Snapshot);
|
||||
|
||||
public interface IAccessSecurityCache
|
||||
{
|
||||
bool IsConfigured { get; }
|
||||
Task<AccessSecurityCacheState?> GetAsync(AccessSecurityCacheLookup lookup, CancellationToken cancellationToken = default);
|
||||
Task SetAsync(AccessSecurityCacheState state, CancellationToken cancellationToken = default);
|
||||
Task InvalidateSessionAsync(Guid sessionId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateUserAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateTenantAsync(Guid tenantId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateMembershipAsync(Guid tenantId, Guid userId, CancellationToken cancellationToken = default);
|
||||
Task SetAuthorizationVersionAsync(AuthRealm realm, Guid? tenantId, long version, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IAuthorizationSnapshotCache
|
||||
{
|
||||
Task<CachedAuthorizationSnapshot?> GetAsync(AuthRealm realm, Guid? tenantId, Guid userId, CancellationToken cancellationToken = default);
|
||||
Task SetAsync(AuthRealm realm, Guid? tenantId, Guid userId, CachedAuthorizationSnapshot snapshot, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IAuthorizationStateInvalidator
|
||||
{
|
||||
Task InvalidateSessionAsync(Guid sessionId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateUserAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateTenantAsync(Guid tenantId, CancellationToken cancellationToken = default);
|
||||
Task InvalidateMembershipAsync(Guid tenantId, Guid userId, CancellationToken cancellationToken = default);
|
||||
Task<long> BumpScopeAsync(AuthRealm realm, Guid? tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IAuthorizationCacheInvalidationProcessor
|
||||
{
|
||||
Task<int> ProcessPendingAsync(int batchSize = 100, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IRequestAccessValidator
|
||||
{
|
||||
Task<AuthSessionValidationResult?> ValidateAsync(
|
||||
Guid sessionId,
|
||||
Guid userId,
|
||||
AuthRealm realm,
|
||||
Guid? tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public sealed class AuthorizationSecurityUnavailableException(Exception innerException)
|
||||
: Exception("Authentication security dependencies are unavailable.", innerException);
|
||||
Reference in New Issue
Block a user