Installation
composer require ac/kalinka:dev-master
Add to composer.json if not using latest dev version.
Define a Base Guard
Create an abstract guard class extending AC\Kalinka\Guard\BaseGuard in app/Guards/MyAppBaseGuard.php:
namespace App\Guards;
use AC\Kalinka\Guard\BaseGuard;
abstract class MyAppBaseGuard extends BaseGuard
{
protected function policyAdmin($subject)
{
return $subject->isAdmin();
}
}
Create a Concrete Guard
Extend MyAppBaseGuard and define actions (methods prefixed with can):
namespace App\Guards;
use App\Guards\MyAppBaseGuard;
class UserGuard extends MyAppBaseGuard
{
public function canEditProfile($subject)
{
return $subject->isAuthenticated();
}
}
Register the Guard
Bind the guard in AppServiceProvider:
public function register()
{
$this->app->singleton('kalinka.guard', function ($app) {
return new \App\Guards\UserGuard();
});
}
First Usage Inject the guard into a controller/service and check permissions:
use AC\Kalinka\Guard\GuardInterface;
class ProfileController extends Controller
{
protected $guard;
public function __construct(GuardInterface $guard)
{
$this->guard = $guard;
}
public function edit()
{
if ($this->guard->can('editProfile', auth()->user())) {
return view('profile.edit');
}
abort(403);
}
}
Role-Based Access Control (RBAC) Define roles in policies and reuse them across guards:
protected function policyEditor($subject)
{
return in_array('editor', $subject->roles);
}
Resource-Specific Permissions Pass resources as subjects to enforce granular control:
public function canDeletePost($subject, $post)
{
return $subject->id === $post->user_id;
}
Combining Policies Use logical operators in guards:
public function canManageContent($subject)
{
return $this->policyAdmin($subject) || $this->policyEditor($subject);
}
Laravel Integration Bind the guard to Laravel’s container and resolve it via dependency injection:
$this->app->bind('kalinka.guard', function ($app) {
return new UserGuard($app['auth']->user());
});
Middleware Create middleware to check permissions globally:
namespace App\Http\Middleware;
use Closure;
use AC\Kalinka\Guard\GuardInterface;
class Authorize
{
protected $guard;
public function __construct(GuardInterface $guard)
{
$this->guard = $guard;
}
public function handle($request, Closure $next)
{
if (!$this->guard->can('accessDashboard', $request->user())) {
abort(403);
}
return $next($request);
}
}
Dynamic Guards Instantiate guards dynamically based on context (e.g., tenant ID):
$guard = new UserGuard(auth()->user(), $tenantId);
Fallback Policies
Override BaseGuard's fallbackPolicy() to define default deny/allow behavior:
protected function fallbackPolicy($action, $subject)
{
return false; // Default deny
}
Method Naming
policy (e.g., policyAdmin).can (e.g., canEditProfile).can_edit_profile) will fail silently.Subject Type Mismatch
$subject passed to guards matches what your policies expect (e.g., User model vs. string).__toString() method to your subject for clarity in logs.Circular Dependencies
Performance
protected $policyCache = [];
protected function policyAdmin($subject)
{
$cacheKey = spl_object_hash($subject);
if (!isset($this->policyCache[$cacheKey]['admin'])) {
$this->policyCache[$cacheKey]['admin'] = $subject->isAdmin();
}
return $this->policyCache[$cacheKey]['admin'];
}
Enable Logging
Extend BaseGuard to log policy evaluations:
protected function logPolicy($action, $subject, $result)
{
\Log::debug("Policy $action for " . $subject->id . ": " . ($result ? 'ALLOWED' : 'DENIED'));
}
Test Policies in Isolation Use PHPUnit to test guards independently:
public function testAdminPolicy()
{
$guard = new UserGuard($this->createMockUser(['isAdmin' => true]));
$this->assertTrue($guard->can('editProfile', $user));
}
Custom Guard Storage
Override BaseGuard's getSubject() to fetch users from non-standard sources (e.g., API tokens):
protected function getSubject($request)
{
return $request->bearerToken()->user();
}
Action Metadata Attach metadata to actions (e.g., HTTP methods, descriptions) via traits:
trait ActionMetadata
{
protected $actionMetadata = [
'editProfile' => ['method' => 'PUT', 'description' => 'Edit user profile'],
];
public function getActionMetadata($action)
{
return $this->actionMetadata[$action] ?? null;
}
}
Event Dispatching Trigger events on policy evaluation (e.g., audit logs):
use Illuminate\Support\Facades\Event;
protected function evaluatePolicy($action, $subject)
{
$result = parent::evaluatePolicy($action, $subject);
Event::dispatch('kalinka.policy.evaluated', [$action, $subject, $result]);
return $result;
}
Policy Groups
Organize policies by grouping guards (e.g., AdminGuard, EditorGuard) and composing them:
class CompositeGuard extends MyAppBaseGuard
{
protected $guards = [];
public function __construct(array $guards)
{
$this->guards = $guards;
}
public function can($action, $subject)
{
return any($this->guards, function ($guard) use ($action, $subject) {
return $guard->can($action, $subject);
});
}
}
How can I help you explore Laravel packages today?