Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/corpentunida-org/corpen/llms.txt

Use this file to discover all available pages before exploring further.

Corpen uses Spatie Laravel Permission (v6.9) for role-based access control across all modules. Every route in Corpen that requires a specific module access — insurance policies, claims, reservations, credits, archive management, and more — is guarded either by a candirect middleware check (direct permissions) or by Spatie’s can middleware (role permissions). This page explains the full access control model and how to manage roles and permissions through the admin panel.

Package and Database Tables

The spatie/laravel-permission package is installed via Composer and creates four tables during the 2024_11_05_135024_create_permission_tables.php migration:
TablePurpose
permissionsAll named permissions (e.g. seguros.poliza.index)
rolesAll roles (e.g. admin, seguros, creditos)
model_has_permissionsDirect permission → User assignments
role_has_permissionsPermission → Role assignments
An additional actions table (migration 2024_10_21_141315_create_actions_table.php) stores the User → Role pivot used by Corpen’s custom Role model relationship.

User Model Integration

The App\Models\User model uses the HasRoles trait from Spatie, giving it the full Spatie permission API:
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // Checks whether a permission is assigned directly to the user (model_has_permissions)
    public function hasPermission($permiso)
    {
        return $this->permissions()
            ->where('name', $permiso)
            ->exists();
    }

    // Convenience wrapper around Spatie's getDirectPermissions()
    public function hasDirectPermission($permiso)
    {
        return $this->getDirectPermissions()
            ->pluck('name')
            ->contains($permiso);
    }
}
The CanDirect middleware calls $user->getDirectPermissions() directly (the Spatie HasRoles method) to retrieve permissions stored in model_has_permissions. The custom hasDirectPermission() wrapper on User is a convenience helper available elsewhere in the codebase but is not invoked by the middleware itself.

The CanDirect Middleware

Corpen defines a custom middleware at app/Http/Middleware/CanDirect.php that checks whether the authenticated user has a direct permission matching the permission name passed to the middleware:
class CanDirect
{
    public function handle(Request $request, Closure $next, ...$permissions)
    {
        $user = Auth::user();
        $directPermissions = $user->getDirectPermissions()->pluck('name')->toArray();

        foreach ($permissions as $permission) {
            if (in_array($permission, $directPermissions)) {
                return $next($request);
            }
        }

        abort(404, 'Pagina no encontrada.');
    }
}
The middleware accepts one or more permission names as arguments and passes the request if any of them match. If none match, it aborts with a 404 response (rather than 403) to avoid revealing whether a resource exists.
The CanDirect middleware aborts with 404, not 403. This is by design — it prevents enumeration of protected resources. Users without the required direct permission will see a “Page not found” response, not an “Access denied” page. Ensure your admin documentation reflects this so operators are not confused when auditing access denials.

Middleware Usage in Routes

The middleware alias candirect is applied across all sensitive routes:
// Insurance module
Route::resource('poliza', SegPolizaController::class)
    ->names('seguros.poliza')
    ->middleware(['auth', 'candirect:seguros.poliza.index']);

Route::resource('reclamacion', SegReclamacionesController::class)
    ->names('seguros.reclamacion')
    ->middleware(['auth', 'candirect:seguros.reclamacion.index']);

Route::resource('convenio', SegConvenioController::class)
    ->names('seguros.convenio')
    ->middleware(['auth', 'candirect:seguros.convenio.index']);

Route::resource('beneficios', SegBeneficiosController::class)
    ->names('seguros.beneficios')
    ->middleware(['auth', 'candirect:seguros.beneficios.index']);

// Admin module
Route::resource('users', UserController::class)
    ->names('admin.users')
    ->middleware(['auth', 'candirect:admin.users.index']);

// Cinco / Savings module
Route::resource('terceros', TercerosController::class)
    ->names('cinco.tercero')
    ->middleware(['auth', 'candirect:cinco.tercero.index']);

// Support module
Route::resource('soportes', ScpSoporteController::class)
    ->middleware('candirect:soporte.lista.administrador');

// Correspondence module
Route::resource('usuarios', ScpUsuarioController::class)
    ->middleware('candirect:correspondencia.usuario.admin');

Default Roles

The RoleSeeder creates five default roles when you run php artisan db:seed --class=RoleSeeder:
Role::create(['name' => 'admin']);
Role::create(['name' => 'exequial']);
Role::create(['name' => 'creditos']);
Role::create(['name' => 'seguros']);
Role::create(['name' => 'read']);
RoleIntended use
adminFull platform access, user and role management
exequialFuneral services module
creditosCredit management module
segurosInsurance policies, claims, and benefits
readRead-only viewer across modules

Permission Naming Convention

