Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 4d23199

Browse files
committed
feat: Enhance role and permission management
- Added `revokeAllPermissions()` to HasPermissions trait - Added `hasPermissionTo()` to HasPermissions trait - Added `syncRolesWithoutDetaching()` to HasRoles trait - Added `revokeRoles()` and `getRoleNames()` to HasRoles trait - Improved type handling in `hasRole()` and related methods - Updated model configurations and cache settings - Refactored permission and role creation commands
1 parent 7c53d0a commit 4d23199

24 files changed

Lines changed: 921 additions & 307 deletions

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,52 @@
22

33
All notable changes to `guard-laravel` will be documented in this file.
44

5+
## v1.1.0 - 2026-01-30
6+
7+
### 🔨 Breaking Changes
8+
9+
- Removed deprecated `detach()` usage in `syncRoles()` method
10+
- Changed return type of `assignRole()` from `Model` to `self`
11+
- Changed `revokeRole()` return type from `int` to `int` (number of roles remaining)
12+
13+
### ✨ New Features
14+
15+
- **HasPermissions Trait**:
16+
- Added `revokeAllPermissions()` method to revoke all permissions
17+
- Added `hasPermissionTo()` method to check if role has a permission
18+
19+
- **HasRoles Trait**:
20+
- Added `syncRolesWithoutDetaching()` method to sync roles without detaching
21+
- Added `revokeRole()` method to revoke a single role
22+
- Added `revokeRoles()` method to revoke all roles
23+
- Added `getRoleNames()` method to get all role names
24+
- Improved `hasRole()` to handle Collection types
25+
- Improved `hasAllRoles()` and `hasAnyRole()` to handle Collection types
26+
- Simplified wildcard matching using `contains()` with closure
27+
28+
- **Models**:
29+
- Removed outdated `static $table` pattern from `Permission` and `Role` models
30+
- Now uses Laravel's built-in `getTable()` with config fallback
31+
32+
- **Service Provider**:
33+
- Fixed `permissionsTableExists()` to use config table name
34+
- Improved `registerModelObservers()` readability using `->each()`
35+
- Improved `defineGatePermissions()` and `defineGateRoles()` readability
36+
37+
### 📚 Documentation
38+
39+
- Added comprehensive Laravel-style documentation comments to config file
40+
- Added docblocks to all methods in Service Provider
41+
- Updated configuration documentation with type casting examples
42+
- Updated Contributing guide with correct script names
43+
- Updated code examples for new methods
44+
45+
### 🛠️ Code Quality
46+
47+
- Improved type hints throughout codebase
48+
- Added proper exception handling documentation
49+
- Refactored code for better readability and maintainability
50+
551
## v1.0.0 - 2025-01-01
652

753
### 🎉 Initial Stable Release

