Files
TeleWave/backend/src/TeleWave.Infrastructure/Identity/IdentityService.cs
T
Leonid Pershin 8a3eebc48f Initial commit: base slice (auth, roles, users, admin) scaffold
Backend: .NET 10 Clean Architecture + LiteCqrs.Net + EF Core/PostgreSQL +
Identity/JWT. Frontend: React 19 + Vite + TanStack Query/Router + Tailwind v4
with a retro CRT theme. Docker/compose deployment mirroring PnvPanel's
conventions, scoped down to the current base feature set.
2026-07-24 05:40:34 +03:00

226 lines
7.6 KiB
C#

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Admin.Users;
using TeleWave.Application.Auth;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Infrastructure.Persistence;
namespace TeleWave.Infrastructure.Identity;
internal sealed class IdentityService(
UserManager<AppUser> userManager,
SignInManager<AppUser> signInManager,
RoleManager<AppRole> roleManager,
AppDbContext dbContext
) : IIdentityService
{
public async Task<Result<Guid>> CreateUserAsync(
string userName,
string password,
CancellationToken cancellationToken
)
{
if (await userManager.FindByNameAsync(userName) is not null)
return Result.Failure<Guid>(AuthErrors.UserNameTaken);
var user = new AppUser { UserName = userName, CreatedAt = DateTimeOffset.UtcNow };
var createResult = await userManager.CreateAsync(user, password);
if (!createResult.Succeeded)
{
return Result.Failure<Guid>(
Error.Validation(
"Auth.CreateFailed",
string.Join("; ", createResult.Errors.Select(e => e.Description))
)
);
}
await userManager.AddToRoleAsync(user, RoleNames.User);
return Result.Success(user.Id);
}
public async Task<Result<AuthenticatedUser>> ValidateCredentialsAsync(
string userName,
string password,
CancellationToken cancellationToken
)
{
var user = await userManager.FindByNameAsync(userName);
if (user is null)
return Result.Failure<AuthenticatedUser>(AuthErrors.InvalidCredentials);
var checkResult = await signInManager.CheckPasswordSignInAsync(
user,
password,
lockoutOnFailure: true
);
if (!checkResult.Succeeded)
return Result.Failure<AuthenticatedUser>(AuthErrors.InvalidCredentials);
var role = await GetPrimaryRoleAsync(user);
return Result.Success(new AuthenticatedUser(user.Id, user.UserName!, role));
}
public async Task<CurrentUserProfile?> GetProfileAsync(
Guid userId,
CancellationToken cancellationToken
)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return null;
var role = await GetPrimaryRoleAsync(user);
var roleEntity = await roleManager.FindByNameAsync(role);
return new CurrentUserProfile(user.Id, user.UserName!, roleEntity?.Id ?? Guid.Empty, role, user.IsBlocked);
}
public async Task<Result> ChangePasswordAsync(
Guid userId,
string currentPassword,
string newPassword,
CancellationToken cancellationToken
)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return Result.Failure(AuthErrors.Unauthorized);
var result = await userManager.ChangePasswordAsync(user, currentPassword, newPassword);
return result.Succeeded
? Result.Success()
: Result.Failure(
Error.Validation(
"Auth.ChangePasswordFailed",
string.Join("; ", result.Errors.Select(e => e.Description))
)
);
}
public async Task<Result> ChangeUserNameAsync(
Guid userId,
string newUserName,
CancellationToken cancellationToken
)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return Result.Failure(AuthErrors.Unauthorized);
var existing = await userManager.FindByNameAsync(newUserName);
if (existing is not null && existing.Id != userId)
return Result.Failure(AuthErrors.UserNameTaken);
var result = await userManager.SetUserNameAsync(user, newUserName);
return result.Succeeded
? Result.Success()
: Result.Failure(
Error.Validation(
"Auth.ChangeUserNameFailed",
string.Join("; ", result.Errors.Select(e => e.Description))
)
);
}
public async Task<Result> DeleteUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return Result.Failure(UserErrors.NotFound);
var result = await userManager.DeleteAsync(user);
return result.Succeeded
? Result.Success()
: Result.Failure(
Error.Failure(
"Users.DeleteFailed",
string.Join("; ", result.Errors.Select(e => e.Description))
)
);
}
public async Task<Result> BlockUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return Result.Failure(UserErrors.NotFound);
user.IsBlocked = true;
await userManager.UpdateAsync(user);
return Result.Success();
}
public async Task<Result> UnblockUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return Result.Failure(UserErrors.NotFound);
user.IsBlocked = false;
await userManager.UpdateAsync(user);
return Result.Success();
}
public async Task<PagedList<UserSummaryDto>> ListUsersAsync(
int page,
int pageSize,
string? search,
Guid? roleId,
bool? isBlocked,
CancellationToken cancellationToken
)
{
var query =
from user in dbContext.Users
join userRole in dbContext.UserRoles on user.Id equals userRole.UserId into userRoles
from userRole in userRoles.DefaultIfEmpty()
join role in dbContext.Roles on userRole.RoleId equals role.Id into roles
from role in roles.DefaultIfEmpty()
select new { user, RoleId = (Guid?)userRole.RoleId, RoleName = role != null ? role.Name : null };
if (!string.IsNullOrWhiteSpace(search))
query = query.Where(x => x.user.UserName!.Contains(search));
if (roleId is not null)
query = query.Where(x => x.RoleId == roleId);
if (isBlocked is not null)
query = query.Where(x => x.user.IsBlocked == isBlocked);
var total = await query.CountAsync(cancellationToken);
var items = await query
.OrderByDescending(x => x.user.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new UserSummaryDto(
x.user.Id,
x.user.UserName!,
x.RoleName ?? RoleNames.User,
x.user.IsBlocked,
x.user.CreatedAt
))
.ToListAsync(cancellationToken);
return new PagedList<UserSummaryDto>(items, total, page, pageSize);
}
public async Task<UserSummaryDto?> GetUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await userManager.FindByIdAsync(userId.ToString());
if (user is null)
return null;
var role = await GetPrimaryRoleAsync(user);
return new UserSummaryDto(user.Id, user.UserName!, role, user.IsBlocked, user.CreatedAt);
}
private async Task<string> GetPrimaryRoleAsync(AppUser user)
{
var roles = await userManager.GetRolesAsync(user);
return roles.FirstOrDefault() ?? RoleNames.User;
}
}