using DodoSSH.Domain; using Microsoft.AspNetCore.Authorization; namespace DodoSSH.Api.Authorization; /// Requires the caller's account to be . /// /// /// is documented as "sign-in blocked", and nothing blocked it: the identity /// provider still issues tokens for the person, every endpoint resolved them through the current-user context, /// and that context looked an account up by issuer, subject and deletion alone. A suspended account could pull /// and push vaults, take relay tickets and hold the events socket open. This is the check, on both policies, so /// that no endpoint can be reached without it. /// /// /// A connection already open when the account is suspended — a relay tunnel, the events socket — is not /// reached by this; it ends on its own idle and lifetime limits. /// /// internal sealed class ActiveAccountRequirement : IAuthorizationRequirement; /// Satisfies from the account row. internal sealed class ActiveAccountHandler(ICurrentUserContext currentUser, IHttpContextAccessor accessor) : AuthorizationHandler { /// protected override async Task HandleRequirementAsync( AuthorizationHandlerContext context, ActiveAccountRequirement requirement) { ArgumentNullException.ThrowIfNull(context); // The policy's authentication requirement already refuses an anonymous caller with a 401; failing this // as well would turn that into a 403. if (context.User.Identity?.IsAuthenticated != true) { return; } var cancellationToken = accessor.HttpContext?.RequestAborted ?? CancellationToken.None; var user = await currentUser.GetOrProvisionAsync(cancellationToken).ConfigureAwait(false); if (user.Status == UserStatus.Active) { context.Succeed(requirement); } } }