CLAUDE.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Guard Laravel - Claude Code Guidelines
2+
3+
## Development Commands
4+
5+
```bash
6+
# Format code with Laravel Pint
7+
composer lint
8+
9+
# Check code style without modifying files
10+
composer lint:check
11+
12+
# Run static analysis with Larastan
13+
composer analyse
14+
15+
# Run code refactoring with Rector
16+
composer refactor
17+
18+
# Check Rector dry-run
19+
composer refactor:check
20+
21+
# Run Pest tests
22+
composer test
23+
24+
# Run tests with coverage
25+
composer test-coverage
26+
```
27+
28+
## Architecture Overview
29+
30+
### Package Purpose
31+
32+
Guard is a modern Role and Permission management system for Laravel 10, 11, and 12 with PHP 8.2-8.5 support. It provides comprehensive ACL functionality with wildcard permissions, caching, and middleware protection.
33+
34+
### Key Components
35+
36+
#### Contracts (`src/Contracts/`)
37+
38+
Define the interface that models must implement:
39+
40+
- **User**: Interface for models using roles/permissions (methods: `hasRole()`, `hasPermission()`, `assignRole()`, `syncRoles()`, etc.)
41+
- **Role**: Interface for role models (methods: `getName()`, `permissions()`, `givePermissionTo()`, `syncPermissions()`, etc.)
42+
- **Permission**: Interface for permission models (methods: `getName()`, `roles()`, `isWildcard()`, `getGroup()`, `getType()`, etc.)
43+
44+
#### Models (`src/Models/`)
45+
46+
- **Role**: Eloquent model with `is_guarded` boolean for protecting system-critical roles
47+
- **Permission**: Eloquent model with wildcard detection and grouping support
48+
49+
#### Traits (`src/`)
50+
51+
- **HasRoles**: Primary trait for user models, implements all role management methods
52+
- **HasPermissions**: Trait for role models, implements all permission management methods
53+
54+
#### Service Provider (`src/GuardServiceProvider.php`)
55+
56+
- Registers models, commands, middleware
57+
- Defines Gates for authorization (`Gate::define()`)
58+
- Handles automatic cache invalidation via model observers
59+
- Validates configured models exist (skips user model during tests)
60+
61+
#### Middleware
62+
63+
- **RoleMiddleware**: Checks user has specific role
64+
- **PermissionMiddleware**: Checks user has specific permission
65+
- **RoleOrPermissionMiddleware**: Checks user has role OR permission
66+
67+
#### Commands
68+
69+
- `guard:create-role`: Create new roles with optional label and user assignment
70+
- `guard:create-permission`: Create new permissions with optional label and role assignment
71+
72+
### Laravel Integration
73+
74+
#### Service Provider Registration
75+
76+
Automatically registered via `extra.laravel.providers` in composer.json
77+
78+
#### Facade
79+
80+
```php
81+
use AmdadulHaq\Guard\Facades\Guard;
82+
```
83+
84+
#### Configuration
85+
86+
Published to `config/guard.php` with:
87+
88+
- `models`: User, Role, Permission model mappings
89+
- `tables`: Custom table names
90+
- `cache`: Cache duration settings
91+
- `middleware`: Middleware aliases
92+
- `wildcard`: Enable wildcard permissions
93+
94+
### Role-Permission Relationship
95+
96+
#### Database Structure
97+
98+
- **roles**: `id`, `name`, `label`, `description`, `is_guarded`, timestamps
99+
- **permissions**: `id`, `name`, `label`, `description`, `group`, `is_wildcard`, timestamps
100+
- **permission_role**: Pivot table for role-permission relationships
101+
- **role_user**: Pivot table for user-role relationships
102+
103+
#### Key Features
104+
105+
- Many-to-many relationships between roles and permissions
106+
- Many-to-many between users and roles
107+
- Wildcard permissions (`posts.*` matches `posts.create`, `posts.edit`, etc.)
108+
- Permission groups for organization
109+
- Guarded roles that can't be deleted
110+
111+
### Caching Mechanism
112+
113+
#### Automatic Cache Invalidation
114+
115+
- Cache keys defined in `CacheKey` enum (`guard_roles`, `guard_permissions`)
116+
- Cache cleared automatically on model save/deleted via observers
117+
118+
#### Manual Cache Clear
119+
120+
```php
121+
Guard::clearCache();
122+
```
123+
124+
### Wildcard Permissions System
125+
126+
#### Implementation
127+
128+
- Permissions ending with `*` are automatically marked as wildcards in the `booted()` static method
129+
- Wildcard matching uses pattern prefix comparison (e.g., `posts.*` matches `posts.create`)
130+
- Pattern format: `resource.action` where `action` can be `*`
131+
132+
#### Usage
133+
134+
```php
135+
// Create wildcard permission
136+
$permission = Permission::create(['name' => 'posts.*']);
137+
138+
// User with wildcard permission can access all matching permissions
139+
$user->hasPermission('posts.create'); // returns true
140+
```
141+
142+
### Contract Implementation Requirements
143+
144+
All contract interfaces must be implemented by their respective models. When implementing `UserContract`:
145+
146+
```php
147+
use AmdadulHaq\Guard\Contracts\User as UserContract;
148+
use AmdadulHaq\Guard\HasPermissions;
149+
use AmdadulHaq\Guard\HasRoles;
150+
151+
class User extends Authenticatable implements UserContract
152+
{
153+
use HasPermissions, HasRoles;
154+
}
155+
```
156+
157+
### Important Implementation Notes
158+
159+
#### Type Safety
160+
161+
- Uses PHP 8.2+ strict types on all files
162+
- Enums for `CacheKey` and `PermissionType`
163+
- Return types on all public methods
164+
- Collection type is `Illuminate\Support\Collection` for methods returning permission/role names
165+
166+
#### Exception Handling
167+
168+
- Custom exceptions in `src/Exceptions/`
169+
- `PermissionDeniedException` for failed middleware checks
170+
- `RoleDoesNotExistException` for missing roles
171+
- `PermissionDoesNotExistException` for missing permissions
172+
173+
#### Test Models
174+
175+
Test models in `tests/Models/User.php` must implement the same methods as the actual User model, including `getRoleNames()` and `getPermissionNames()`.
176+
177+
#### Guarded Roles
178+
179+
Protected roles (with `is_guarded = true`) cannot be deleted through standard operations. Use `isProtectedRole()` to check protection status.

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ Ensure your code follows Laravel Pint standards:
3030

