Overview
PROD-SYS implements a personnel-centric user management model where each employee (persona) can have an associated system user account. This design separates organizational identity from system access, enabling flexible workforce management.Personnel vs Users
Personnel (Personas)
Database Table:personas
Represents physical employees in your organization:
- Unique employee code (
codigo_interno) - Personal information (name, email, phone)
- Organizational assignment (area, role)
- Employment status (Activo, Incapacitado, Inactivo, Baja)
- Absence tracking (dates, type, reason)
Users (Usuarios)
Database Table:usuarios
Represents system access credentials:
- Linked to a persona (optional for technical admin)
- Username and password
- System role (Administrador, Inspector, Supervisor, etc.)
- Access state (Activo, Inactivo)
- Security controls (lockout, password expiry)
Relationship
- One persona can have zero or one user account
- Production staff may not need system access
- Administrative users must have a persona (except bootstrap admin)
- User account status is derived from persona employment status
backend/database/sqlite.js:1023-1066
User Lifecycle
1. System Bootstrap
Initial Administrator Creation The first system startup requires creating the initial administrator: Endpoint:POST /api/bootstrap/initialize
Prerequisites:
- System state:
NO_INICIALIZADO - No existing users in database
- Creates persona with
rol_organizacional = 'ADMIN' - Creates user with
rol_id= Administrador role - Sets system state to
INICIALIZADO - Logs bootstrap event to audit trail
backend/domains/bootstrap/bootstrap.service.js:31-84
2. Personnel Registration
Register New Employee Endpoint:POST /api/personnel/personal
Permission: MANAGE_STAFF
Request Body:
- Validates unique
codigo_internoandemail - Creates persona record
- Generates temporary password (8-character alphanumeric)
- Creates user account with username =
codigo_interno - Assigns default role (Operario for production staff)
- Sets
must_change_password = true - Returns temporary password (send via secure channel)
backend/domains/personal/personal.service.js:94-139
Response:
In production, temporary passwords should be sent via email or SMS, not returned in API responses. The current implementation logs them for development convenience.
3. User Account States
Employment Status (Personas)
| Status | Description | Access Impact |
|---|---|---|
| Activo | Currently employed and working | Full system access if user exists |
| Incapacitado | Medical leave / disability | Login denied |
| Inactivo | Temporary leave (vacation, etc.) | Login denied |
| Baja | Terminated employment | Login denied, no modifications allowed |
backend/database/sqlite.js:1032
Access Status (Usuarios)
| Status | Description | Effect |
|---|---|---|
| Activo | Account enabled | Can authenticate |
| Inactivo | Account disabled | Login denied |
| Bloqueado | Security lockout | Login denied until admin unlocks |
- Triggered after 5 consecutive failed login attempts
- Sets
bloqueado_attimestamp - Increments
intentos_fallidoscounter
backend/domains/auth/auth.service.js:64-71
4. Status Management
Update Employment Status Endpoint:PUT /api/personnel/personal/:id
Permission: MANAGE_STAFF
Absence Registration (Incapacitado/Inactivo):
Incapacitadorequirestipo_ausencia = 'Incapacidad'Inactivorequirestipo_ausencia = 'Permiso'ausencia_desdeandausencia_hastamandatory for absencesBajais terminal - no further modifications allowed
- System checks absence expiration daily
- Auto-reverts to
Activowhenausencia_hastais past - Records automatic state change in audit log
backend/domains/personal/personal.service.js:141-198
Termination (Baja):
5. Access Control Toggle
Enable/Disable System Access Endpoint:POST /api/personnel/personal/:id/toggle-acceso
Permission: MANAGE_STAFF
Use Case: Temporarily disable system access without changing employment status
Restrictions:
- Only applies to Production area personnel
- Cannot modify if persona is in
Bajastatus - Does not affect employment record
- Updates
usuarios.estado_usuariotoActivoorInactivo - Immediate session termination (checked on every request)
- Logged to audit trail
backend/domains/personal/personal.service.js:200-233
Role Assignment
System Roles
Available Roles (defined inroles table):
| Role | Description | Key Permissions |
|---|---|---|
| Administrador | Full system access | All permissions |
| Inspector | Quality and production management | MANAGE_STAFF, MANAGE_PRODUCTION, MANAGE_QUALITY, VIEW_AUDIT |
| Supervisor | Production oversight | VIEW_STAFF, ASSIGN_OPERATIONS, VIEW_PRODUCTION |
| Jefe de Operaciones | Operations leadership | MANAGE_STAFF, VIEW_PRODUCTION, MANAGE_MACHINES |
| Gerencia | Executive view | VIEW_* permissions (read-only access) |
| Operario | Production workers | MANAGE_PRODUCTION (own work) |
backend/shared/auth/permissions.js:24-67
Assign Role
Endpoint:POST /api/personnel/personal/:id/asignar-rol
Permission: MANAGE_STAFF
Request:
rol_id: ID fromrolestablemotivo_cambio: Required justification for audit trailes_correccion: Flag for error corrections (prefixes with[CORRECCIÓN])categoria_motivo: Optional audit category
- Validates persona exists and is not in
Bajastatus - Updates
usuarios.rol_id - Creates history entry in
persona_rolestable - Logs to audit trail with action
ROLE_CHANGE - Returns success
backend/domains/personal/personal.service.js:241-270
Password Management
First Login Flow
Must Change Password: All new users havemust_change_password = true:
- User logs in with temporary password
- JWT payload includes
must_change_password: true - Frontend enforces password change before accessing system
- User submits new password via change password endpoint
must_change_passwordset tofalse
Password Change
Endpoint:POST /api/auth/change-password
Authentication: Required (own account)
Request:
- Current password must match (bcrypt comparison)
- New password must differ from current
- Updates
password_last_changed_attimestamp - Sets
must_change_password = false
backend/domains/auth/auth.service.js:97-108
Admin Password Reset
Endpoint:POST /api/personnel/personal/:id/reset-password
Permission: MANAGE_STAFF
Use Case: User forgot password or account recovery
Process:
- Generates new 8-character temporary password
- Hashes with bcrypt (cost factor 10)
- Updates user account
- Sets
must_change_password = true - Logs password reset to audit trail
- Returns temporary password
backend/domains/personal/personal.service.js:272-293
Temporary passwords should be communicated through secure out-of-band channels (email, SMS, or in-person).
User Authentication
Login Flow
Endpoint:POST /api/auth/login
Request:
- System Initialization Check: Denies login if system not initialized
- User Lookup: Retrieves user by username
- Employment Status Check: Verifies persona is not in Baja/Incapacitado/Inactivo
- Account Lockout Check: Verifies
bloqueado_atis null - Password Verification: Constant-time bcrypt comparison
- Timing Attack Mitigation: Ensures minimum 300ms processing time
- Failed Attempt Tracking: Increments counter, locks after 5 failures
- Success: Resets failed attempts, generates JWT
backend/domains/auth/auth.service.js:22-95
Response:
- Cookie:
token(httpOnly, secure in production, sameSite: Strict) - Response body:
data.token
Session Validation
Every authenticated API request:- Extracts JWT from
Authorization: Bearer <token>or cookie - Verifies signature with
JWT_SECRET - Real-time status check: Queries database for current
estado_usuario - Denies access if user is no longer
Activo - Attaches decoded user object to
req.user
backend/middlewares/auth.middleware.js:8-53
Session invalidation is immediate. Disabling a user account takes effect on their very next API request.
Operational Assignments
Assign to Production
Endpoint:POST /api/personnel/personal/:id/asignar-operacion
Permission: ASSIGN_OPERATIONS
Use Case: Assign personnel to specific machines/processes
Request:
- Persona must be in
Activostatus - Associated user must exist and be
Activo - Machine must not be in
BajaorFuera de serviciostatus - Admin user (username=‘admin’) cannot be assigned
asignaciones_operativas table
Location: backend/domains/personal/personal.service.js:295-354
Querying Users
Get All Staff
Endpoint:GET /api/personnel/personal
Permission: VIEW_STAFF
Returns: List of all personas with enriched status information
Enrichment:
- Calculates
estado_efectivo(effective status considering absence expiration) - Flags
ausencia_vencidaif absence end date has passed - Auto-updates expired absences to
Activo
backend/domains/personal/personal.service.js:37-61
Get Staff Details
Endpoint:GET /api/personnel/personal/:id
Permission: VIEW_STAFF
Returns:
backend/domains/personal/personal.service.js:63-92
Best Practices
User Provisioning
- Verify employee identity before creating accounts
- Use secure channels for temporary password delivery
- Assign roles based on job function, not individual
- Document role assignment justifications
- Review access permissions quarterly
Access Control
- Disable accounts immediately upon termination
- Use
Bajastatus for permanent employment termination - Review and remove unnecessary administrative privileges
- Monitor failed login attempts for security threats
- Require password changes every 90 days (implement policy)
Audit Compliance
- Always provide
motivo_cambiofor status changes - Use
es_correccionflag when fixing data entry errors - Review audit logs regularly for unauthorized changes
- Maintain separation of duties (different users for different roles)
Troubleshooting
User Cannot Login
Check:- System initialization status (
SELECT valor FROM sistema_config WHERE clave = 'estado_sistema') - User account status (
SELECT estado_usuario FROM usuarios WHERE username = ?) - Persona employment status (
SELECT estado_laboral FROM personas WHERE id = ?) - Account lockout (
SELECT bloqueado_at, intentos_fallidos FROM usuarios WHERE username = ?)
- If locked: Admin password reset clears lockout
- If inactive: Toggle access or update employment status
- If not initialized: Complete bootstrap process
Cannot Modify User
Check:- Persona status:
Bajais terminal, no modifications allowed - User role: Verify you have
MANAGE_STAFFpermission - Request format: Ensure all required fields present
Temporary Password Not Working
Check:- Password not expired (no expiry currently implemented)
- User not locked out from previous attempts
- Correct username (case-sensitive)
- Temporary password copied exactly (no extra spaces)
Related Documentation
Roles & Permissions
Understanding permission mappings
Audit Logs
Tracking user management actions
Configuration
Authentication and security settings