The ASP.NET Core solution (mws.api/mws.application/mws.domain/ mws.infrastructure) existed only as untracked working files. Adding it to version control, plus a .gitignore for build output and local tooling directories, so the CQRS/mediator refactor plan has a real git history to branch and diff against.
117 lines
4.2 KiB
C#
117 lines
4.2 KiB
C#
using AutoMapper;
|
|
using Mws.Application.Auth;
|
|
using Mws.Application.Common;
|
|
using Mws.Application.Permissions;
|
|
using Mws.Domain.Users;
|
|
|
|
namespace Mws.Application.Accounts;
|
|
|
|
public class AccountService(IUnitOfWork uow, IPasswordHasher passwordHasher, IPermissionService permissions, IMapper mapper) : IAccountService
|
|
{
|
|
private const string Screen = "accounts";
|
|
|
|
public async Task<List<AccountDto>> GetAccountsAsync(Guid actorUserId, string? term, CancellationToken ct = default)
|
|
{
|
|
await permissions.EnsureAsync(actorUserId, Screen, PermissionAction.View, ct);
|
|
|
|
var users = await uow.Users.SearchWithRoleAsync(term, null, ct);
|
|
return mapper.Map<List<AccountDto>>(users);
|
|
}
|
|
|
|
public async Task<AccountDto> CreateAccountAsync(Guid actorUserId, CreateAccountRequest request, CancellationToken ct = default)
|
|
{
|
|
await permissions.EnsureAsync(actorUserId, Screen, PermissionAction.Create, ct);
|
|
|
|
var username = request.Username.Trim();
|
|
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(request.Password))
|
|
{
|
|
throw new BadRequestException("Username and password are required");
|
|
}
|
|
|
|
if (await uow.Users.ExistsByUsernameAsync(username, ct))
|
|
{
|
|
throw new BadRequestException("Username already exists");
|
|
}
|
|
|
|
var role = await uow.Roles.GetByIdAsync(request.RoleId, ct)
|
|
?? throw new BadRequestException("Role not found");
|
|
|
|
var now = DateTime.UtcNow;
|
|
var user = new User
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Username = username,
|
|
PasswordHash = passwordHasher.Hash(request.Password),
|
|
DisplayName = request.DisplayName.Trim(),
|
|
RoleId = role.Id,
|
|
IsActive = true,
|
|
CreatedAt = now,
|
|
UpdatedAt = now,
|
|
};
|
|
|
|
uow.Users.Add(user);
|
|
await uow.SaveChangesAsync(ct);
|
|
user.Role = role;
|
|
return mapper.Map<AccountDto>(user);
|
|
}
|
|
|
|
public async Task<AccountDto> UpdateAccountAsync(Guid actorUserId, Guid id, UpdateAccountRequest request, CancellationToken ct = default)
|
|
{
|
|
await permissions.EnsureAsync(actorUserId, Screen, PermissionAction.Edit, ct);
|
|
|
|
var user = await uow.Users.GetByIdWithRoleAsync(id, ct)
|
|
?? throw new NotFoundException("Account not found");
|
|
|
|
var role = await uow.Roles.GetByIdAsync(request.RoleId, ct)
|
|
?? throw new BadRequestException("Role not found");
|
|
|
|
user.DisplayName = request.DisplayName.Trim();
|
|
user.RoleId = role.Id;
|
|
user.Role = role;
|
|
user.IsActive = request.IsActive;
|
|
user.UpdatedAt = DateTime.UtcNow;
|
|
|
|
await uow.SaveChangesAsync(ct);
|
|
return mapper.Map<AccountDto>(user);
|
|
}
|
|
|
|
public async Task DeleteAccountAsync(Guid actorUserId, Guid id, CancellationToken ct = default)
|
|
{
|
|
await permissions.EnsureAsync(actorUserId, Screen, PermissionAction.Delete, ct);
|
|
|
|
var user = await uow.Users.GetByIdAsync(id, ct)
|
|
?? throw new NotFoundException("Account not found");
|
|
|
|
var soleOwnerProjectIds = await uow.Projects.GetOwnedProjectIdsAsync(id, ct);
|
|
|
|
foreach (var projectId in soleOwnerProjectIds)
|
|
{
|
|
var ownerCount = await uow.Projects.CountOwnersAsync(projectId, ct);
|
|
if (ownerCount <= 1)
|
|
{
|
|
throw new BadRequestException("Cannot delete an account that is the sole owner of a project");
|
|
}
|
|
}
|
|
|
|
uow.Users.Remove(user);
|
|
await uow.SaveChangesAsync(ct);
|
|
}
|
|
|
|
public async Task ResetPasswordAsync(Guid actorUserId, Guid id, ResetPasswordRequest request, CancellationToken ct = default)
|
|
{
|
|
await permissions.EnsureAsync(actorUserId, Screen, PermissionAction.Edit, ct);
|
|
|
|
if (string.IsNullOrWhiteSpace(request.NewPassword))
|
|
{
|
|
throw new BadRequestException("New password is required");
|
|
}
|
|
|
|
var user = await uow.Users.GetByIdAsync(id, ct)
|
|
?? throw new NotFoundException("Account not found");
|
|
|
|
user.PasswordHash = passwordHasher.Hash(request.NewPassword);
|
|
user.UpdatedAt = DateTime.UtcNow;
|
|
await uow.SaveChangesAsync(ct);
|
|
}
|
|
}
|