3131
```bash
3232
# Check code style
33-
composer format:check
33+
composer lint:check
3434

3535
# Fix code style
36-
composer format
36+
composer lint
3737
```
3838

3939
## Static Analysis
@@ -49,7 +49,7 @@ composer analyse
4949
1. Create a new branch from `main`
5050
2. Write tests for your changes
5151
3. Ensure all tests pass (`composer test`)
52-
4. Ensure code style passes (`composer format:check`)
52+
4. Ensure code style passes (`composer lint:check`)
5353
5. Ensure static analysis passes (`composer analyse`)
5454
6. Submit a pull request with a clear description of changes
5555

README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ return [
127127
'permissions' => 'permissions',
128128
],
129129
'cache' => [
130-
'permissions_duration' => env('GUARD_PERMISSIONS_CACHE_DURATION', 3600),
131-
'roles_duration' => env('GUARD_ROLES_CACHE_DURATION', 3600),
132130
'enabled' => env('GUARD_CACHE_ENABLED', true),
131+
'roles_duration' => (int) env('GUARD_ROLES_CACHE_DURATION', 3600),
132+
'permissions_duration' => (int) env('GUARD_PERMISSIONS_CACHE_DURATION', 3600),
133133
],
134134
'middleware' => [
135135
'role' => 'role',
@@ -189,17 +189,30 @@ $user->hasPermission('posts.delete'); // true
189189
### Assigning Permissions to Roles
190190

191191
```php
192-
// Assign a single permission
192+
use AmdadulHaq\Guard\Models\Permission;
193+
194+
// Assign a single permission by model
193195
$role->givePermissionTo($permission);
196+
197+
// Assign a single permission by name
194198
$role->givePermissionTo('users.create');
195199

196200
// Sync multiple permissions (supports both IDs and names)
197201
$role->syncPermissions([$permission1->id, $permission2->id]);
198202
$role->syncPermissions(['users.create', 'users.edit', 'users.delete']);
199203

200-
// Revoke a permission
204+
// Sync without detaching existing permissions
205+
$role->syncRolesWithoutDetaching(['editor', 'moderator']);
206+
207+
// Revoke a specific permission
201208
$role->revokePermissionTo($permission);
202209
$role->revokePermissionTo('users.delete');
210+
211+
// Revoke all permissions
212+
$role->revokeAllPermissions();
213+
214+
// Check if role has a permission
215+
$role->hasPermissionTo('users.edit'); // true or false
203216
```
204217

205218
### Assigning Roles to Users
@@ -218,8 +231,24 @@ $user->assignRole('administrator');
218231
// Sync multiple roles
219232
$user->syncRoles([$role1->id, $role2->id]);
220233

221-
// Revoke a role
234+
// Sync without detaching existing roles
235+
$user->syncRolesWithoutDetaching(['editor', 'moderator']);
236+
237+
// Revoke a specific role
222238
$user->revokeRole($role);
239+
$user->revokeRole('editor');
240+
241+
// Revoke all roles
242+
$user->revokeRoles();
243+
244+
// Get all role names
245+
$user->getRoleNames(); // ['administrator', 'editor']
246+
247+
// Check if model has all specified roles
248+
$user->hasAllRoles(['admin', 'editor']); // true if user has both
249+
250+
// Check if model has any of the specified roles
251+
$user->hasAnyRole(['admin', 'editor']); // true if user has at least one
223252
```
224253

225254
### Direct User Permissions
@@ -243,6 +272,9 @@ $user->syncPermissions(['posts.create', 'posts.update', 'posts.delete']);
243272
// Revoke specific permission
244273
$user->revokePermissionTo('posts.delete');
245274

275+
// Revoke all permissions
276+
$user->revokeAllPermissions();
277+
246278
// Check if user has direct permission
247279
$user->hasDirectPermission('posts.create'); // true
248280

@@ -422,9 +454,9 @@ Role::unguarded()->get(); // Get all unguarded roles
422454
The package automatically caches permissions and roles. Clear cache manually:
423455

424456
```php
425-
use AmdadulHaq\Guard\GuardServiceProvider;
457+
use AmdadulHaq\Guard\Facades\Guard;
426458

427-
GuardServiceProvider::staticClearCache();
459+
Guard::clearCache();
428460
```
429461

430462
Cache is automatically invalidated when roles or permissions are created, updated, or deleted.

0 commit comments

Comments
 (0)