< Summary

Information
Class: FAU.Logica.Services.UsuarioService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/UsuarioService.cs
Line coverage
88%
Covered lines: 111
Uncovered lines: 14
Coverable lines: 125
Total lines: 266
Line coverage: 88.8%
Branch coverage
93%
Covered branches: 28
Total branches: 30
Branch coverage: 93.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetByIdAsync()100%11100%
GetByUsernameWithRolesAsync()100%11100%
GetAllAsync()100%11100%
CreateAsync()75%4485.71%
UpdateAsync()83.33%6678.57%
CambiarEstadoAsync()100%8889.47%
CambiarPasswordAsync()100%4485.71%
AsignarRolAsync()100%6690.47%
RemoverRolAsync()100%4489.47%
DeleteAsync()100%11100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/UsuarioService.cs

#LineLine coverage
 1using FAU.DataAccess.Repositories;
 2using FAU.Entidades;
 3using FAU.Logica.Validadores;
 4
 5namespace FAU.Logica.Services;
 6
 7public class UsuarioService : IUsuarioService
 8{
 9    private readonly IUsuarioRepository _usuarioRepository;
 10    private readonly IRolRepository _rolRepository;
 11    private readonly IAuditoriaService _auditoriaService;
 12    private readonly ICurrentUserContext _currentUser;
 13
 2514    public UsuarioService(IUsuarioRepository usuarioRepository, IRolRepository rolRepository, IAuditoriaService auditori
 15    {
 2516        _usuarioRepository = usuarioRepository;
 2517        _rolRepository = rolRepository;
 2518        _auditoriaService = auditoriaService;
 2519        _currentUser = currentUser;
 2520    }
 21
 22    public async Task<Usuario?> GetByIdAsync(long id)
 23    {
 224        return await _usuarioRepository.GetByIdAsync(id);
 225    }
 26
 27    public async Task<Usuario?> GetByUsernameWithRolesAsync(string username)
 28    {
 129        return await _usuarioRepository.GetByUsernameWithRolesAsync(username);
 130    }
 31
 32    public async Task<IEnumerable<Usuario>> GetAllAsync()
 33    {
 134        return await _usuarioRepository.GetAllAsync();
 135    }
 36
 37    public async Task<(bool Success, Usuario? Usuario, string? Error)> CreateAsync(string username, string password, lon
 38    {
 39        try
 40        {
 241            if (await _usuarioRepository.ExistsAsync(username))
 42            {
 143                return (false, null, "El nombre de usuario ya está en uso");
 44            }
 45
 146            var (isValid, error) = PasswordPolicy.Validate(password);
 147            if (!isValid)
 48            {
 049                return (false, null, error);
 50            }
 51
 152            var passwordHash = BCrypt.Net.BCrypt.HashPassword(password);
 53
 154            var nuevoUsuario = new Usuario
 155            {
 156                Username = username,
 157                PasswordHash = passwordHash,
 158                Estado = "activo",
 159                IntentosFallidos = 0,
 160                PersonaId = personaId,
 161                FechaActualizacion = DateTime.Now
 162            };
 63
 164            var usuarioCreado = await _usuarioRepository.CreateAsync(nuevoUsuario);
 165            await _auditoriaService.LogAuditoriaAsync(_currentUser.UserId ?? 0, AccionEnum.CrearUsuario, ContextoEnum.Us
 166            return (true, usuarioCreado, null);
 67        }
 068        catch (Exception ex)
 69        {
 070            return (false, null, $"Error al crear usuario: {ex.Message}");
 71        }
 272    }
 73
 74    public async Task<(bool Success, Usuario? Usuario, string? Error)> UpdateAsync(long id, string? newPassword = null)
 75    {
 76        try
 77        {
 378            var usuario = await _usuarioRepository.GetByIdAsync(id);
 379            if (usuario == null)
 80            {
 181                return (false, null, "Usuario no encontrado");
 82            }
 83
 284            if (!string.IsNullOrWhiteSpace(newPassword))
 85            {
 186                var (isValid, error) = PasswordPolicy.Validate(newPassword);
 187                if (!isValid)
 88                {
 089                    return (false, null, error);
 90                }
 91
 192                usuario.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
 93            }
 94
 295            var usuarioActualizado = await _usuarioRepository.UpdateAsync(usuario);
 296            await _auditoriaService.LogAuditoriaAsync(_currentUser.UserId ?? 0, AccionEnum.ActualizarUsuario, ContextoEn
 297            return (true, usuarioActualizado, null);
 98        }
 099        catch (Exception ex)
 100        {
 0101            return (false, null, $"Error al actualizar usuario: {ex.Message}");
 102        }
 3103    }
 104
 105    public async Task<(bool Success, string? Error)> CambiarEstadoAsync(long id, string nuevoEstado)
 106    {
 107        try
 108        {
 4109            var usuario = await _usuarioRepository.GetByIdAsync(id);
 4110            if (usuario == null)
 111            {
 1112                return (false, "Usuario no encontrado");
 113            }
 114
 115            // Validar estados permitidos
 3116            var estadosPermitidos = new[] { "Activo", "Bloqueado", "Inactivo" };
 3117            if (!estadosPermitidos.Contains(nuevoEstado, StringComparer.OrdinalIgnoreCase))
 118            {
 1119                return (false, "Estado inválido. Valores permitidos: Activo, Bloqueado, Inactivo");
 120            }
 121
 2122            var estadoAnterior = usuario.Estado;
 2123            usuario.Estado = nuevoEstado;
 2124            usuario.FechaActualizacion = DateTime.Now;
 125
 126            // Cuando se desbloquea un usuario, limpiar bloqueado_hasta e intentos_fallidos
 2127            if (estadoAnterior.Equals("bloqueado", StringComparison.OrdinalIgnoreCase) &&
 2128                nuevoEstado.Equals("activo", StringComparison.OrdinalIgnoreCase))
 129            {
 1130                usuario.BloqueadoHasta = null;
 1131                usuario.IntentosFallidos = 0;
 132            }
 133
 2134            await _usuarioRepository.UpdateAsync(usuario);
 2135            await _auditoriaService.LogAuditoriaAsync(_currentUser.UserId ?? 0, AccionEnum.ActualizarUsuario, ContextoEn
 2136            return (true, null);
 137        }
 0138        catch (Exception ex)
 139        {
 0140            return (false, $"Error al cambiar estado del usuario: {ex.Message}");
 141        }
 4142    }
 143
 144    public async Task<(bool Success, string? Error)> CambiarPasswordAsync(long id, string nuevaPassword)
 145    {
 146        try
 147        {
 4148            var usuario = await _usuarioRepository.GetByIdAsync(id);
 4149            if (usuario == null)
 150            {
 1151                return (false, "Usuario no encontrado");
 152            }
 153
 3154            var (isValid, error) = PasswordPolicy.Validate(nuevaPassword);
 3155            if (!isValid)
 156            {
 2157                return (false, error);
 158            }
 159
 1160            usuario.PasswordHash = BCrypt.Net.BCrypt.HashPassword(nuevaPassword);
 1161            usuario.FechaActualizacion = DateTime.Now;
 162
 1163            await _usuarioRepository.UpdateAsync(usuario);
 1164            await _auditoriaService.LogAuditoriaAsync(_currentUser.UserId ?? 0, AccionEnum.ActualizarUsuario, ContextoEn
 1165            return (true, null);
 166        }
 0167        catch (Exception ex)
 168        {
 0169            return (false, $"Error al cambiar contraseña: {ex.Message}");
 170        }
 4171    }
 172
 173    public async Task<(bool Success, string? Error)> AsignarRolAsync(long usuarioId, long rolId)
 174    {
 175        try
 176        {
 4177            var usuario = await _usuarioRepository.GetByIdAsync(usuarioId);
 4178            if (usuario == null)
 179            {
 1180                return (false, "Usuario no encontrado");
 181            }
 182
 3183            var rol = await _rolRepository.GetByIdAsync(rolId);
 3184            if (rol == null)
 185            {
 1186                return (false, "Rol no encontrado");
 187            }
 188
 189            // Check if role is already assigned (by ID to avoid tracking issues)
 3190            if (usuario.Roles.Any(r => r.Id == rolId))
 191            {
 1192                return (false, "El usuario ya tiene este rol asignado");
 193            }
 194
 195            // Add the role to the collection
 1196            usuario.Roles.Add(rol);
 197
 198            // Update the usuario (this will persist the role assignment)
 1199            await _usuarioRepository.UpdateAsync(usuario);
 200
 201            // Log the audit
 1202            await _auditoriaService.LogAuditoriaAsync(
 1203                _currentUser.UserId ?? 0,
 1204                AccionEnum.ActualizarUsuario,
 1205                ContextoEnum.Usuarios,
 1206                _currentUser.Host,
 1207                entidad: nameof(Usuario),
 1208                entidadId: usuarioId,
 1209                detalle: new { evento = "asignar_rol", rolId, rolNombre = rol.Nombre });
 210
 1211            return (true, null);
 212        }
 0213        catch (Exception ex)
 214        {
 0215            return (false, $"Error al asignar rol: {ex.Message}");
 216        }
 4217    }
 218
 219    public async Task<(bool Success, string? Error)> RemoverRolAsync(long usuarioId, long rolId)
 220    {
 221        try
 222        {
 3223            var usuario = await _usuarioRepository.GetByIdAsync(usuarioId);
 3224            if (usuario == null)
 225            {
 1226                return (false, "Usuario no encontrado");
 227            }
 228
 229            // Find the role by ID to avoid tracking issues
 3230            var rol = usuario.Roles.FirstOrDefault(r => r.Id == rolId);
 2231            if (rol == null)
 232            {
 1233                return (false, "El usuario no tiene este rol asignado");
 234            }
 235
 236            // Remove the role from the collection
 1237            usuario.Roles.Remove(rol);
 238
 239            // Update the usuario (this will persist the role removal)
 1240            await _usuarioRepository.UpdateAsync(usuario);
 241
 242            // Log the audit
 1243            await _auditoriaService.LogAuditoriaAsync(
 1244                _currentUser.UserId ?? 0,
 1245                AccionEnum.ActualizarUsuario,
 1246                ContextoEnum.Usuarios,
 1247                _currentUser.Host,
 1248                entidad: nameof(Usuario),
 1249                entidadId: usuarioId,
 1250                detalle: new { evento = "remover_rol", rolId, rolNombre = rol.Nombre });
 251
 1252            return (true, null);
 253        }
 0254        catch (Exception ex)
 255        {
 0256            return (false, $"Error al remover rol: {ex.Message}");
 257        }
 3258    }
 259
 260    public async Task DeleteAsync(long id)
 261    {
 1262        await _auditoriaService.LogAuditoriaAsync(_currentUser.UserId ?? 0, AccionEnum.ActualizarUsuario, ContextoEnum.U
 1263        await _usuarioRepository.DeleteAsync(id);
 1264    }
 265}
 266