Lightweight access control provider for ImpulsePHP with abilities, policies and middleware integration.

Maintainers

Package info

github.com/ImpulsePHP/acl

pkg:composer/impulsephp/acl

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-04-11 16:58 UTC

This package is auto-updated.

Last update: 2026-04-11 17:00:02 UTC


README

impulsephp/acl fournit le contrôle d’accès applicatif d’ImpulsePHP. Le package reste volontairement simple : des abilities pour les accès globaux, des policies pour les objets métier, et un middleware pour protéger des zones de l’application sans réinventer le routeur.

Ce que fait le package

  • expose un service ACL avec define(), policy(), can(), canFor(), cannot(), authorize() et authorizeFor() ;
  • résout automatiquement l’utilisateur courant si impulsephp/auth est présent ;
  • protège des zones applicatives via un middleware basé sur la configuration ;
  • délègue les checks sur objets métier à des classes de policy explicites ;
  • fournit un trait léger pour consommer l’ACL depuis pages, composants ou services.

Installation

composer require impulsephp/acl

Le package déclare son provider via extra.impulse-provider. Si votre application ne gère pas l’auto-découverte, ajoutez Impulse\Acl\AclProvider::class dans providers.

Configuration dédiée

Le provider charge automatiquement un fichier dédié config/acl.php quand il existe dans l’application. Ce fichier peut retourner soit directement la config ACL, soit une clé racine acl.

<?php

use App\Policy\PostPolicy;
use Impulse\Acl\Attributes\Policy;
use Impulse\Acl\AclProvider;

return [
    'abilities' => [
        'admin.access' => static fn (?object $user): bool => in_array('admin', $user->roles ?? [], true),
    ],
    'role_hierarchy' => [
        'admin' => ['manager'],
        'manager' => ['editor'],
    ],
    'messages' => [
        'forbidden' => 'Vous ne pouvez pas accéder à cette zone.',
        'flash_key' => 'acl_error',
    ],
];

Le impulse.php principal peut alors rester léger :

return [
    'providers' => [
        AclProvider::class,
    ],
];

Options disponibles :

  • abilities
  • zones : fallback optionnel par pattern de route
  • role_hierarchy
  • messages.forbidden
  • messages.flash_key
  • policies : fallback optionnel si vous ne voulez pas annoter la classe métier

Policies via attribut PHP 8

La manière privilégiée pour relier une ressource métier à sa policy est désormais l’attribut :

use App\Policy\PostPolicy;
use Impulse\Acl\Attributes\Policy;

#[Policy(PostPolicy::class)]
final class Post
{
}

Le mapping acl.policies reste accepté comme fallback pour des classes externes ou un wiring explicite.

API principale

use Impulse\Acl\Contracts\AclInterface;
use Impulse\Core\App;

$acl = App::get(AclInterface::class);

$acl->can('admin.access');
$acl->can('update', $post);
$acl->canFor($user, 'update', $post);
$acl->authorize('admin.access');

Protéger une page avec PageProperty.roles

Quand le provider acl est installé dans l’application principale, core consomme maintenant PageProperty::$roles nativement pendant le routage.

use Impulse\Acl\Middleware\AuthorizeMiddleware;
use Impulse\Auth\Middleware\RequireAuthMiddleware;
use Impulse\Core\Attributes\PageProperty;
use Impulse\Core\Component\AbstractPage;

#[PageProperty(
    route: '/admin/users',
    roles: ['admin'],
    middlewares: [RequireAuthMiddleware::class]
)]
final class AdminUsersPage extends AbstractPage
{
    public function template(): string
    {
        return '<h1>Admin</h1>';
    }
}

Avec une hiérarchie :

'role_hierarchy' => [
    'admin' => ['manager'],
    'manager' => ['editor'],
],

un utilisateur admin satisfera aussi une page qui demande manager ou editor.

AuthorizeMiddleware reste utile pour le fallback par pattern de chemin avec zones.

Fallback par ability de zone

Si vous préférez une protection par pattern plutôt que par page, le fallback zones reste disponible :

'zones' => [
    '/admin*' => 'admin.access',
],

Policies pour les objets métier

final class PostPolicy
{
    public function update(?object $user, Post $post): bool
    {
        if (!$user instanceof User) {
            return false;
        }

        return $user->role === 'admin' || $user->id === $post->authorId;
    }
}

Puis :

$acl->can('update', $post);

Messages d’erreur

Les refus liés à PageProperty.roles dans core, comme ceux du middleware ACL, utilisent messages.forbidden et poussent aussi un flash sous la clé messages.flash_key.

Intégration avec auth

Le package ne gère jamais l’authentification. Si Impulse\Auth\Contracts\AuthInterface est présent dans le conteneur, Acl l’utilise seulement pour résoudre user() et les rôles du user courant.

Ancien exemple de configuration

Le format historique sous impulse.php['acl'] reste accepté. Le fichier dédié config/acl.php est simplement prioritaire pour mieux cibler la configuration.

Utilisation depuis une page ou un composant

use Impulse\Acl\Trait\AuthorizesRequests;
use Impulse\Core\Component\AbstractPage;

final class AccountPage extends AbstractPage
{
    use AuthorizesRequests;

    public function template(): string
    {
        if (!$this->can('admin.access')) {
            return '<p>Accès limité</p>';
        }

        return '<p>Administration</p>';
    }
}

Intégration avec auth

Le package ne gère jamais l’authentification. Si Impulse\Auth\Contracts\AuthInterface est présent dans le conteneur, Acl l’utilise seulement pour résoudre user(). Sinon, les checks implicites se font avec null comme utilisateur courant.

Pour forcer une authentification avant une autorisation, composez les middlewares :

#[PageProperty(
    route: '/admin',
    middlewares: [
        \Impulse\Auth\Middleware\RequireAuthMiddleware::class,
        \Impulse\Acl\Middleware\AuthorizeMiddleware::class,
    ]
)]

Documentation

  • docs/installation.md
  • docs/usage.md

Tests

php ../core/vendor/bin/phpunit -c phpunit.xml