From 0d1305bdbeb306c61c7bdf29a751dd6782a6c30b Mon Sep 17 00:00:00 2001 From: Eduardo Lopes <155753879+eduardolopesx03@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:46:32 -0300 Subject: [PATCH] Add admin-managed user editing and role validation --- Controllers/UsersController.cs | 106 ++++++++++++++++++++++++++++++++- Dtos/UserDtos.cs | 4 ++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/Controllers/UsersController.cs b/Controllers/UsersController.cs index ee908e2..3af3ce0 100644 --- a/Controllers/UsersController.cs +++ b/Controllers/UsersController.cs @@ -14,6 +14,12 @@ namespace line_gestao_api.Controllers; [Authorize] public class UsersController : ControllerBase { + private static readonly HashSet AllowedRoles = new(StringComparer.OrdinalIgnoreCase) + { + "admin", + "gestor" + }; + private readonly AppDbContext _db; private readonly UserManager _userManager; private readonly RoleManager> _roleManager; @@ -65,7 +71,7 @@ public class UsersController : ControllerBase } var role = req.Permissao.Trim().ToLowerInvariant(); - if (!await _roleManager.RoleExistsAsync(role)) + if (!AllowedRoles.Contains(role) || !await _roleManager.RoleExistsAsync(role)) { return BadRequest(new ValidationErrorResponse { @@ -211,12 +217,33 @@ public class UsersController : ControllerBase [Authorize(Roles = "admin")] public async Task Update(Guid id, [FromBody] UserUpdateRequest req) { + var errors = await ValidateUpdateAsync(id, req); + if (errors.Count > 0) + { + return BadRequest(new ValidationErrorResponse { Errors = errors }); + } + var user = await _userManager.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return NotFound(); } + if (!string.IsNullOrWhiteSpace(req.Nome)) + { + user.Name = req.Nome.Trim(); + } + + if (!string.IsNullOrWhiteSpace(req.Email)) + { + var email = req.Email.Trim().ToLowerInvariant(); + if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase)) + { + await _userManager.SetEmailAsync(user, email); + await _userManager.SetUserNameAsync(user, email); + } + } + if (req.Ativo.HasValue) { user.IsActive = req.Ativo.Value; @@ -225,7 +252,7 @@ public class UsersController : ControllerBase if (!string.IsNullOrWhiteSpace(req.Permissao)) { var roleName = req.Permissao.Trim().ToLowerInvariant(); - if (!await _roleManager.RoleExistsAsync(roleName)) + if (!AllowedRoles.Contains(roleName) || !await _roleManager.RoleExistsAsync(roleName)) { return BadRequest(new ValidationErrorResponse { @@ -245,6 +272,23 @@ public class UsersController : ControllerBase await _userManager.AddToRoleAsync(user, roleName); } + if (!string.IsNullOrWhiteSpace(req.Senha)) + { + var token = await _userManager.GeneratePasswordResetTokenAsync(user); + var resetResult = await _userManager.ResetPasswordAsync(user, token, req.Senha); + if (!resetResult.Succeeded) + { + return BadRequest(new ValidationErrorResponse + { + Errors = resetResult.Errors.Select(e => new ValidationErrorDto + { + Field = "senha", + Message = e.Description + }).ToList() + }); + } + } + await _userManager.UpdateAsync(user); return NoContent(); } @@ -277,6 +321,64 @@ public class UsersController : ControllerBase { errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão é obrigatória." }); } + else if (!AllowedRoles.Contains(req.Permissao.Trim())) + { + errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão inválida." }); + } + + return errors; + } + + private async Task> ValidateUpdateAsync(Guid userId, UserUpdateRequest req) + { + var errors = new List(); + + if (!string.IsNullOrWhiteSpace(req.Nome) && req.Nome.Trim().Length < 2) + { + errors.Add(new ValidationErrorDto { Field = "nome", Message = "Nome inválido." }); + } + + if (!string.IsNullOrWhiteSpace(req.Email)) + { + var email = req.Email.Trim().ToLowerInvariant(); + var normalized = _userManager.NormalizeEmail(email); + + var tenantId = _tenantProvider.TenantId; + if (tenantId == null) + { + errors.Add(new ValidationErrorDto { Field = "email", Message = "Tenant inválido." }); + } + else + { + var exists = await _userManager.Users.AnyAsync(u => + u.Id != userId && + u.TenantId == tenantId && + u.NormalizedEmail == normalized); + + if (exists) + { + errors.Add(new ValidationErrorDto { Field = "email", Message = "E-mail já cadastrado." }); + } + } + } + + if (!string.IsNullOrWhiteSpace(req.Senha)) + { + if (req.Senha.Length < 6) + { + errors.Add(new ValidationErrorDto { Field = "senha", Message = "Senha deve ter pelo menos 6 caracteres." }); + } + + if (req.Senha != req.ConfirmarSenha) + { + errors.Add(new ValidationErrorDto { Field = "confirmarSenha", Message = "Confirmação de senha inválida." }); + } + } + + if (!string.IsNullOrWhiteSpace(req.Permissao) && !AllowedRoles.Contains(req.Permissao.Trim())) + { + errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão inválida." }); + } return errors; } diff --git a/Dtos/UserDtos.cs b/Dtos/UserDtos.cs index 97edb4d..697fadc 100644 --- a/Dtos/UserDtos.cs +++ b/Dtos/UserDtos.cs @@ -11,6 +11,10 @@ public class UserCreateRequest public class UserUpdateRequest { + public string? Nome { get; set; } + public string? Email { get; set; } + public string? Senha { get; set; } + public string? ConfirmarSenha { get; set; } public string? Permissao { get; set; } public bool? Ativo { get; set; } }