nowo-tech / rbac-kit-bundle
Role-based access control for Symfony: a configurable permission catalogue, roles as permission sets checked in their context (global or a tenant / project subject), a memoised worker-safe voter, a route guard, coverage checks and a UiKit admin UI.
Package info
github.com/nowo-tech/RbacKitBundle
Type:symfony-bundle
pkg:composer/nowo-tech/rbac-kit-bundle
Fund package maintenance!
Requires
- php: >=8.2 <8.6
- nowo-tech/form-kit-bundle: ^2.17
- nowo-tech/ui-kit-bundle: ^2.21
- psr/clock: ^1.0
- psr/event-dispatcher: ^1.0
- psr/log: ^2.0 || ^3.0
- symfony/cache-contracts: ^3.5
- symfony/config: ^7.4 || ^8.0
- symfony/console: ^7.4 || ^8.0
- symfony/dependency-injection: ^7.4 || ^8.0
- symfony/event-dispatcher: ^7.4 || ^8.0
- symfony/form: ^7.4 || ^8.0
- symfony/framework-bundle: ^7.4 || ^8.0
- symfony/http-foundation: ^7.4 || ^8.0
- symfony/http-kernel: ^7.4 || ^8.0
- symfony/routing: ^7.4 || ^8.0
- symfony/security-core: ^7.4 || ^8.0
- symfony/security-csrf: ^7.4 || ^8.0
- symfony/service-contracts: ^3.5
- symfony/translation-contracts: ^3.5
- symfony/twig-bundle: ^7.4 || ^8.0
- symfony/validator: ^7.4 || ^8.0
- twig/extra-bundle: ^3.12
- twig/string-extra: ^3.12
- twig/twig: ^3.12
Requires (Dev)
- doctrine/dbal: ^3.8 || ^4.0
- doctrine/doctrine-bundle: ^2.13 || ^3.0
- doctrine/orm: ^3.3
- doctrine/persistence: ^3.1 || ^4.0
- friendsofphp/php-cs-fixer: ^3.0
- igor-php/igor-php: ^0.10.1
- nowo-tech/dashboard-menu-bundle: ^2.16
- nowo-tech/phpstan-frankenphp: ^1.3
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-doctrine: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-symfony: ^2.0
- phpunit/phpunit: ^11.5
- rector/rector: ^2.0
- symfony/browser-kit: ^7.4 || ^8.0
- symfony/cache: ^7.4 || ^8.0
- symfony/css-selector: ^7.4 || ^8.0
- symfony/dom-crawler: ^7.4 || ^8.0
- symfony/filesystem: ^7.4 || ^8.0
- symfony/phpunit-bridge: ^7.4 || ^8.0
- symfony/security-bundle: ^7.4 || ^8.0
- symfony/translation: ^7.4 || ^8.0
- symfony/yaml: ^7.4 || ^8.0
Suggests
- doctrine/doctrine-bundle: Store custom / edited permission sets in the database (storage.type: doctrine) and manage them in the admin UI
- doctrine/orm: Required by storage.type: doctrine
- nowo-tech/dashboard-menu-bundle: Hide DashboardMenu items the user may not open (menu.dashboard_menu: true)
- symfony/flex: For automatic recipe installation
- symfony/security-bundle: Required for the voter in an application and for the admin UI (unless security.allow_unauthenticated is true in dev / demo only)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-02 21:11:50 UTC
README
⭐ Found this useful? Install from Packagist · Give it a star on GitHub so more developers can find it.
Role-based access control for Symfony where roles are permission sets. Catalogue every permission a page, a page
resource or a menu link can ask for; define roles as sets of those permissions (two roles need not be comparable);
let users hold sets globally or per context (a project, a tenant, an organisation); ask
isGranted('permission:<key>', $context) or rbac_can('<key>', context) everywhere. A memoised, worker-safe voter
answers with the union of the sets the user holds in that context, a request guard refuses catalogued routes before
their controller, a coverage command proves every route is guarded, and a UiKit admin UI edits the sets. Fail closed:
an empty catalogue denies everything.
📋 Compatible with Symfony 7.4, 8.0 and 8.1 · PHP 8.2+ (Symfony 8.x requires PHP 8.4+) · UiKit ^2.21 · FormKit ^2.17
This bundle is FrankenPHP worker mode friendly: every memo (catalogue, sets, grants, decisions) implements
ResetInterface and is cleared between requests.
Screenshots
Permission sets — system, edited, locked and custom |
A set is a collection of catalogued permissions |
rbac_can() per project: the union of the sets held there |
Features
- Permission catalogue from the bundle configuration and tagged
PermissionProviderInterfaceservices: key, scope (global/context), guarded routes, group, labels; area aliases; validated (no redefinition, no route guarded twice, bounded size). - Permission sets (roles) from the configuration (system sets,
all(context keys) /except/include_global,locked) and, withstorage.type: doctrine, editable and custom sets stored in one table; unknown keys grant nothing. - Typed contexts: any object implementing
RbacContextInterface(or yourContextResolverInterface) is a context identified by<type>:<id>(project:5≠organization:5); yourGrantProviderInterfacelists the sets each user holds (one call per user and request). A set held in a context never grants a platform-wide key unless the key opts in (aggregate: true). - Memoised voter
permission:<key>/permission_route:<route>: deny-first gates, optional bypass roles with vetoes ("view as member"),role_grants, per-key aggregation over the user's contexts, an optional fallback set for users without any assignment; worker-safe (ResetInterface), bounded memos. - Checks everywhere:
PermissionChecker(can,canRoute,canArea,filter), Twigrbac_can(),rbac_can_route(),rbac_can_area(), and an optional DashboardMenu permission checker. - Request guard (opt-in): 403 (or the login entry point for guests) before the controller on a catalogued route without its permission; strangers to the context are refused by default.
- Coverage:
nowo:rbac:checkandPermissionCoveragefail when a route under your prefixes has no permission, a catalogue route does not exist or a set names an uncatalogued key. - Admin UI (opt-in): permission sets and catalogue on the UiKit page contract, FormKit forms, CSRF bound to action
and set, audit events (
PermissionSetChangedEvent) after every mutation, 7 locales.
Installation
composer require nowo-tech/rbac-kit-bundle
With Symfony Flex the recipe registers the bundle and adds config/packages/nowo_rbac_kit.yaml (safe defaults: an
empty catalogue denies everything) and config/routes/nowo_rbac_kit.yaml (the opt-in admin routes). Without Flex, see
docs/INSTALLATION.md.
Quick start
# config/packages/nowo_rbac_kit.yaml nowo_rbac_kit: catalogue: permissions: reports.view: { scope: global, routes: [app_reports], aggregate: true } # general page over my projects project.view: { scope: context, routes: [app_project_show] } project.issues.triage: { scope: context } permission_sets: viewer: { permissions: [reports.view, project.view] } triage: { permissions: [project.view, project.issues.triage] } owner: { all: true, locked: true } # every *context* key, never a global one grant_provider: App\Security\MembershipGrantProvider # yields Assignment::in('owner', 'project', $id)
// App\Entity\Project implements Nowo\RbacKitBundle\Context\RbacContextInterface // (getRbacContextType(): 'project', getRbacContextId(): (string) $this->id) $this->denyAccessUnlessGranted('permission:project.issues.triage', $project);
{% if rbac_can('project.issues.triage', project) %}<button>Triage</button>{% endif %}
Requirements
- PHP 8.2+ (8.4+ with Symfony 8), Symfony 7.4 / 8.0 / 8.1,
symfony/security-bundlein the application. - UiKit ^2.21 and FormKit ^2.17 (installed with the bundle; used by the admin UI).
- Optional: Doctrine ORM (
storage.type: doctrine), nowo-tech/dashboard-menu-bundle (menu checker).
Configuration
Every key, its default and its validation: docs/CONFIGURATION.md.
Usage
The decision model, grant providers, contexts, gates, the request guard, coverage, the admin UI and template overrides: docs/USAGE.md.
Demo
demo/symfony8— Symfony 8.1 (FrankenPHP PHP 8.5, worker mode) with MySQL; host port 8096 (bound to 127.0.0.1). Two projects, four demo users (admin,alice,bob,carol; the password is the user name), the request guard and the admin UI on Doctrine storage.
Runtime mode is FRANKENPHP_MODE (worker default, or classic) — see docs/DEMO-FRANKENPHP.md.
make -C demo up-symfony8 # http://localhost:8096 make test-e2e # Playwright e2e against the running demo make demo-screenshots # refresh docs/images/demo/*.webp make demo-smoke # boot, HTTP 200, down (REQ-TEST-011)
Development
make up
make install
make test
make cs-check
make phpstan
make igor
make test-coverage-100
make release-check
Documentation
- Installation
- Configuration
- Usage
- Contributing
- Code of Conduct
- Changelog
- Upgrading
- Release
- Security
- Engram
- Spec-driven development
- GitHub Spec Kit
Additional documentation
- Templates (UiKit page contract)
- Architecture
- PSR evaluation (REQ-CS-007)
- Demo (FrankenPHP)
- FrankenPHP worker audit (kernel not reset)
- GitHub Actions CI requirements
Tests and coverage
- Tests: PHPUnit (PHP) — unit tests plus kernel integration tests (voter through Symfony Security, Twig functions, request guard, kernel.reset between requests, admin UI end to end with CSRF and Doctrine on SQLite, opt-in routes, invalid configuration, coverage command) and the UiKit page contract test; Playwright e2e on the demo
- PHP: 100% (1230/1230 statements, 1420/1420 elements; gate enforced in CI by
composer test-coverage-100, no exclusions) - TS/JS: N/A
- Python: N/A
License and author
MIT · Nowo.tech · Héctor Franco Aceituno



