Почистить комментарии от упоминаний процесса
Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками на процесс удалены; то же в .proto. Правила обновлены в docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
@@ -4,36 +4,20 @@ using Deal.Modules.Tenants.Application.Models;
|
||||
namespace Deal.Modules.Tenants.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Прикладной сервис аутентификации: login, logout, смена пароля, разрешение сессии, impersonation.
|
||||
/// Прикладной сервис аутентификации
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Семантика повторяет прототип LeadRadar (<c>backend/app/auth.py</c>): сообщения об ошибках
|
||||
/// фиксирует HTTP-слой (Task 4/7), сервис возвращает коды/null и не бросает исключений
|
||||
/// для бизнес-отказов. Логин нормализуется в нижний регистр (Ruling: сравнение по lowercase).
|
||||
/// <para>
|
||||
/// Гейт приостановки (Task 7/Ruling 10(5); этап 12, пакет B): login проверяет статус тенанта через
|
||||
/// <see cref="ITenantRepository"/> — suspended возвращает <see cref="LoginResultDto.ErrorTenantSuspended"/>
|
||||
/// (HTTP-слой отвечает 403 и пишет tenant_login_failed с tenantId); <see cref="ResolveSessionAsync"/>
|
||||
/// проверяет статус на каждом запросе — активные сессии suspended-тенанта перестают действовать немедленно
|
||||
/// (включая impersonation), при resume — вновь работают. Impersonation выпускает
|
||||
/// обычную tenant-сессию выбранного пользователя с маркером оператора (<see cref="SessionDto.ImpersonatedByOperatorId"/>);
|
||||
/// завершение — logout'ом пользователя, о нём сервис сообщает <see cref="LogoutResultDto"/> для аудита
|
||||
/// impersonation_stopped. Пароль при impersonation не меняется.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class AuthService(
|
||||
IAuthStore authStore,
|
||||
IPasswordHasher passwordHasher,
|
||||
ITenantRepository tenantRepository)
|
||||
{
|
||||
/// <summary>
|
||||
/// Срок жизни сессии, дней (Ruling 6: 30). Единый источник «30» — на него ссылается кука (Task 5).
|
||||
/// Срок жизни сессии, дней.
|
||||
/// </summary>
|
||||
public const int SessionLifetimeDays = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Минимальная длина нового пароля. Единый источник — на него ссылается активация инвайта
|
||||
/// (JoinService, Task 6), чтобы минимум не разошёлся.
|
||||
/// Минимальная длина нового пароля.
|
||||
/// </summary>
|
||||
public const int MinNewPasswordLength = 8;
|
||||
|
||||
@@ -42,16 +26,11 @@ public sealed class AuthService(
|
||||
private const string UserActiveStatus = "active";
|
||||
|
||||
/// <summary>
|
||||
/// Вход: при успехе создаёт сессию и возвращает её raw-токен. Вход suspended-тенанта заблокирован (Ruling 10(5)).
|
||||
/// Вход: при успехе создаёт сессию и возвращает её raw-токен.
|
||||
/// </summary>
|
||||
/// <param name="login">Логин (регистр и пробелы не важны — нормализуется).</param>
|
||||
/// <param name="password">Пароль в открытом виде.</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <returns>
|
||||
/// При успехе — Login и Token (UserId/TenantId для аудита). Иначе Login/Token null: Error может отличать
|
||||
/// заблокированный вход приостановленного тенанта (<see cref="LoginResultDto.ErrorTenantSuspended"/>,
|
||||
/// UserId/TenantId заполнены) от «неверные учётные данные» (Error null, UserId/TenantId пусты).
|
||||
/// </returns>
|
||||
/// <returns>При успехе — Login и Token (UserId/TenantId для аудита). Иначе Login/Token null: Error может отличать заблокированный вход приостановленного тенанта (<see cref="LoginResultDto.ErrorTenantSuspended"/>, UserId/TenantId заполнены) от «неверные учётные данные» (Error null, UserId/TenantId пусты).</returns>
|
||||
public async Task<LoginResultDto> LoginAsync(
|
||||
string login,
|
||||
string password,
|
||||
@@ -74,7 +53,6 @@ public sealed class AuthService(
|
||||
var tenant = await tenantRepository.FindByIdAsync(user.TenantId, ct);
|
||||
if (tenant is not null && tenant.Status == TenantStatuses.Suspended)
|
||||
{
|
||||
// Заблокированный вход: HTTP-слой пишет tenant_login_failed с tenantId (замечание ревью Task 4).
|
||||
return new LoginResultDto(
|
||||
Login: null,
|
||||
Token: null,
|
||||
@@ -88,14 +66,10 @@ public sealed class AuthService(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выход: удаляет сессию по raw-токену (no-op без токена).
|
||||
/// Выход: удаляет сессию по raw-токену
|
||||
/// </summary>
|
||||
/// <param name="rawToken">Raw-токен из куки (может отсутствовать — no-op).</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <returns>
|
||||
/// Не null, если удалённая сессия была impersonation — сведения для аудита impersonation_stopped
|
||||
/// (актор — оператор по маркеру сессии). Обычный logout и no-op возвращают null.
|
||||
/// </returns>
|
||||
/// <returns>Не null, если удалённая сессия была impersonation — сведения для аудита impersonation_stopped (актор — оператор по маркеру сессии). Обычный logout и no-op возвращают null.</returns>
|
||||
public async Task<LogoutResultDto?> LogoutAsync(string? rawToken, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawToken))
|
||||
@@ -130,11 +104,7 @@ public sealed class AuthService(
|
||||
/// <param name="login">Логин пользователя.</param>
|
||||
/// <param name="oldPassword">Текущий пароль.</param>
|
||||
/// <param name="NewPassword">Новый пароль (минимум 8 символов).</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <returns>
|
||||
/// При успехе — Ok=true и NewToken (raw-токен свежей сессии). Иначе Ok=false и код ошибки:
|
||||
/// <see cref="ChangePasswordResultDto.ErrorOldPassword"/> или <see cref="ChangePasswordResultDto.ErrorTooShort"/>.
|
||||
/// </returns>
|
||||
/// <returns>При успехе — Ok=true и NewToken (raw-токен свежей сессии). Иначе Ok=false и код ошибки: <see cref="ChangePasswordResultDto.ErrorOldPassword"/> или <see cref="ChangePasswordResultDto.ErrorTooShort"/>.</returns>
|
||||
public async Task<ChangePasswordResultDto> ChangePasswordAsync(
|
||||
string login,
|
||||
string oldPassword,
|
||||
@@ -153,7 +123,6 @@ public sealed class AuthService(
|
||||
return new ChangePasswordResultDto(Ok: false, Error: ChangePasswordResultDto.ErrorTooShort, NewToken: null);
|
||||
}
|
||||
|
||||
// Семантика прототипа (change_password + auth_routes.change): разлогиниваем все старые
|
||||
// сессии, обновляем хэш и выдаём свежую — её raw-токен вернёт HTTP-слой в куке.
|
||||
await authStore.DeleteSessionsByUserIdAsync(user.Id, ct);
|
||||
await authStore.UpdatePasswordHashAsync(user.Id, passwordHasher.Hash(newPassword), ct);
|
||||
@@ -162,19 +131,11 @@ public sealed class AuthService(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Impersonation: tenant-сессия целевого пользователя от имени оператора (план Task 7).
|
||||
/// Impersonation: tenant-сессия целевого пользователя от имени оператора.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Пароль пользователя не меняется и не требуется: сессия выпускается оператором напрямую с маркером
|
||||
/// <see cref="SessionDto.ImpersonatedByOperatorId"/> (для аудита stopped при logout). login опционален:
|
||||
/// не задан — берётся первый пользователь тенанта (по CreatedAt); задан — пользователь обязан
|
||||
/// принадлежать тенанту. Приостановленный тенант не блокирует impersonation (операторский доступ,
|
||||
/// аудируется; ИИ-расход всё равно заморожен гейтом Task 9).
|
||||
/// </remarks>
|
||||
/// <param name="tenantId">Идентификатор тенанта.</param>
|
||||
/// <param name="targetLogin">Логин пользователя (null/пустой — первый пользователь тенанта).</param>
|
||||
/// <param name="operatorId">Идентификатор оператора, начинающего impersonation (маркер сессии).</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <returns>Результат: Ok=true — SessionToken (raw-токен для куки deal_session) и срок жизни; иначе код ошибки.</returns>
|
||||
public async Task<ImpersonationResultDto> ImpersonateAsync(
|
||||
Guid tenantId,
|
||||
@@ -215,10 +176,9 @@ public sealed class AuthService(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Разрешение сессии по raw-токену: возвращает пользователя или null (нет/протухла).
|
||||
/// Разрешение сессии по raw-токену
|
||||
/// </summary>
|
||||
/// <param name="rawToken">Raw-токен из куки.</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <returns>Идентичность пользователя или null.</returns>
|
||||
public async Task<UserIdentityDto?> ResolveSessionAsync(string? rawToken, CancellationToken ct)
|
||||
{
|
||||
@@ -242,9 +202,7 @@ public sealed class AuthService(
|
||||
|
||||
if (user is not null)
|
||||
{
|
||||
// Приостановка тенанта действует немедленно (этап 12, пакет B): статус проверяется на
|
||||
// каждом разрешении сессии, поэтому активные сессии suspended-тенанта перестают работать
|
||||
// сразу после suspend, а не доживают до expiry (ранее — Ruling 10(5)). Флаг состояния не
|
||||
// храним: при resume доступ возвращается тем же путём (login не блокирует активные сессии).
|
||||
// Касается и impersonation-сессий (та же tenant-сессия с маркером оператора).
|
||||
var tenant = await tenantRepository.FindByIdAsync(user.TenantId, ct);
|
||||
|
||||
Reference in New Issue
Block a user