Corpen permission names follow the pattern {module}.{resource}.{action}. Examples from production routes:
Permission NameGuards Access To
admin.users.indexUser management panel
admin.auditoria.indexAudit log
seguros.poliza.indexInsurance policies list
seguros.reclamacion.indexInsurance claims list
seguros.convenio.indexInsurance agreements
seguros.beneficios.indexInsurance benefits
cinco.tercero.indexSavings module — third parties
cinco.movcontables.indexSavings accounting movements
cinco.retiros.indexSavings withdrawals
reservas.Reserva.pagosReservation payments
reservas.reserva.confirmadasConfirmed reservations
reservas.inmueble.activeActive properties
soporte.lista.administradorSupport admin panel
correspondencia.usuario.adminCorrespondence admin tools

Managing Roles and Permissions via the Admin Panel

Corpen provides a web-based admin interface for creating permissions, managing roles, and assigning permissions to roles.

Admin Panel Routes

// User management
Route::resource('users', UserController::class)
    ->names('admin.users');

// Role management
Route::resource('roles', RoleController::class)
    ->names('admin.roles');

// Permission management
Route::resource('permisos', PermissionsController::class)
    ->names('admin.permisos');
URLControllerPurpose
/usersAdmin\UserControllerList users, assign/remove roles
/rolesAdmin\RoleControllerList roles, attach/detach permissions
/permisos/createAdmin\PermissionsControllerCreate a new named permission

How Role → Permission Assignment Works

RoleController@update syncs permissions for a given role by computing the diff between existing and submitted permission IDs:
public function update(Request $request, Role $role)
{
    $currentPermissions = $role->permissions()->pluck('id')->toArray();
    $permissions = $request->input('permissions', []);

    $permissionsToAdd    = array_diff($permissions, $currentPermissions);
    $permissionsToRemove = array_diff($currentPermissions, $permissions);

    if (!empty($permissionsToAdd)) {
        $role->permissions()->attach($permissionsToAdd);
    }
    if (!empty($permissionsToRemove)) {
        $role->permissions()->detach($permissionsToRemove);
    }

    return redirect()->back()->with('success', 'Permisos actualizados al rol correctamente.');
}

Full Workflow: Creating and Assigning Access

1
Create the permission
2
Navigate to /permisos/create in the admin panel, or create it programmatically using Spatie’s Permission model:
3
use Spatie\Permission\Models\Permission;

Permission::create([
    'name'       => 'seguros.poliza.index',
    'guard_name' => 'web',
]);
4
Permission names must match the string passed to the candirect or can middleware on the corresponding route exactly.
5
Create a role (if it doesn’t exist)
6
Navigate to /roles in the admin panel and submit the role creation form, or create it programmatically:
7
use Spatie\Permission\Models\Role;

$role = Role::create([
    'name'       => 'seguros',
    'guard_name' => 'web',
]);
8
Corpen’s RoleController@store automatically lowercases the role name before saving.
9
Assign the permission to the role
10
In the admin panel at /roles, select a role and check the desired permissions in the permission matrix form. Programmatically:
11
$role = Role::findByName('seguros');
$role->givePermissionTo('seguros.poliza.index');
$role->givePermissionTo('seguros.reclamacion.index');
$role->givePermissionTo('seguros.convenio.index');
$role->givePermissionTo('seguros.beneficios.index');
12
Assign the role to a user
13
In the admin panel at /users, select a user and attach the role. Programmatically, using the Spatie API on the User model:
14
$user = User::find($userId);

// Assign a role
$user->assignRole('seguros');

// Or assign a direct permission (checked by CanDirect middleware)
$user->givePermissionTo('seguros.poliza.index');
15
The CanDirect middleware checks direct permissions (model_has_permissions table), not role-based permissions. If you only assign a role to a user — without also assigning the individual permission directly — the candirect middleware will still deny access. Use $user->givePermissionTo(...) to assign direct permissions, or use the admin panel’s user permission form.

Programmatic Permission Reference

use App\Models\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

// Check if a user has a direct permission
$user->hasDirectPermission('seguros.poliza.index'); // true/false

// Check via the custom User model method
$user->hasPermission('seguros.reclamacion.index'); // queries model_has_permissions

// Assign role
$user->assignRole('admin');

// Remove role
$user->removeRole('read');

// Give direct permission
$user->givePermissionTo('admin.users.index');

// Revoke direct permission
$user->revokePermissionTo('admin.users.index');

// Sync permissions on a role
$role = Role::findByName('seguros');
$role->syncPermissions([
    'seguros.poliza.index',
    'seguros.reclamacion.index',
    'seguros.convenio.index',
    'seguros.beneficios.index',
]);

// Get all direct permissions for a user
$permissions = $user->getDirectPermissions()->pluck('name');
// e.g. ["seguros.poliza.index", "seguros.reclamacion.index"]

Model Relationships

The App\Models\Permisos model (backed by the permissions table) exposes the following relationships:
// All roles that have this permission
$permiso->roles;            // belongsToMany via role_has_permissions

// The primary role this permission was created for
$permiso->rolePermission;   // belongsTo Role via role_id
The App\Models\Role model exposes:
// Users associated with this role (via actions pivot table)
$role->users;

// All permissions attached to this role (via role_has_permissions)
$role->permissions;

// Permissions where this role is the primary role
$role->permissionsRole;

Build docs developers (and LLMs) love