using System.Text.Json; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Tiku.Application.Auth; using Tiku.Domain.Identity; using Tiku.Domain.Operations; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Auth; internal sealed class AuthAdministrationService( TikuDbContext dbContext, UserManager userManager, IAuthSessionStore sessionStore) : IAuthAdministrationService { public async Task ResetPasswordAsync( AdministrativePasswordResetRequest request, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(request.Reason)) throw new InvalidCredentialsException("password_reset_reason_required"); var permitted = request.TenantId is { } tenantId ? await dbContext.TenantMemberships.AnyAsync( item => item.TenantId == tenantId && item.UserId == request.TargetUserId && item.Status == MembershipStatus.Active, cancellationToken) : await dbContext.PlatformBackendUserRoles.AnyAsync( item => item.UserId == request.TargetUserId, cancellationToken); if (!permitted) throw new AuthSessionNotFoundException(); var user = await userManager.FindByIdAsync(request.TargetUserId.ToString()) ?? throw new AuthSessionNotFoundException(); var token = await userManager.GeneratePasswordResetTokenAsync(user); var reset = await userManager.ResetPasswordAsync(user, token, request.TemporaryPassword); if (!reset.Succeeded) throw new InvalidCredentialsException("invalid_new_password"); user.ForcePasswordChange = true; var updated = await userManager.UpdateAsync(user); if (!updated.Succeeded) throw new InvalidOperationException("Unable to require a password change after the administrative reset."); await sessionStore.RevokeAllAsync(user.Id, "administrative_password_reset", cancellationToken); dbContext.AuditLogs.Add(new AuditLog { TenantId = request.TenantId, ActorUserId = request.ActorUserId, Action = "auth.password.reset_by_administrator", TargetType = "user", TargetId = request.TargetUserId.ToString(), Details = JsonSerializer.SerializeToElement(new { request.Reason, ForcePasswordChange = true }) }); await dbContext.SaveChangesAsync(cancellationToken); } }