feat: Permission System Hướng B - Models, Command, User can(), session cache

This commit is contained in:
2026-04-29 08:25:37 +00:00
parent d2df9edd69
commit 40b75fcf75
15 changed files with 354 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class PermissionModule extends Model
{
use HasUuids, HasFactory;
protected $fillable = ['module', 'label', 'actions'];
protected $casts = [
'actions' => 'array',
];
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class RoleTemplate extends Model
{
use HasUuids, HasFactory;
protected $fillable = ['name', 'description', 'permissions', 'is_active'];
protected $casts = [
'permissions' => 'array',
'is_active' => 'boolean',
];
public function users()
{
return $this->hasMany(User::class);
}
}

View File

@@ -29,9 +29,83 @@ class User extends Authenticatable implements FilamentUser
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'extra_permissions' => 'array',
'excluded_permissions' => 'array',
];
}
public function roleTemplate()
{
return $this->belongsTo(RoleTemplate::class);
}
/**
* Tính toán effective permissions từ role template + extra - excluded.
* Cache trong session (1 lần/login).
*/
public function getEffectivePermissions(): array
{
$cacheKey = "user.{$this->id}.permissions";
if (session()->has($cacheKey)) {
return session()->get($cacheKey);
}
$permissions = $this->calculateEffectivePermissions();
session()->put($cacheKey, $permissions);
return $permissions;
}
public function hasEffectivePermission(string $permission): bool
{
return in_array($permission, $this->getEffectivePermissions());
}
public function clearPermissionCache(): void
{
session()->forget("user.{$this->id}.permissions");
}
protected function calculateEffectivePermissions(): array
{
$templatePerms = [];
if ($this->roleTemplate) {
$templatePerms = $this->roleTemplate->permissions ?? [];
}
// Flatten template permissions từ {"contracts":["view","create"]} thành ["contracts.view","contracts.create"]
$templateFlat = [];
foreach ($templatePerms as $module => $actions) {
foreach ($actions as $action) {
$templateFlat[] = "{$module}.{$action}";
}
}
$extra = $this->extra_permissions ?? [];
$excluded = $this->excluded_permissions ?? [];
return array_values(array_diff(
array_unique(array_merge($templateFlat, $extra)),
$excluded
));
}
/**
* Override can() để tích hợp với Laravel Authorization.
* Nếu ability dạng "contracts.view" dùng effective permissions.
* Ngược lại fallback về parent.
*/
public function can($abilities, $arguments = []): bool
{
if (is_string($abilities) && str_contains($abilities, '.')) {
return $this->hasEffectivePermission($abilities);
}
return parent::can($abilities, $arguments);
}
public function canAccessPanel(Panel $panel): bool
{
return true;