265 lines
8.0 KiB
C#
265 lines
8.0 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using System.Security.Claims;
|
|
using line_gestao_api.Dtos;
|
|
using line_gestao_api.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace line_gestao_api.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/profile")]
|
|
[Authorize]
|
|
public class ProfileController : ControllerBase
|
|
{
|
|
private static readonly EmailAddressAttribute EmailValidator = new();
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
|
|
public ProfileController(UserManager<ApplicationUser> userManager)
|
|
{
|
|
_userManager = userManager;
|
|
}
|
|
|
|
[HttpGet("me")]
|
|
public async Task<ActionResult<ProfileMeDto>> GetMe()
|
|
{
|
|
var user = await GetAuthenticatedUserAsync();
|
|
if (user == null)
|
|
{
|
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
|
}
|
|
|
|
return Ok(ToProfileDto(user));
|
|
}
|
|
|
|
[HttpPatch]
|
|
public async Task<ActionResult<ProfileMeDto>> UpdateProfile([FromBody] UpdateProfileRequest req)
|
|
{
|
|
var user = await GetAuthenticatedUserAsync();
|
|
if (user == null)
|
|
{
|
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
|
}
|
|
|
|
var errors = await ValidateProfileUpdateAsync(user.Id, req);
|
|
if (errors.Count > 0)
|
|
{
|
|
return BadRequest(new ValidationErrorResponse { Errors = errors });
|
|
}
|
|
|
|
var nome = req.Nome.Trim();
|
|
var email = req.Email.Trim().ToLowerInvariant();
|
|
|
|
user.Name = nome;
|
|
|
|
if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var setEmailResult = await _userManager.SetEmailAsync(user, email);
|
|
if (!setEmailResult.Succeeded)
|
|
{
|
|
return BadRequest(ToValidationErrorResponse("email", setEmailResult.Errors));
|
|
}
|
|
|
|
var setUserNameResult = await _userManager.SetUserNameAsync(user, email);
|
|
if (!setUserNameResult.Succeeded)
|
|
{
|
|
return BadRequest(ToValidationErrorResponse("email", setUserNameResult.Errors));
|
|
}
|
|
}
|
|
|
|
var updateResult = await _userManager.UpdateAsync(user);
|
|
if (!updateResult.Succeeded)
|
|
{
|
|
return BadRequest(ToValidationErrorResponse("perfil", updateResult.Errors));
|
|
}
|
|
|
|
return Ok(ToProfileDto(user));
|
|
}
|
|
|
|
[HttpPost("change-password")]
|
|
public async Task<IActionResult> ChangePassword([FromBody] ChangeMyPasswordRequest req)
|
|
{
|
|
var errors = ValidatePasswordChange(req);
|
|
if (errors.Count > 0)
|
|
{
|
|
return BadRequest(new ValidationErrorResponse { Errors = errors });
|
|
}
|
|
|
|
var user = await GetAuthenticatedUserAsync();
|
|
if (user == null)
|
|
{
|
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
|
}
|
|
|
|
var result = await _userManager.ChangePasswordAsync(user, req.CredencialAtual, req.NovaCredencial);
|
|
if (!result.Succeeded)
|
|
{
|
|
return BadRequest(MapPasswordChangeErrors(result.Errors));
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
private async Task<ApplicationUser?> GetAuthenticatedUserAsync()
|
|
{
|
|
var userIdRaw = User.FindFirstValue(ClaimTypes.NameIdentifier)
|
|
?? User.FindFirstValue("sub");
|
|
|
|
if (!Guid.TryParse(userIdRaw, out var userId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return await _userManager.Users
|
|
.FirstOrDefaultAsync(u => u.Id == userId && u.IsActive);
|
|
}
|
|
|
|
private async Task<List<ValidationErrorDto>> ValidateProfileUpdateAsync(Guid userId, UpdateProfileRequest req)
|
|
{
|
|
var errors = new List<ValidationErrorDto>();
|
|
|
|
if (string.IsNullOrWhiteSpace(req.Nome) || req.Nome.Trim().Length < 2)
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "nome",
|
|
Message = "Nome é obrigatório e deve ter pelo menos 2 caracteres."
|
|
});
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.Email))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "email",
|
|
Message = "Email é obrigatório."
|
|
});
|
|
}
|
|
else
|
|
{
|
|
var email = req.Email.Trim().ToLowerInvariant();
|
|
if (!EmailValidator.IsValid(email))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "email",
|
|
Message = "Email inválido."
|
|
});
|
|
}
|
|
else
|
|
{
|
|
var normalized = _userManager.NormalizeEmail(email);
|
|
var exists = await _userManager.Users
|
|
.AnyAsync(u => u.Id != userId && u.NormalizedEmail == normalized);
|
|
|
|
if (exists)
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "email",
|
|
Message = "E-mail já cadastrado."
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private static List<ValidationErrorDto> ValidatePasswordChange(ChangeMyPasswordRequest req)
|
|
{
|
|
var errors = new List<ValidationErrorDto>();
|
|
|
|
if (string.IsNullOrWhiteSpace(req.CredencialAtual))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "credencialAtual",
|
|
Message = "Credencial atual é obrigatória."
|
|
});
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.NovaCredencial))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "novaCredencial",
|
|
Message = "Nova credencial é obrigatória."
|
|
});
|
|
}
|
|
else if (req.NovaCredencial.Length < 8)
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "novaCredencial",
|
|
Message = "Nova credencial deve ter pelo menos 8 caracteres."
|
|
});
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.ConfirmarNovaCredencial))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "confirmarNovaCredencial",
|
|
Message = "Confirmação da nova credencial é obrigatória."
|
|
});
|
|
}
|
|
else if (!string.Equals(req.NovaCredencial, req.ConfirmarNovaCredencial, StringComparison.Ordinal))
|
|
{
|
|
errors.Add(new ValidationErrorDto
|
|
{
|
|
Field = "confirmarNovaCredencial",
|
|
Message = "Confirmação da nova credencial inválida."
|
|
});
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private static ValidationErrorResponse ToValidationErrorResponse(string field, IEnumerable<IdentityError> identityErrors)
|
|
{
|
|
return new ValidationErrorResponse
|
|
{
|
|
Errors = identityErrors.Select(e => new ValidationErrorDto
|
|
{
|
|
Field = field,
|
|
Message = e.Description
|
|
}).ToList()
|
|
};
|
|
}
|
|
|
|
private static ValidationErrorResponse MapPasswordChangeErrors(IEnumerable<IdentityError> identityErrors)
|
|
{
|
|
var errors = identityErrors.Select(e =>
|
|
{
|
|
var field = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase)
|
|
? "credencialAtual"
|
|
: "novaCredencial";
|
|
|
|
var message = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase)
|
|
? "Credencial atual inválida."
|
|
: e.Description;
|
|
|
|
return new ValidationErrorDto
|
|
{
|
|
Field = field,
|
|
Message = message
|
|
};
|
|
}).ToList();
|
|
|
|
return new ValidationErrorResponse { Errors = errors };
|
|
}
|
|
|
|
private static ProfileMeDto ToProfileDto(ApplicationUser user)
|
|
{
|
|
return new ProfileMeDto
|
|
{
|
|
Id = user.Id,
|
|
Nome = user.Name,
|
|
Email = user.Email ?? string.Empty
|
|
};
|
|
}
|
|
}
|