Merge 0d1305bdbe into 9ff61cf937
This commit is contained in:
commit
2d49c6b8fb
|
|
@ -72,16 +72,34 @@ public class AuthController : ControllerBase
|
|||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login(LoginRequest req)
|
||||
{
|
||||
// ✅ normaliza e evita null
|
||||
var email = (req.Email ?? "").Trim().ToLowerInvariant();
|
||||
var password = req.Password ?? "";
|
||||
var normalizedEmail = _userManager.NormalizeEmail(email);
|
||||
|
||||
// ✅ SOLUÇÃO A: ignora filtros globais (multi-tenant / HasQueryFilter)
|
||||
// e pega 1 usuário (pra você logar logo).
|
||||
var user = await _userManager.Users
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail);
|
||||
var tenantId = ResolveTenantId(req);
|
||||
ApplicationUser? user;
|
||||
|
||||
if (tenantId == null)
|
||||
{
|
||||
var users = await _userManager.Users
|
||||
.IgnoreQueryFilters()
|
||||
.Where(u => u.NormalizedEmail == normalizedEmail)
|
||||
.ToListAsync();
|
||||
|
||||
if (users.Count == 0)
|
||||
return Unauthorized("Credenciais inválidas.");
|
||||
|
||||
if (users.Count > 1)
|
||||
return BadRequest("Informe o tenant para realizar o login.");
|
||||
|
||||
user = users[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
user = await _userManager.Users
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail && u.TenantId == tenantId);
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
return Unauthorized("Credenciais inválidas.");
|
||||
|
|
@ -97,6 +115,15 @@ public class AuthController : ControllerBase
|
|||
return Ok(new AuthResponse(token));
|
||||
}
|
||||
|
||||
private Guid? ResolveTenantId(LoginRequest req)
|
||||
{
|
||||
if (req.TenantId.HasValue)
|
||||
return req.TenantId.Value;
|
||||
|
||||
var headerValue = Request.Headers["X-Tenant-Id"].FirstOrDefault();
|
||||
return Guid.TryParse(headerValue, out var parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
private async Task<string> GenerateJwtAsync(ApplicationUser user)
|
||||
{
|
||||
var key = _config["Jwt:Key"]!;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ namespace line_gestao_api.Controllers;
|
|||
[Authorize]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private static readonly HashSet<string> AllowedRoles = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"admin",
|
||||
"gestor"
|
||||
};
|
||||
|
||||
private readonly AppDbContext _db;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly RoleManager<IdentityRole<Guid>> _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<IActionResult> 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<List<ValidationErrorDto>> ValidateUpdateAsync(Guid userId, UserUpdateRequest req)
|
||||
{
|
||||
var errors = new List<ValidationErrorDto>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
namespace line_gestao_api.Dtos;
|
||||
|
||||
public record LoginRequest(string Email, string Password);
|
||||
public record LoginRequest(string Email, string Password, Guid? TenantId = null);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,16 @@ public class TenantMiddleware
|
|||
Guid? tenantId = null;
|
||||
var claim = context.User.FindFirst("tenantId")?.Value
|
||||
?? context.User.FindFirst("tenant")?.Value;
|
||||
var headerValue = context.Request.Headers["X-Tenant-Id"].FirstOrDefault();
|
||||
|
||||
if (Guid.TryParse(claim, out var parsed))
|
||||
{
|
||||
tenantId = parsed;
|
||||
}
|
||||
else if (Guid.TryParse(headerValue, out var headerTenant))
|
||||
{
|
||||
tenantId = headerTenant;
|
||||
}
|
||||
|
||||
tenantProvider.SetTenantId(tenantId);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue