🛠 Unidad ISS-23 · CRUD de la matriz — capa 🛠 CONSTRUIR
Capa Página Para qué 🧠 Aprender — no está en la fuente comprender, explicar y relacionar 🛠 Construir esta página ejecutar, programar y verificar ✅ GATE Condiciones de cierre condición para pasar al bloque siguiente Esta es la guía ejecutable. El cuerpo de abajo es el ISS técnico verbatim: comandos, rutas, versiones, verificaciones y criterios, sin simplificar.
Guía de estudio. Esta unidad no tiene cuaderno en
material/django/iss/ISS-23/aprendizaje/. La página publica solo el ISS técnico.
ISS-23 — CRUD de la matriz
Objetivo
Administrar la matriz y usarla para autorizar: el CRUD crea cada fila, RoleUser asigna el rol al usuario y ResourceRole asigna el recurso al rol. Con los dos enlaces activos, ese usuario puede ejecutar la ruta concedida.
Requisitos
- ISS-22 superado.
Construcción
SoftDeleteModelViewSet es un ModelViewSet con baja lógica y filtro ?status=. De él heredan UserViewSet, RoleViewSet, RoleUserViewSet, ResourceViewSet y ResourceRoleViewSet.
UserSerializer.password es write_only. create y update llaman set_password(). validate_password usa los validadores de Django y traduce ValidationError de Django al de DRF.
El CRUD se agrega al urls.py que ya tiene login, refresh, logout y perfil. No se crea otro archivo ni otro include.
ARCHIVO: apps/security/urls.py
AGREGAR:
from rest_framework.routers import SimpleRouter
y los viewsets de rbac_views.
DEBAJO DE los path("auth/...") y ANTES de cerrar el módulo:
router = SimpleRouter(use_regex_path=False)
router.register("users", UserViewSet, basename="user")
router.register("roles", RoleViewSet, basename="role")
router.register("role-users", RoleUserViewSet, basename="role-user")
router.register("resources", ResourceViewSet, basename="resource")
router.register("resource-roles", ResourceRoleViewSet, basename="resource-role")
urlpatterns += router.urls
urlpatterns += router.urls concatena las diez rutas del CRUD (lista y detalle de cada recurso) después de las cuatro de auth/. El prefijo sigue siendo el api/ del include del ISS-17. auth/profile/ queda fuera del router a propósito.
Estos endpoints son JWT con token + RBAC. Crear un rol no es OPEN ni basta con traer el JWT: hace falta la concesión. La prueba nace aquí, ya con grant_crud: no hubo una versión abierta que parchear.
ARCHIVO: apps/security/tests.py
AL FINAL DEL ARCHIVO, AGREGAR la clase SecurityMatrixApiTests.
Su setUp concede el CRUD de /api/users/, /api/roles/, /api/role-users/,
/api/resources/ y /api/resource-roles/, y llama force_authenticate.
test_role_crud cubre alta, lista, detalle, PATCH y baja lógica del rol.
test_user_role_resource_chain no se queda en el alta. Después de cada eslabón
entra como el usuario nuevo a GET /api/clients/:
sin rol es 403, con RoleUser y sin recurso es 403, con el recurso suelto es 403,
y con ResourceRole active es 200. POST /api/clients/ sigue en 403: solo se concedió GET.
El método del recurso se envía en mayúsculas: las choices no aceptan "get".
La route es el patrón api/clients/, el mismo que devuelve el resolver.
Explicación
El CRUD de cada tabla es homogéneo: listar, crear, leer, actualizar y dar de baja. Por eso es ModelViewSet. La contraseña cabe en el serializer: es write_only y pasa por set_password.
Las tablas role_users y resource_roles no son un CRUD más. RoleUser dice qué rol tiene el usuario. ResourceRole dice qué método y qué ruta puede ese rol. HasResourceAccess exige los dos enlaces, y el rol, el recurso y la concesión, en active. Un usuario creado por aquí nace inactive si el cuerpo no envía status. Hasta que alguien lo active, login responde 401.
Cómo probarlo
Capa HTTP, acceso JWT con token + RBAC. El setUp solo abre la matriz, para poder escribirla. La prueba de negocio usa otro usuario, el que el propio POST acaba de crear, y no llama a grant() para él.
python manage.py test apps.security.tests.SecurityMatrixApiTests
│
├── roles POST GET GET/<id> PATCH DELETE la fila queda inactive
│
└── asignación y RBAC
POST /api/roles/ bodega active
POST /api/users/ bodega1 active; password no vuelve
GET /api/clients/ como bodega1 403
POST /api/role-users/ rol bodega → bodega1
GET /api/clients/ como bodega1 403 falta el recurso
POST /api/resources/ GET api/clients/
GET /api/clients/ como bodega1 403 falta ResourceRole
POST /api/resource-roles/ ese recurso → rol bodega
GET /api/clients/ como bodega1 200
POST /api/clients/ como bodega1 403 no se concedió POST
User ── RoleUser ── Role ── ResourceRole ── Resource
bodega1 bodega GET api/clients/
│
▼
HasResourceAccess
│
└── los cuatro eslabones active → la ruta responde
En Swagger el mismo armado se hace en el ISS-24, con el rol admin ya sembrado para poder llamar a la matriz. Aquí el HTTP crea la concesión y enseguida la usa.
Criterios de aceptación
- AC-23-01: el serializer no devuelve
password. - AC-23-02: la contraseña pasa por
set_password, no se asigna en claro. - AC-23-03: DELETE de estas entidades no borra la fila.
- AC-23-04: asignar el rol con
RoleUser, sinResourceRole, dejaGET /api/clients/en 403. - AC-23-05: con
ResourceRoleactive deGET api/clients/, ese GET responde 200 y el POST sigue en 403.
Verificación
SecurityMatrixApiTests publica el usuario, asigna el rol, concede solo GET api/clients/ y entra otra vez como ese usuario. Antes del ResourceRole el GET es 403. Después es 200. El POST de clientes sigue en 403.
Evidencias
- EVI-23-01:
password = serializers.CharField(write_only=True). - EVI-23-02:
set_passwordencreateyupdate.
GATE
| AC | Verificación | Evidencia | Resultado |
|---|---|---|---|
| AC-23-01 | write_only | EVI-23-01 | PASS |
| AC-23-02 | set_password | EVI-23-02 | PASS |
| AC-23-03 | perform_destroy | rbac_views.py |
PASS |
| AC-23-04 | GET clientes sin ResourceRole | test de la cadena | PASS |
| AC-23-05 | GET 200 y POST 403 | test de la cadena | PASS |
✅ GATE de la unidad ISS-23 — este bloque no añade ningún criterio nuevo.
Las condiciones de cierre son las de esta misma página:
- 📋 Criterios de aceptación → Criterios de aceptación
- ✅ GATE → GATE
- 🔎 Verificación → Verificación
Con el GATE en verde queda habilitado el bloque siguiente de la ruta.
Navegación de la ruta: ← ISS-22 · 🛠 Construir · ↑ Ruta Django · → ISS-24 · 🛠 Construir