El módulo de Seguridad proporciona endpoints para la gestión de usuarios y autenticación en el sistema PoliMarket. Implementa los requisitos funcionales RF01 (Gestión Centralizada de Autorizaciones) y RF02 (Inicio de Sesión de Usuarios).
http://localhost:{puerto}/api/Security
- Clean Architecture: Separación de capas (Domain, Application, Infrastructure, Presentation)
- Service Layer: Servicios con lógica de negocio (SecurityService)
- Repository Pattern: Abstracción del acceso a datos
- Facade Pattern: SecurityController como punto de entrada unificado
- Dependency Injection: Inyección de dependencias mediante interfaces
- Principios SOLID: Aplicados en toda la implementación
Controller → ISecurityService → SecurityService → IRepository → Repository → DbContext
Por el momento, el sistema implementa autenticación básica (usuario y contraseña). Las credenciales se validan contra la base de datos y el password se almacena hasheado usando SHA256.
Para información detallada de cada endpoint, consulte los siguientes documentos:
- POST /api/Security/login - Inicio de sesión de usuarios
- POST /api/Security/users - Crear nuevo usuario
- PUT /api/Security/users/{id} - Actualizar usuario
- PATCH /api/Security/users/{id}/revoke-access - Revocar acceso
- GET /api/Security/users - Listar usuarios
- GET /api/Security/roles - Listar roles
Endpoints relacionados:
POST /api/Security/users- Crear usuarioPUT /api/Security/users/{id}- Modificar usuarioPATCH /api/Security/users/{id}/revoke-access- Revocar acceso
Criterios de Aceptación:
- ✅ CA 1.1: Usuario con rol "Vendedor Activo" puede iniciar sesión
- ✅ CA 1.2: Usuario "Vendedor Inactivo" o "Suspendido" no puede acceder
Endpoint relacionado:
POST /api/Security/login
Criterios de Aceptación:
- ✅ CA 2.1: Autenticación exitosa con credenciales válidas
- ✅ CA 2.2: Error con contraseña incorrecta
- ✅ CA 2.3: Error con usuario inexistente
- ✅ CA 2.4: Carga de rol y permisos después del login
| Código | Descripción |
|---|---|
| 200 | OK - Solicitud exitosa |
| 201 | Created - Recurso creado exitosamente |
| 400 | Bad Request - Datos inválidos |
| 401 | Unauthorized - Credenciales inválidas o usuario inactivo |
| 404 | Not Found - Recurso no encontrado |
| 500 | Internal Server Error - Error del servidor |
Todas las respuestas de error siguen el siguiente formato:
{
"message": "Descripción del error"
}Ejemplos:
// Error de credenciales inválidas
{
"message": "Usuario o contraseña inválidos"
}
// Error de usuario inactivo
{
"message": "El usuario está inactivo o suspendido. No se permite el acceso."
}
// Error de username duplicado
{
"message": "El nombre de usuario 'jperez' ya está en uso."
}Los roles disponibles controlan el acceso de los usuarios:
| ID | Nombre | Descripción |
|---|---|---|
| 1 | Vendedor Activo | Puede iniciar sesión y acceder al módulo de ventas |
| 2 | Vendedor Inactivo | No puede iniciar sesión |
| 3 | Administrador | Acceso completo al sistema |
POST /api/Security/usersPOST /api/Security/loginPUT /api/Security/users/{id}PATCH /api/Security/users/{id}/revoke-accessLas contraseñas se hashean usando SHA256 antes de almacenarse en la base de datos. Nunca se almacenan en texto plano.
Nota: Para producción se recomienda usar algoritmos más robustos como BCrypt o Argon2.
El endpoint de login devuelve el mismo mensaje de error tanto para usuario inexistente como para contraseña incorrecta, previniendo ataques de enumeración de usuarios.
Todos los endpoints validan los datos de entrada antes de procesarlos.
// Login
async function login(username, password) {
const response = await fetch('http://localhost:5000/api/Security/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
if (response.ok) {
const data = await response.json();
console.log('Login exitoso:', data);
return data;
} else {
const error = await response.json();
console.error('Error de login:', error.message);
throw new Error(error.message);
}
}
// Crear usuario
async function createUser(userData) {
const response = await fetch('http://localhost:5000/api/Security/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
return await response.json();
}// Login
var client = new HttpClient();
var loginRequest = new { username = "jperez", password = "password123" };
var content = new StringContent(
JsonSerializer.Serialize(loginRequest),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"http://localhost:5000/api/Security/login",
content
);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
var loginResponse = JsonSerializer.Deserialize<LoginResponse>(result);
Console.WriteLine($"Login exitoso: {loginResponse.FirstName}");
}import requests
# Login
def login(username, password):
url = "http://localhost:5000/api/Security/login"
payload = {
"username": username,
"password": password
}
response = requests.post(url, json=payload)
if response.status_code == 200:
return response.json()
else:
raise Exception(response.json()['message'])
# Uso
try:
result = login("jperez", "password123")
print(f"Login exitoso: {result['firstName']}")
except Exception as e:
print(f"Error: {e}"){
"info": {
"name": "PoliMarket - Security API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Login",
"request": {
"method": "POST",
"url": "{{baseUrl}}/api/Security/login",
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"jperez\",\n \"password\": \"password123\"\n}"
}
}
}
],
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:5000"
}
]
}Solución: Verificar que appsettings.json contenga la configuración de connection string.
Posibles causas:
- Username incorrecto
- Password incorrecto
- Usuario no existe en la base de datos
Solución: El usuario tiene IsActive = false. Un administrador debe reactivarlo usando el endpoint de actualización.
- Implementar JWT para autenticación stateless
- Agregar refresh tokens
- Implementar rate limiting
- Agregar logging de intentos de login
- Implementar bloqueo de cuenta después de múltiples intentos fallidos
- Migrar a BCrypt o Argon2 para hashing de contraseñas
Para preguntas o reportar issues relacionados con el módulo de seguridad, contactar al equipo de desarrollo backend.