From cba32e90e7ab3b44dbe26a4a4ae389cf5344de67 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 28 Jul 2026 14:58:58 -0300 Subject: [PATCH] feat(authorization): implement AuthorizationSeeder with roles and permissions, add tests --- database/seeders/AuthorizationSeeder.php | 173 ++++++++++++++++++ database/seeders/DatabaseSeeder.php | 1 + docs/authorization.md | 85 +++++++++ .../Seeders/AuthorizationSeederTest.php | 49 +++++ 4 files changed, 308 insertions(+) create mode 100644 database/seeders/AuthorizationSeeder.php create mode 100644 docs/authorization.md create mode 100644 tests/Feature/Seeders/AuthorizationSeederTest.php diff --git a/database/seeders/AuthorizationSeeder.php b/database/seeders/AuthorizationSeeder.php new file mode 100644 index 0000000..3c18bbd --- /dev/null +++ b/database/seeders/AuthorizationSeeder.php @@ -0,0 +1,173 @@ + + */ + private const PERMISSIONS = [ + 'tenant.ver' => [ + 'nombre' => 'Ver configuración del tenant', + 'descripcion' => 'Permite consultar la configuración general de un tenant.', + ], + 'tenant.editar' => [ + 'nombre' => 'Editar configuración del tenant', + 'descripcion' => 'Permite modificar los datos generales, dominio, colores y recursos visuales del tenant.', + ], + 'usuarios.ver' => [ + 'nombre' => 'Ver usuarios', + 'descripcion' => 'Permite consultar los usuarios asociados al tenant.', + ], + 'usuarios.gestionar' => [ + 'nombre' => 'Gestionar usuarios', + 'descripcion' => 'Permite invitar, editar, desactivar y asignar roles permitidos a los usuarios del tenant.', + ], + 'roles.ver' => [ + 'nombre' => 'Ver roles', + 'descripcion' => 'Permite consultar los roles y sus permisos.', + ], + 'roles.gestionar' => [ + 'nombre' => 'Gestionar roles', + 'descripcion' => 'Permite crear, modificar y asignar permisos a roles.', + ], + 'catalogo.ver' => [ + 'nombre' => 'Ver catálogo', + 'descripcion' => 'Permite consultar administrativamente productos, variantes, categorías, marcas y bundles.', + ], + 'catalogo.gestionar' => [ + 'nombre' => 'Gestionar catálogo', + 'descripcion' => 'Permite crear, modificar, archivar y eliminar productos, variantes, categorías, marcas y bundles.', + ], + 'inventario.ver' => [ + 'nombre' => 'Ver inventario', + 'descripcion' => 'Permite consultar el stock y la información de inventario.', + ], + 'inventario.ajustar' => [ + 'nombre' => 'Ajustar inventario', + 'descripcion' => 'Permite modificar el stock disponible.', + ], + 'pedidos.ver' => [ + 'nombre' => 'Ver pedidos', + 'descripcion' => 'Permite consultar los pedidos del tenant.', + ], + 'pedidos.gestionar' => [ + 'nombre' => 'Gestionar pedidos', + 'descripcion' => 'Permite modificar estados y datos operativos de los pedidos.', + ], + 'pedidos.cancelar' => [ + 'nombre' => 'Cancelar pedidos', + 'descripcion' => 'Permite cancelar pedidos y liberar sus reservas de inventario.', + ], + 'pagos.ver' => [ + 'nombre' => 'Ver pagos', + 'descripcion' => 'Permite consultar el estado y la información de los pagos.', + ], + 'pagos.gestionar' => [ + 'nombre' => 'Gestionar pagos', + 'descripcion' => 'Permite confirmar, rechazar o devolver pagos.', + ], + 'tickets.ver' => [ + 'nombre' => 'Ver tickets', + 'descripcion' => 'Permite consultar los tickets emitidos.', + ], + 'tickets.gestionar' => [ + 'nombre' => 'Gestionar tickets', + 'descripcion' => 'Permite emitir, invalidar o regenerar tickets.', + ], + 'contenido.gestionar' => [ + 'nombre' => 'Gestionar contenido', + 'descripcion' => 'Permite administrar menús, carruseles, destacados y redes sociales.', + ], + 'integraciones.ver' => [ + 'nombre' => 'Ver integraciones', + 'descripcion' => 'Permite consultar la configuración y el estado de las integraciones.', + ], + 'integraciones.gestionar' => [ + 'nombre' => 'Gestionar integraciones', + 'descripcion' => 'Permite configurar credenciales y proveedores de integraciones.', + ], + 'reportes.ver' => [ + 'nombre' => 'Ver reportes', + 'descripcion' => 'Permite consultar reportes de ventas y métricas.', + ], + 'reportes.exportar' => [ + 'nombre' => 'Exportar reportes', + 'descripcion' => 'Permite descargar reportes e información comercial.', + ], + ]; + + /** + * @var array}> + */ + private const ROLES = [ + 'admin' => [ + 'nombre' => 'Administrador general', + 'descripcion' => 'Administra la plataforma y todos sus tenants.', + 'permisos' => [], + ], + 'tenant_admin' => [ + 'nombre' => 'Administrador del tenant', + 'descripcion' => 'Administra la operación de su propio tenant.', + 'permisos' => [ + 'tenant.ver', + 'tenant.editar', + 'usuarios.ver', + 'usuarios.gestionar', + 'roles.ver', + 'catalogo.ver', + 'catalogo.gestionar', + 'inventario.ver', + 'inventario.ajustar', + 'pedidos.ver', + 'pedidos.gestionar', + 'pedidos.cancelar', + 'pagos.ver', + 'pagos.gestionar', + 'tickets.ver', + 'tickets.gestionar', + 'contenido.gestionar', + 'integraciones.ver', + 'integraciones.gestionar', + 'reportes.ver', + 'reportes.exportar', + ], + ], + 'user' => [ + 'nombre' => 'Usuario', + 'descripcion' => 'Cliente final limitado a sus propios datos y operaciones.', + 'permisos' => [], + ], + ]; + + public function run(): void + { + foreach (self::PERMISSIONS as $code => $attributes) { + Permission::query()->updateOrCreate( + ['codigo' => $code], + $attributes + ); + } + + foreach (self::ROLES as $code => $attributes) { + $role = Role::query()->updateOrCreate( + ['codigo' => $code], + [ + 'nombre' => $attributes['nombre'], + 'descripcion' => $attributes['descripcion'], + ] + ); + + $permissionCodes = $code === 'admin' + ? array_keys(self::PERMISSIONS) + : $attributes['permisos']; + + $role->permissions()->sync($permissionCodes); + } + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 64d8861..a13d0ec 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -24,6 +24,7 @@ class DatabaseSeeder extends Seeder $this->call([ WebsiteTypeSeeder::class, + AuthorizationSeeder::class, SocialMediaSeeder::class, TenantSeeder::class, AttributeSeeder::class, diff --git a/docs/authorization.md b/docs/authorization.md new file mode 100644 index 0000000..19e5784 --- /dev/null +++ b/docs/authorization.md @@ -0,0 +1,85 @@ +# Roles y permisos + +El sistema utiliza control de acceso basado en roles. Los códigos son identificadores +estables para uso interno; los nombres y las descripciones pueden modificarse sin +afectar las reglas de autorización. + +Los permisos administrativos deben evaluarse siempre dentro del tenant correspondiente. +El rol `admin` es la única excepción porque su alcance es toda la plataforma. + +## Roles + +| Código | Nombre | Alcance | +| --- | --- | --- | +| `admin` | Administrador general | Administra la plataforma, todos los tenants, roles y permisos. Tiene todos los permisos. | +| `tenant_admin` | Administrador del tenant | Administra usuarios y la operación de su tenant. No puede gestionar la definición de roles. | +| `user` | Usuario | Cliente final. No tiene permisos administrativos y solamente puede operar sobre sus propios datos, carrito, pedidos y tickets. | + +## Permisos + +| Código | Nombre | Descripción | +| --- | --- | --- | +| `tenant.ver` | Ver configuración del tenant | Consultar la configuración general de un tenant. | +| `tenant.editar` | Editar configuración del tenant | Modificar datos generales, dominio, colores y recursos visuales. | +| `usuarios.ver` | Ver usuarios | Consultar los usuarios asociados al tenant. | +| `usuarios.gestionar` | Gestionar usuarios | Invitar, editar, desactivar y asignar roles permitidos a usuarios del tenant. | +| `roles.ver` | Ver roles | Consultar los roles y sus permisos. | +| `roles.gestionar` | Gestionar roles | Crear, modificar y asignar permisos a roles. | +| `catalogo.ver` | Ver catálogo | Consultar productos, variantes, categorías, marcas y bundles administrativamente. | +| `catalogo.gestionar` | Gestionar catálogo | Crear, modificar, archivar y eliminar productos, variantes, categorías, marcas y bundles. | +| `inventario.ver` | Ver inventario | Consultar stock e información de inventario. | +| `inventario.ajustar` | Ajustar inventario | Modificar el stock disponible. | +| `pedidos.ver` | Ver pedidos | Consultar los pedidos del tenant. | +| `pedidos.gestionar` | Gestionar pedidos | Modificar estados y datos operativos de pedidos. | +| `pedidos.cancelar` | Cancelar pedidos | Cancelar pedidos y liberar sus reservas de inventario. | +| `pagos.ver` | Ver pagos | Consultar el estado y la información de pagos. | +| `pagos.gestionar` | Gestionar pagos | Confirmar, rechazar o devolver pagos. | +| `tickets.ver` | Ver tickets | Consultar tickets emitidos. | +| `tickets.gestionar` | Gestionar tickets | Emitir, invalidar o regenerar tickets. | +| `contenido.gestionar` | Gestionar contenido | Administrar menús, carruseles, destacados y redes sociales. | +| `integraciones.ver` | Ver integraciones | Consultar la configuración y el estado de integraciones. | +| `integraciones.gestionar` | Gestionar integraciones | Configurar credenciales y proveedores de integraciones. | +| `reportes.ver` | Ver reportes | Consultar reportes de ventas y métricas. | +| `reportes.exportar` | Exportar reportes | Descargar reportes e información comercial. | + +## Matriz de asignación + +| Permiso | `admin` | `tenant_admin` | `user` | +| --- | :---: | :---: | :---: | +| `tenant.ver` | Sí | Sí | No | +| `tenant.editar` | Sí | Sí | No | +| `usuarios.ver` | Sí | Sí | No | +| `usuarios.gestionar` | Sí | Sí | No | +| `roles.ver` | Sí | Sí | No | +| `roles.gestionar` | Sí | No | No | +| `catalogo.ver` | Sí | Sí | No | +| `catalogo.gestionar` | Sí | Sí | No | +| `inventario.ver` | Sí | Sí | No | +| `inventario.ajustar` | Sí | Sí | No | +| `pedidos.ver` | Sí | Sí | No | +| `pedidos.gestionar` | Sí | Sí | No | +| `pedidos.cancelar` | Sí | Sí | No | +| `pagos.ver` | Sí | Sí | No | +| `pagos.gestionar` | Sí | Sí | No | +| `tickets.ver` | Sí | Sí | No | +| `tickets.gestionar` | Sí | Sí | No | +| `contenido.gestionar` | Sí | Sí | No | +| `integraciones.ver` | Sí | Sí | No | +| `integraciones.gestionar` | Sí | Sí | No | +| `reportes.ver` | Sí | Sí | No | +| `reportes.exportar` | Sí | Sí | No | + +## Carga inicial + +El seeder `Database\Seeders\AuthorizationSeeder` crea o actualiza este catálogo y +sincroniza sus asignaciones. Puede ejecutarse de manera independiente: + +```bash +php artisan db:seed --class=AuthorizationSeeder +``` + +También se ejecuta como parte de `DatabaseSeeder`. + +Las acciones del rol `user` no se representan como permisos administrativos. El +acceso al perfil, carrito, pedidos y tickets se autoriza comprobando que el recurso +pertenezca al usuario autenticado y al tenant actual. diff --git a/tests/Feature/Seeders/AuthorizationSeederTest.php b/tests/Feature/Seeders/AuthorizationSeederTest.php new file mode 100644 index 0000000..32ff411 --- /dev/null +++ b/tests/Feature/Seeders/AuthorizationSeederTest.php @@ -0,0 +1,49 @@ +seed(AuthorizationSeeder::class); + + $this->assertSame( + ['admin', 'tenant_admin', 'user'], + Role::query()->orderBy('codigo')->pluck('codigo')->all() + ); + $this->assertCount(22, Permission::query()->get()); + } + + public function test_it_assigns_the_expected_permissions_to_each_role(): void + { + $this->seed(AuthorizationSeeder::class); + + $admin = Role::query()->where('codigo', 'admin')->firstOrFail(); + $tenantAdmin = Role::query()->where('codigo', 'tenant_admin')->firstOrFail(); + $user = Role::query()->where('codigo', 'user')->firstOrFail(); + + $this->assertCount(22, $admin->permissions); + $this->assertCount(21, $tenantAdmin->permissions); + $this->assertFalse($tenantAdmin->permissions->contains('codigo', 'roles.gestionar')); + $this->assertCount(0, $user->permissions); + } + + public function test_it_can_run_more_than_once_without_creating_duplicates(): void + { + $this->seed(AuthorizationSeeder::class); + $this->seed(AuthorizationSeeder::class); + + $this->assertCount(3, Role::query()->get()); + $this->assertCount(22, Permission::query()->get()); + $this->assertDatabaseCount('roles_permisos', 43); + } +}