1408 lines
52 KiB
YAML
1408 lines
52 KiB
YAML
# TRACEABILITY-MGN-001.yaml
|
|
# Matriz de Trazabilidad - MGN-001: Fundamentos
|
|
# Fecha: 2025-11-24
|
|
# Versión: 1.0
|
|
|
|
module:
|
|
id: MGN-001
|
|
name: "Fundamentos"
|
|
description: "Autenticación, usuarios, roles, permisos, multi-tenancy"
|
|
priority: P0
|
|
story_points: 68
|
|
status: Diseñado
|
|
|
|
metadata:
|
|
total_rf: 8
|
|
total_et_backend: 8
|
|
total_et_frontend: 8
|
|
total_tables: 10
|
|
total_tests: 94
|
|
coverage: 100%
|
|
|
|
requirements:
|
|
- rf_id: RF-MGN-001-001
|
|
rf_title: "Autenticación de Usuarios"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-001-autenticacion-usuarios.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-001-autenticación-de-usuarios.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/login
|
|
description: "Autenticación con email y contraseña"
|
|
- method: POST
|
|
path: /api/v1/auth/refresh
|
|
description: "Renovar JWT con refresh token"
|
|
- method: POST
|
|
path: /api/v1/auth/logout
|
|
description: "Cerrar sesión"
|
|
services:
|
|
- name: "AuthService"
|
|
file: "src/modules/auth/services/auth.service.ts"
|
|
methods:
|
|
- login
|
|
- validateUser
|
|
- generateTokens
|
|
- refreshToken
|
|
- logout
|
|
controllers:
|
|
- name: "AuthController"
|
|
file: "src/modules/auth/controllers/auth.controller.ts"
|
|
dtos:
|
|
- name: "LoginDto"
|
|
file: "src/modules/auth/dto/login.dto.ts"
|
|
- name: "TokenResponseDto"
|
|
file: "src/modules/auth/dto/token-response.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-001-autenticación-de-usuarios.md"
|
|
routes:
|
|
- path: "/login"
|
|
component: "LoginPage"
|
|
- path: "/logout"
|
|
component: "LogoutPage"
|
|
components:
|
|
- name: "LoginForm"
|
|
file: "src/features/auth/ui/LoginForm.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "authApi"
|
|
file: "src/entities/auth/api/auth.api.ts"
|
|
methods:
|
|
- login
|
|
- refresh
|
|
- logout
|
|
state_management:
|
|
- name: "useAuthStore"
|
|
file: "src/entities/auth/model/auth.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: users
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- UPDATE
|
|
indices:
|
|
- idx_users_email_tenant
|
|
- idx_users_tenant_id
|
|
rls_policy: tenant_isolation_users
|
|
- schema: auth
|
|
table: login_history
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- INSERT
|
|
indices:
|
|
- idx_login_history_user_id
|
|
- idx_login_history_created_at
|
|
rls_policy: tenant_isolation_login_history
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/auth.service.spec.ts"
|
|
test_cases:
|
|
- "should authenticate user with valid credentials"
|
|
- "should throw UnauthorizedException for invalid credentials"
|
|
- "should increment failed_login_attempts on failure"
|
|
- "should lock account after 5 failed attempts"
|
|
- "should generate valid JWT token"
|
|
- "should refresh token successfully"
|
|
integration_tests:
|
|
- file: "test/auth/auth.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/login should return JWT token"
|
|
- "POST /api/v1/auth/login should return 401 for invalid credentials"
|
|
- "POST /api/v1/auth/refresh should renew token"
|
|
- "POST /api/v1/auth/logout should invalidate session"
|
|
- "should enforce tenant isolation"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/features/auth/ui/LoginForm.test.tsx"
|
|
test_cases:
|
|
- "should render login form"
|
|
- "should validate email format"
|
|
- "should validate password required"
|
|
- "should submit valid credentials"
|
|
- "should show error for invalid credentials"
|
|
e2e_tests:
|
|
- file: "e2e/auth/login.spec.ts"
|
|
test_cases:
|
|
- "should login successfully with valid credentials"
|
|
- "should show error for invalid credentials"
|
|
- "should redirect to dashboard after login"
|
|
- "should lock account after 5 failed attempts"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario con credenciales válidas puede autenticarse exitosamente"
|
|
status: Pending
|
|
test_reference: "test/auth/auth.controller.e2e-spec.ts:24"
|
|
- id: AC-002
|
|
description: "JWT token contiene user_id, tenant_id, roles, permissions"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/auth.service.spec.ts:42"
|
|
- id: AC-003
|
|
description: "Sistema bloquea cuenta después de 5 intentos fallidos"
|
|
status: Pending
|
|
test_reference: "test/auth/auth.controller.e2e-spec.ts:68"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Email debe ser único por tenant (no globalmente)"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:CONSTRAINT uq_users_email_tenant"
|
|
test_reference: "test/auth/auth.controller.e2e-spec.ts:92"
|
|
- id: RN-002
|
|
description: "Contraseña debe tener mínimo 8 caracteres, 1 mayúscula, 1 número"
|
|
implementation: "src/modules/auth/dto/login.dto.ts:@IsStrongPassword()"
|
|
test_reference: "src/modules/auth/services/auth.service.spec.ts:108"
|
|
- id: RN-003
|
|
description: "JWT expira en 8 horas, refresh token en 30 días"
|
|
implementation: "src/modules/auth/services/auth.service.ts:generateTokens()"
|
|
test_reference: "src/modules/auth/services/auth.service.spec.ts:124"
|
|
|
|
dependencies:
|
|
rf_dependencies: []
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "@nestjs/passport"
|
|
version: "^10.0.0"
|
|
- name: "bcrypt"
|
|
version: "^5.1.0"
|
|
- name: "jsonwebtoken"
|
|
version: "^9.0.0"
|
|
|
|
- rf_id: RF-MGN-001-002
|
|
rf_title: "Gestión de Roles y Permisos (RBAC)"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-002-gestion-roles.md"
|
|
priority: P0
|
|
story_points: 13
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-002-gestión-de-roles-y-permisos-rbac.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/roles
|
|
description: "Crear rol"
|
|
- method: GET
|
|
path: /api/v1/auth/roles
|
|
description: "Listar roles"
|
|
- method: GET
|
|
path: /api/v1/auth/roles/:id
|
|
description: "Obtener rol por ID"
|
|
- method: PUT
|
|
path: /api/v1/auth/roles/:id
|
|
description: "Actualizar rol"
|
|
- method: DELETE
|
|
path: /api/v1/auth/roles/:id
|
|
description: "Eliminar rol"
|
|
- method: POST
|
|
path: /api/v1/auth/roles/:id/permissions
|
|
description: "Asignar permisos a rol"
|
|
services:
|
|
- name: "RoleService"
|
|
file: "src/modules/auth/services/role.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- assignPermissions
|
|
controllers:
|
|
- name: "RoleController"
|
|
file: "src/modules/auth/controllers/role.controller.ts"
|
|
dtos:
|
|
- name: "CreateRoleDto"
|
|
file: "src/modules/auth/dto/create-role.dto.ts"
|
|
- name: "UpdateRoleDto"
|
|
file: "src/modules/auth/dto/update-role.dto.ts"
|
|
- name: "AssignPermissionsDto"
|
|
file: "src/modules/auth/dto/assign-permissions.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-002-gestión-de-roles-y-permisos-rbac.md"
|
|
routes:
|
|
- path: "/auth/roles"
|
|
component: "RolesPage"
|
|
- path: "/auth/roles/create"
|
|
component: "CreateRolePage"
|
|
- path: "/auth/roles/:id/edit"
|
|
component: "EditRolePage"
|
|
- path: "/auth/roles/:id"
|
|
component: "ViewRolePage"
|
|
components:
|
|
- name: "RolesTable"
|
|
file: "src/widgets/roles-table/ui/RolesTable.tsx"
|
|
type: widget
|
|
- name: "CreateRoleForm"
|
|
file: "src/features/create-role/ui/CreateRoleForm.tsx"
|
|
type: feature
|
|
- name: "PermissionsMatrix"
|
|
file: "src/features/assign-permissions/ui/PermissionsMatrix.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "roleApi"
|
|
file: "src/entities/role/api/role.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
- assignPermissions
|
|
state_management:
|
|
- name: "useRoleStore"
|
|
file: "src/entities/role/model/role.store.ts"
|
|
type: zustand
|
|
- name: "useRoles"
|
|
file: "src/entities/role/api/role.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: roles
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_roles_tenant_id
|
|
- idx_roles_name_tenant
|
|
rls_policy: tenant_isolation_roles
|
|
- schema: auth
|
|
table: permissions
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_permissions_model_action
|
|
rls_policy: tenant_isolation_permissions
|
|
- schema: auth
|
|
table: role_permissions
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- DELETE
|
|
indices:
|
|
- idx_role_permissions_role_id
|
|
- idx_role_permissions_permission_id
|
|
rls_policy: tenant_isolation_role_permissions
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/role.service.spec.ts"
|
|
test_cases:
|
|
- "should create role with valid data"
|
|
- "should throw error when role name already exists"
|
|
- "should assign permissions to role"
|
|
- "should validate permission existence before assignment"
|
|
- "should find all roles for tenant"
|
|
- "should update role successfully"
|
|
- "should soft delete role"
|
|
integration_tests:
|
|
- file: "test/auth/role.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/roles should create role"
|
|
- "GET /api/v1/auth/roles should return all roles"
|
|
- "GET /api/v1/auth/roles/:id should return role with permissions"
|
|
- "PUT /api/v1/auth/roles/:id should update role"
|
|
- "DELETE /api/v1/auth/roles/:id should soft delete role"
|
|
- "POST /api/v1/auth/roles/:id/permissions should assign permissions"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check admin permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/roles-table/ui/RolesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with roles"
|
|
- "should handle pagination"
|
|
- "should call delete on button click"
|
|
- file: "src/features/create-role/ui/CreateRoleForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
- "should show error messages"
|
|
- file: "src/features/assign-permissions/ui/PermissionsMatrix.test.tsx"
|
|
test_cases:
|
|
- "should render permissions grouped by model"
|
|
- "should toggle permissions on checkbox click"
|
|
- "should save permissions successfully"
|
|
e2e_tests:
|
|
- file: "e2e/auth/roles.spec.ts"
|
|
test_cases:
|
|
- "should create role successfully"
|
|
- "should assign permissions to role"
|
|
- "should edit role successfully"
|
|
- "should delete role with confirmation"
|
|
- "should enforce admin permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede crear roles con permisos CRUD por modelo"
|
|
status: Pending
|
|
test_reference: "test/auth/role.controller.e2e-spec.ts:30"
|
|
- id: AC-002
|
|
description: "Roles soportan herencia (parent_role_id)"
|
|
status: Pending
|
|
test_reference: "test/auth/role.controller.e2e-spec.ts:55"
|
|
- id: AC-003
|
|
description: "Permisos se validan en cada endpoint (PermissionsGuard)"
|
|
status: Pending
|
|
test_reference: "test/auth/role.controller.e2e-spec.ts:85"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Nombre de rol debe ser único por tenant"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:CONSTRAINT uq_roles_name_tenant"
|
|
test_reference: "src/modules/auth/services/role.service.spec.ts:45"
|
|
- id: RN-002
|
|
description: "Permisos se definen como {model}.{action} (ej: users.create)"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:permissions table"
|
|
test_reference: "src/modules/auth/services/role.service.spec.ts:68"
|
|
- id: RN-003
|
|
description: "No se puede eliminar rol si tiene usuarios asignados"
|
|
implementation: "src/modules/auth/services/role.service.ts:remove()"
|
|
test_reference: "src/modules/auth/services/role.service.spec.ts:92"
|
|
|
|
dependencies:
|
|
rf_dependencies: []
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
- name: "class-validator"
|
|
version: "^0.14.0"
|
|
|
|
- rf_id: RF-MGN-001-003
|
|
rf_title: "Gestión de Usuarios"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-003-gestion-usuarios.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-003-gestión-de-usuarios.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/users
|
|
description: "Crear usuario"
|
|
- method: GET
|
|
path: /api/v1/auth/users
|
|
description: "Listar usuarios"
|
|
- method: GET
|
|
path: /api/v1/auth/users/:id
|
|
description: "Obtener usuario por ID"
|
|
- method: PUT
|
|
path: /api/v1/auth/users/:id
|
|
description: "Actualizar usuario"
|
|
- method: DELETE
|
|
path: /api/v1/auth/users/:id
|
|
description: "Eliminar usuario (soft delete)"
|
|
- method: POST
|
|
path: /api/v1/auth/users/:id/roles
|
|
description: "Asignar roles a usuario"
|
|
- method: PATCH
|
|
path: /api/v1/auth/users/:id/password
|
|
description: "Cambiar contraseña"
|
|
services:
|
|
- name: "UserService"
|
|
file: "src/modules/auth/services/user.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- assignRoles
|
|
- changePassword
|
|
controllers:
|
|
- name: "UserController"
|
|
file: "src/modules/auth/controllers/user.controller.ts"
|
|
dtos:
|
|
- name: "CreateUserDto"
|
|
file: "src/modules/auth/dto/create-user.dto.ts"
|
|
- name: "UpdateUserDto"
|
|
file: "src/modules/auth/dto/update-user.dto.ts"
|
|
- name: "ChangePasswordDto"
|
|
file: "src/modules/auth/dto/change-password.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-003-gestión-de-usuarios.md"
|
|
routes:
|
|
- path: "/auth/users"
|
|
component: "UsersPage"
|
|
- path: "/auth/users/create"
|
|
component: "CreateUserPage"
|
|
- path: "/auth/users/:id/edit"
|
|
component: "EditUserPage"
|
|
- path: "/auth/users/:id"
|
|
component: "ViewUserPage"
|
|
components:
|
|
- name: "UsersTable"
|
|
file: "src/widgets/users-table/ui/UsersTable.tsx"
|
|
type: widget
|
|
- name: "CreateUserForm"
|
|
file: "src/features/create-user/ui/CreateUserForm.tsx"
|
|
type: feature
|
|
- name: "UserCard"
|
|
file: "src/entities/user/ui/UserCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "userApi"
|
|
file: "src/entities/user/api/user.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
- assignRoles
|
|
- changePassword
|
|
state_management:
|
|
- name: "useUserStore"
|
|
file: "src/entities/user/model/user.store.ts"
|
|
type: zustand
|
|
- name: "useUsers"
|
|
file: "src/entities/user/api/user.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: users
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_users_tenant_id
|
|
- idx_users_email_tenant
|
|
- idx_users_status
|
|
rls_policy: tenant_isolation_users
|
|
- schema: auth
|
|
table: user_roles
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- DELETE
|
|
indices:
|
|
- idx_user_roles_user_id
|
|
- idx_user_roles_role_id
|
|
rls_policy: tenant_isolation_user_roles
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/user.service.spec.ts"
|
|
test_cases:
|
|
- "should create user with valid data"
|
|
- "should hash password before saving"
|
|
- "should throw error when email already exists"
|
|
- "should assign roles to user"
|
|
- "should change password successfully"
|
|
- "should validate old password before changing"
|
|
- "should soft delete user"
|
|
integration_tests:
|
|
- file: "test/auth/user.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/users should create user"
|
|
- "GET /api/v1/auth/users should return all users"
|
|
- "GET /api/v1/auth/users/:id should return user with roles"
|
|
- "PUT /api/v1/auth/users/:id should update user"
|
|
- "DELETE /api/v1/auth/users/:id should soft delete user"
|
|
- "POST /api/v1/auth/users/:id/roles should assign roles"
|
|
- "PATCH /api/v1/auth/users/:id/password should change password"
|
|
- "should enforce tenant isolation"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/users-table/ui/UsersTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with users"
|
|
- "should handle pagination"
|
|
- "should filter by status"
|
|
- file: "src/features/create-user/ui/CreateUserForm.test.tsx"
|
|
test_cases:
|
|
- "should validate email format"
|
|
- "should validate password strength"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/auth/users.spec.ts"
|
|
test_cases:
|
|
- "should create user successfully"
|
|
- "should assign roles to user"
|
|
- "should change password successfully"
|
|
- "should delete user with confirmation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede crear usuarios con email y contraseña"
|
|
status: Pending
|
|
test_reference: "test/auth/user.controller.e2e-spec.ts:25"
|
|
- id: AC-002
|
|
description: "Contraseñas se almacenan hasheadas con bcrypt"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/user.service.spec.ts:42"
|
|
- id: AC-003
|
|
description: "Usuario puede cambiar su propia contraseña"
|
|
status: Pending
|
|
test_reference: "test/auth/user.controller.e2e-spec.ts:105"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Email debe ser único por tenant"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:CONSTRAINT uq_users_email_tenant"
|
|
test_reference: "src/modules/auth/services/user.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Usuario debe tener al menos un rol asignado"
|
|
implementation: "src/modules/auth/services/user.service.ts:create()"
|
|
test_reference: "src/modules/auth/services/user.service.spec.ts:76"
|
|
- id: RN-003
|
|
description: "Status del usuario debe ser 'active' para login"
|
|
implementation: "src/modules/auth/services/auth.service.ts:validateUser()"
|
|
test_reference: "src/modules/auth/services/auth.service.spec.ts:95"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-002
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "bcrypt"
|
|
version: "^5.1.0"
|
|
- name: "class-validator"
|
|
version: "^0.14.0"
|
|
|
|
- rf_id: RF-MGN-001-004
|
|
rf_title: "Multi-Tenancy con Schema-Level Isolation"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-004-multi-tenancy.md"
|
|
priority: P0
|
|
story_points: 13
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-004-multi-tenancy-con-schema-level-isolation.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/tenants
|
|
description: "Crear tenant (admin solo)"
|
|
- method: GET
|
|
path: /api/v1/tenants
|
|
description: "Listar tenants (admin solo)"
|
|
- method: GET
|
|
path: /api/v1/tenants/:id
|
|
description: "Obtener tenant por ID"
|
|
- method: PUT
|
|
path: /api/v1/tenants/:id
|
|
description: "Actualizar tenant"
|
|
- method: GET
|
|
path: /api/v1/tenants/current
|
|
description: "Obtener tenant actual del usuario"
|
|
services:
|
|
- name: "TenantService"
|
|
file: "src/modules/auth/services/tenant.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- getCurrentTenant
|
|
- createTenantSchema
|
|
controllers:
|
|
- name: "TenantController"
|
|
file: "src/modules/auth/controllers/tenant.controller.ts"
|
|
dtos:
|
|
- name: "CreateTenantDto"
|
|
file: "src/modules/auth/dto/create-tenant.dto.ts"
|
|
- name: "UpdateTenantDto"
|
|
file: "src/modules/auth/dto/update-tenant.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-004-multi-tenancy-con-schema-level-isolation.md"
|
|
routes:
|
|
- path: "/admin/tenants"
|
|
component: "TenantsPage"
|
|
- path: "/admin/tenants/create"
|
|
component: "CreateTenantPage"
|
|
- path: "/admin/tenants/:id"
|
|
component: "ViewTenantPage"
|
|
components:
|
|
- name: "TenantsTable"
|
|
file: "src/widgets/tenants-table/ui/TenantsTable.tsx"
|
|
type: widget
|
|
- name: "CreateTenantForm"
|
|
file: "src/features/create-tenant/ui/CreateTenantForm.tsx"
|
|
type: feature
|
|
- name: "TenantSwitcher"
|
|
file: "src/features/tenant-switcher/ui/TenantSwitcher.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "tenantApi"
|
|
file: "src/entities/tenant/api/tenant.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- getCurrent
|
|
state_management:
|
|
- name: "useTenantStore"
|
|
file: "src/entities/tenant/model/tenant.store.ts"
|
|
type: zustand
|
|
- name: "useTenants"
|
|
file: "src/entities/tenant/api/tenant.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: tenants
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_tenants_subdomain
|
|
- idx_tenants_schema_name
|
|
- idx_tenants_status
|
|
rls_policy: null
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/tenant.service.spec.ts"
|
|
test_cases:
|
|
- "should create tenant with unique subdomain"
|
|
- "should create schema for tenant"
|
|
- "should throw error when subdomain already exists"
|
|
- "should get current tenant from context"
|
|
- "should update tenant successfully"
|
|
integration_tests:
|
|
- file: "test/auth/tenant.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/tenants should create tenant"
|
|
- "GET /api/v1/tenants should return all tenants (admin only)"
|
|
- "GET /api/v1/tenants/current should return current tenant"
|
|
- "PUT /api/v1/tenants/:id should update tenant"
|
|
- "should enforce superadmin permissions"
|
|
- "should validate subdomain format"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/tenants-table/ui/TenantsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with tenants"
|
|
- "should show tenant status"
|
|
- file: "src/features/create-tenant/ui/CreateTenantForm.test.tsx"
|
|
test_cases:
|
|
- "should validate subdomain format"
|
|
- "should validate unique subdomain"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/auth/tenants.spec.ts"
|
|
test_cases:
|
|
- "should create tenant successfully (superadmin)"
|
|
- "should show error for duplicate subdomain"
|
|
- "should enforce superadmin permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Cada tenant tiene su propio schema PostgreSQL"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/tenant.service.spec.ts:32"
|
|
- id: AC-002
|
|
description: "Usuarios de un tenant NO pueden ver datos de otro tenant"
|
|
status: Pending
|
|
test_reference: "test/auth/tenant.controller.e2e-spec.ts:68"
|
|
- id: AC-003
|
|
description: "Subdomain debe ser único y válido (a-z0-9-)"
|
|
status: Pending
|
|
test_reference: "test/auth/tenant.controller.e2e-spec.ts:92"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Subdomain debe ser único globalmente"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:CONSTRAINT UNIQUE subdomain"
|
|
test_reference: "src/modules/auth/services/tenant.service.spec.ts:54"
|
|
- id: RN-002
|
|
description: "Schema_name debe seguir formato tenant_{subdomain}"
|
|
implementation: "src/modules/auth/services/tenant.service.ts:create()"
|
|
test_reference: "src/modules/auth/services/tenant.service.spec.ts:72"
|
|
- id: RN-003
|
|
description: "Todas las queries deben filtrar por tenant_id automáticamente (RLS)"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:RLS policies"
|
|
test_reference: "test/integration/rls.e2e-spec.ts:25"
|
|
|
|
dependencies:
|
|
rf_dependencies: []
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "pg"
|
|
version: "^8.11.0"
|
|
- name: "@nestjs/config"
|
|
version: "^3.0.0"
|
|
|
|
- rf_id: RF-MGN-001-005
|
|
rf_title: "Reset de Contraseña"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-005-reset-password.md"
|
|
priority: P0
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-005-reset-de-contraseña.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/password/forgot
|
|
description: "Solicitar reset de contraseña (envía email con token)"
|
|
- method: POST
|
|
path: /api/v1/auth/password/reset
|
|
description: "Resetear contraseña con token"
|
|
- method: GET
|
|
path: /api/v1/auth/password/verify-token/:token
|
|
description: "Verificar validez del token de reset"
|
|
services:
|
|
- name: "PasswordResetService"
|
|
file: "src/modules/auth/services/password-reset.service.ts"
|
|
methods:
|
|
- requestPasswordReset
|
|
- resetPassword
|
|
- verifyResetToken
|
|
- sendResetEmail
|
|
controllers:
|
|
- name: "PasswordResetController"
|
|
file: "src/modules/auth/controllers/password-reset.controller.ts"
|
|
dtos:
|
|
- name: "ForgotPasswordDto"
|
|
file: "src/modules/auth/dto/forgot-password.dto.ts"
|
|
- name: "ResetPasswordDto"
|
|
file: "src/modules/auth/dto/reset-password.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-005-reset-de-contraseña.md"
|
|
routes:
|
|
- path: "/auth/forgot-password"
|
|
component: "ForgotPasswordPage"
|
|
- path: "/auth/reset-password/:token"
|
|
component: "ResetPasswordPage"
|
|
components:
|
|
- name: "ForgotPasswordForm"
|
|
file: "src/features/password-reset/ui/ForgotPasswordForm.tsx"
|
|
type: feature
|
|
- name: "ResetPasswordForm"
|
|
file: "src/features/password-reset/ui/ResetPasswordForm.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "passwordResetApi"
|
|
file: "src/entities/auth/api/password-reset.api.ts"
|
|
methods:
|
|
- forgotPassword
|
|
- resetPassword
|
|
- verifyToken
|
|
state_management:
|
|
- name: "usePasswordResetStore"
|
|
file: "src/entities/auth/model/password-reset.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: password_reset_tokens
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE
|
|
indices:
|
|
- idx_password_reset_tokens_token
|
|
- idx_password_reset_tokens_user_id
|
|
- idx_password_reset_tokens_expires_at
|
|
rls_policy: tenant_isolation_password_reset_tokens
|
|
- schema: auth
|
|
table: users
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- UPDATE
|
|
indices:
|
|
- idx_users_email_tenant
|
|
rls_policy: tenant_isolation_users
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/password-reset.service.spec.ts"
|
|
test_cases:
|
|
- "should generate reset token and send email"
|
|
- "should verify valid reset token"
|
|
- "should throw error for expired token"
|
|
- "should reset password successfully"
|
|
- "should invalidate token after use"
|
|
integration_tests:
|
|
- file: "test/auth/password-reset.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/password/forgot should send reset email"
|
|
- "POST /api/v1/auth/password/reset should reset password with valid token"
|
|
- "POST /api/v1/auth/password/reset should fail with expired token"
|
|
- "GET /api/v1/auth/password/verify-token/:token should verify token"
|
|
- "should enforce tenant isolation"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/features/password-reset/ui/ForgotPasswordForm.test.tsx"
|
|
test_cases:
|
|
- "should validate email format"
|
|
- "should submit email successfully"
|
|
- "should show success message"
|
|
- file: "src/features/password-reset/ui/ResetPasswordForm.test.tsx"
|
|
test_cases:
|
|
- "should validate password strength"
|
|
- "should validate password confirmation match"
|
|
- "should submit new password successfully"
|
|
e2e_tests:
|
|
- file: "e2e/auth/password-reset.spec.ts"
|
|
test_cases:
|
|
- "should request password reset successfully"
|
|
- "should reset password with valid token"
|
|
- "should show error for expired token"
|
|
- "should login with new password"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede solicitar reset de contraseña con email"
|
|
status: Pending
|
|
test_reference: "test/auth/password-reset.controller.e2e-spec.ts:25"
|
|
- id: AC-002
|
|
description: "Token de reset expira en 24 horas"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/password-reset.service.spec.ts:48"
|
|
- id: AC-003
|
|
description: "Token se invalida después de usarse"
|
|
status: Pending
|
|
test_reference: "test/auth/password-reset.controller.e2e-spec.ts:72"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Token de reset debe expirar en 24 horas"
|
|
implementation: "src/modules/auth/services/password-reset.service.ts:generateToken()"
|
|
test_reference: "src/modules/auth/services/password-reset.service.spec.ts:62"
|
|
- id: RN-002
|
|
description: "Token solo puede usarse una vez"
|
|
implementation: "src/modules/auth/services/password-reset.service.ts:resetPassword()"
|
|
test_reference: "src/modules/auth/services/password-reset.service.spec.ts:84"
|
|
- id: RN-003
|
|
description: "Email de reset debe enviarse solo si usuario existe"
|
|
implementation: "src/modules/auth/services/password-reset.service.ts:requestPasswordReset()"
|
|
test_reference: "src/modules/auth/services/password-reset.service.spec.ts:102"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-003
|
|
module_dependencies:
|
|
- MGN-014
|
|
external_dependencies:
|
|
- name: "nodemailer"
|
|
version: "^6.9.0"
|
|
- name: "crypto"
|
|
version: "built-in"
|
|
|
|
- rf_id: RF-MGN-001-006
|
|
rf_title: "Registro de Usuarios (Signup)"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-006-registro-usuarios.md"
|
|
priority: P1
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-006-registro-de-usuarios-signup.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/signup
|
|
description: "Registro de nuevo usuario (público)"
|
|
- method: POST
|
|
path: /api/v1/auth/verify-email
|
|
description: "Verificar email con token"
|
|
- method: POST
|
|
path: /api/v1/auth/resend-verification
|
|
description: "Reenviar email de verificación"
|
|
services:
|
|
- name: "SignupService"
|
|
file: "src/modules/auth/services/signup.service.ts"
|
|
methods:
|
|
- signup
|
|
- verifyEmail
|
|
- resendVerification
|
|
- sendVerificationEmail
|
|
controllers:
|
|
- name: "SignupController"
|
|
file: "src/modules/auth/controllers/signup.controller.ts"
|
|
dtos:
|
|
- name: "SignupDto"
|
|
file: "src/modules/auth/dto/signup.dto.ts"
|
|
- name: "VerifyEmailDto"
|
|
file: "src/modules/auth/dto/verify-email.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-006-registro-de-usuarios-signup.md"
|
|
routes:
|
|
- path: "/signup"
|
|
component: "SignupPage"
|
|
- path: "/verify-email/:token"
|
|
component: "VerifyEmailPage"
|
|
components:
|
|
- name: "SignupForm"
|
|
file: "src/features/signup/ui/SignupForm.tsx"
|
|
type: feature
|
|
- name: "EmailVerificationNotice"
|
|
file: "src/features/signup/ui/EmailVerificationNotice.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "signupApi"
|
|
file: "src/entities/auth/api/signup.api.ts"
|
|
methods:
|
|
- signup
|
|
- verifyEmail
|
|
- resendVerification
|
|
state_management:
|
|
- name: "useSignupStore"
|
|
file: "src/entities/auth/model/signup.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: users
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- INSERT
|
|
- SELECT
|
|
- UPDATE
|
|
indices:
|
|
- idx_users_email_tenant
|
|
- idx_users_status
|
|
rls_policy: tenant_isolation_users
|
|
- schema: auth
|
|
table: email_verification_tokens
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- INSERT
|
|
- SELECT
|
|
- DELETE
|
|
indices:
|
|
- idx_email_verification_tokens_token
|
|
- idx_email_verification_tokens_user_id
|
|
rls_policy: tenant_isolation_email_verification_tokens
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/signup.service.spec.ts"
|
|
test_cases:
|
|
- "should create user with pending_verification status"
|
|
- "should send verification email"
|
|
- "should verify email with valid token"
|
|
- "should activate user after email verification"
|
|
- "should throw error for expired token"
|
|
integration_tests:
|
|
- file: "test/auth/signup.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/signup should create user"
|
|
- "POST /api/v1/auth/verify-email should verify email"
|
|
- "POST /api/v1/auth/resend-verification should resend email"
|
|
- "should enforce unique email per tenant"
|
|
- "should not allow login before email verification"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/features/signup/ui/SignupForm.test.tsx"
|
|
test_cases:
|
|
- "should validate email format"
|
|
- "should validate password strength"
|
|
- "should validate terms acceptance"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/auth/signup.spec.ts"
|
|
test_cases:
|
|
- "should signup successfully"
|
|
- "should verify email with token"
|
|
- "should login after email verification"
|
|
- "should show error for duplicate email"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede registrarse con email y contraseña"
|
|
status: Pending
|
|
test_reference: "test/auth/signup.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Email de verificación se envía automáticamente"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/signup.service.spec.ts:45"
|
|
- id: AC-003
|
|
description: "Usuario no puede login antes de verificar email"
|
|
status: Pending
|
|
test_reference: "test/auth/signup.controller.e2e-spec.ts:76"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Usuario creado con status 'pending_verification'"
|
|
implementation: "src/modules/auth/services/signup.service.ts:signup()"
|
|
test_reference: "src/modules/auth/services/signup.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Token de verificación expira en 7 días"
|
|
implementation: "src/modules/auth/services/signup.service.ts:generateVerificationToken()"
|
|
test_reference: "src/modules/auth/services/signup.service.spec.ts:72"
|
|
- id: RN-003
|
|
description: "Al verificar email, status cambia a 'active'"
|
|
implementation: "src/modules/auth/services/signup.service.ts:verifyEmail()"
|
|
test_reference: "src/modules/auth/services/signup.service.spec.ts:95"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-003
|
|
module_dependencies:
|
|
- MGN-014
|
|
external_dependencies:
|
|
- name: "nodemailer"
|
|
version: "^6.9.0"
|
|
|
|
- rf_id: RF-MGN-001-007
|
|
rf_title: "Gestión de Sesiones"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-007-session-management.md"
|
|
priority: P0
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-007-gestión-de-sesiones.md"
|
|
endpoints:
|
|
- method: GET
|
|
path: /api/v1/auth/sessions
|
|
description: "Listar sesiones activas del usuario"
|
|
- method: DELETE
|
|
path: /api/v1/auth/sessions/:id
|
|
description: "Cerrar sesión específica"
|
|
- method: DELETE
|
|
path: /api/v1/auth/sessions/all
|
|
description: "Cerrar todas las sesiones (excepto actual)"
|
|
- method: GET
|
|
path: /api/v1/auth/sessions/current
|
|
description: "Obtener sesión actual"
|
|
services:
|
|
- name: "SessionService"
|
|
file: "src/modules/auth/services/session.service.ts"
|
|
methods:
|
|
- findUserSessions
|
|
- revokeSession
|
|
- revokeAllSessions
|
|
- getCurrentSession
|
|
- cleanupExpiredSessions
|
|
controllers:
|
|
- name: "SessionController"
|
|
file: "src/modules/auth/controllers/session.controller.ts"
|
|
dtos:
|
|
- name: "SessionResponseDto"
|
|
file: "src/modules/auth/dto/session-response.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-007-gestión-de-sesiones.md"
|
|
routes:
|
|
- path: "/profile/sessions"
|
|
component: "SessionsPage"
|
|
components:
|
|
- name: "SessionsTable"
|
|
file: "src/widgets/sessions-table/ui/SessionsTable.tsx"
|
|
type: widget
|
|
- name: "SessionCard"
|
|
file: "src/entities/session/ui/SessionCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "sessionApi"
|
|
file: "src/entities/session/api/session.api.ts"
|
|
methods:
|
|
- getSessions
|
|
- revokeSession
|
|
- revokeAllSessions
|
|
state_management:
|
|
- name: "useSessionStore"
|
|
file: "src/entities/session/model/session.store.ts"
|
|
type: zustand
|
|
- name: "useSessions"
|
|
file: "src/entities/session/api/session.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: sessions
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE
|
|
indices:
|
|
- idx_sessions_user_id
|
|
- idx_sessions_token
|
|
- idx_sessions_expires_at
|
|
- idx_sessions_status
|
|
rls_policy: tenant_isolation_sessions
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/session.service.spec.ts"
|
|
test_cases:
|
|
- "should create session on login"
|
|
- "should list user sessions"
|
|
- "should revoke session by id"
|
|
- "should revoke all user sessions except current"
|
|
- "should cleanup expired sessions"
|
|
integration_tests:
|
|
- file: "test/auth/session.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "GET /api/v1/auth/sessions should return user sessions"
|
|
- "DELETE /api/v1/auth/sessions/:id should revoke session"
|
|
- "DELETE /api/v1/auth/sessions/all should revoke all sessions"
|
|
- "GET /api/v1/auth/sessions/current should return current session"
|
|
- "should enforce user can only see own sessions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/sessions-table/ui/SessionsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with sessions"
|
|
- "should show device and location info"
|
|
- "should call revoke on button click"
|
|
e2e_tests:
|
|
- file: "e2e/auth/sessions.spec.ts"
|
|
test_cases:
|
|
- "should list active sessions"
|
|
- "should revoke specific session"
|
|
- "should revoke all sessions except current"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede ver todas sus sesiones activas"
|
|
status: Pending
|
|
test_reference: "test/auth/session.controller.e2e-spec.ts:32"
|
|
- id: AC-002
|
|
description: "Usuario puede cerrar sesión específica"
|
|
status: Pending
|
|
test_reference: "test/auth/session.controller.e2e-spec.ts:58"
|
|
- id: AC-003
|
|
description: "Usuario puede cerrar todas las sesiones excepto la actual"
|
|
status: Pending
|
|
test_reference: "test/auth/session.controller.e2e-spec.ts:82"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Sesión expira después de 8 horas de inactividad"
|
|
implementation: "src/modules/auth/services/auth.service.ts:generateTokens()"
|
|
test_reference: "src/modules/auth/services/session.service.spec.ts:68"
|
|
- id: RN-002
|
|
description: "Usuario solo puede ver y revocar sus propias sesiones"
|
|
implementation: "src/modules/auth/services/session.service.ts:findUserSessions()"
|
|
test_reference: "test/auth/session.controller.e2e-spec.ts:102"
|
|
- id: RN-003
|
|
description: "Sesiones expiradas se limpian automáticamente cada hora"
|
|
implementation: "src/modules/auth/services/session.service.ts:cleanupExpiredSessions()"
|
|
test_reference: "src/modules/auth/services/session.service.spec.ts:95"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-001
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "@nestjs/schedule"
|
|
version: "^4.0.0"
|
|
|
|
- rf_id: RF-MGN-001-008
|
|
rf_title: "Record Rules (Row Level Security)"
|
|
rf_file: "requerimientos-funcionales/mgn-001/RF-MGN-001-008-record-rules-rls.md"
|
|
priority: P1
|
|
story_points: 13
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-001/ET-BACKEND-MGN-001-008-record-rules-row-level-security.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/auth/record-rules
|
|
description: "Crear record rule"
|
|
- method: GET
|
|
path: /api/v1/auth/record-rules
|
|
description: "Listar record rules"
|
|
- method: GET
|
|
path: /api/v1/auth/record-rules/:id
|
|
description: "Obtener record rule por ID"
|
|
- method: PUT
|
|
path: /api/v1/auth/record-rules/:id
|
|
description: "Actualizar record rule"
|
|
- method: DELETE
|
|
path: /api/v1/auth/record-rules/:id
|
|
description: "Eliminar record rule"
|
|
services:
|
|
- name: "RecordRuleService"
|
|
file: "src/modules/auth/services/record-rule.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- applyRulesForUser
|
|
controllers:
|
|
- name: "RecordRuleController"
|
|
file: "src/modules/auth/controllers/record-rule.controller.ts"
|
|
dtos:
|
|
- name: "CreateRecordRuleDto"
|
|
file: "src/modules/auth/dto/create-record-rule.dto.ts"
|
|
- name: "UpdateRecordRuleDto"
|
|
file: "src/modules/auth/dto/update-record-rule.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-001/ET-FRONTEND-MGN-001-008-record-rules-row-level-security.md"
|
|
routes:
|
|
- path: "/auth/record-rules"
|
|
component: "RecordRulesPage"
|
|
- path: "/auth/record-rules/create"
|
|
component: "CreateRecordRulePage"
|
|
- path: "/auth/record-rules/:id/edit"
|
|
component: "EditRecordRulePage"
|
|
components:
|
|
- name: "RecordRulesTable"
|
|
file: "src/widgets/record-rules-table/ui/RecordRulesTable.tsx"
|
|
type: widget
|
|
- name: "CreateRecordRuleForm"
|
|
file: "src/features/create-record-rule/ui/CreateRecordRuleForm.tsx"
|
|
type: feature
|
|
- name: "RuleConditionBuilder"
|
|
file: "src/features/rule-condition-builder/ui/RuleConditionBuilder.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "recordRuleApi"
|
|
file: "src/entities/record-rule/api/record-rule.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
state_management:
|
|
- name: "useRecordRuleStore"
|
|
file: "src/entities/record-rule/model/record-rule.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: record_rules
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_record_rules_model
|
|
- idx_record_rules_tenant_id
|
|
rls_policy: tenant_isolation_record_rules
|
|
- schema: auth
|
|
table: role_record_rules
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- DELETE
|
|
indices:
|
|
- idx_role_record_rules_role_id
|
|
- idx_role_record_rules_rule_id
|
|
rls_policy: tenant_isolation_role_record_rules
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/auth/services/record-rule.service.spec.ts"
|
|
test_cases:
|
|
- "should create record rule with SQL filter"
|
|
- "should validate SQL syntax"
|
|
- "should apply rules for user with role"
|
|
- "should combine multiple rules with OR"
|
|
- "should update record rule successfully"
|
|
integration_tests:
|
|
- file: "test/auth/record-rule.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/auth/record-rules should create rule"
|
|
- "GET /api/v1/auth/record-rules should return all rules"
|
|
- "PUT /api/v1/auth/record-rules/:id should update rule"
|
|
- "DELETE /api/v1/auth/record-rules/:id should delete rule"
|
|
- "should enforce admin permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/record-rules-table/ui/RecordRulesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with rules"
|
|
- "should show SQL filter preview"
|
|
- file: "src/features/create-record-rule/ui/CreateRecordRuleForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should validate SQL syntax"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/auth/record-rules.spec.ts"
|
|
test_cases:
|
|
- "should create record rule successfully"
|
|
- "should edit record rule successfully"
|
|
- "should delete record rule with confirmation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede crear reglas de filtro por rol"
|
|
status: Pending
|
|
test_reference: "test/auth/record-rule.controller.e2e-spec.ts:32"
|
|
- id: AC-002
|
|
description: "Reglas se aplican automáticamente en queries SQL"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/record-rule.service.spec.ts:68"
|
|
- id: AC-003
|
|
description: "Múltiples reglas se combinan con OR"
|
|
status: Pending
|
|
test_reference: "src/modules/auth/services/record-rule.service.spec.ts:92"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Record rule debe tener SQL válido"
|
|
implementation: "src/modules/auth/services/record-rule.service.ts:validateSql()"
|
|
test_reference: "src/modules/auth/services/record-rule.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Reglas se aplican por modelo (tabla)"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:record_rules.model"
|
|
test_reference: "src/modules/auth/services/record-rule.service.spec.ts:72"
|
|
- id: RN-003
|
|
description: "Usuario sin reglas no ve ningún registro (restrictivo por defecto)"
|
|
implementation: "src/modules/auth/services/record-rule.service.ts:applyRulesForUser()"
|
|
test_reference: "src/modules/auth/services/record-rule.service.spec.ts:105"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-002
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "pg"
|
|
version: "^8.11.0"
|
|
|
|
coverage:
|
|
rf_to_et_backend: 100%
|
|
rf_to_et_frontend: 100%
|
|
rf_to_database: 100%
|
|
rf_to_tests: 100%
|
|
backend_tests: 100%
|
|
frontend_tests: 100%
|
|
|
|
statistics:
|
|
total_endpoints: 38
|
|
total_components: 32
|
|
total_tables: 10
|
|
total_test_cases: 94
|
|
estimated_duration_sprints: 4
|