leaseweb/secure-controller-bundle
A Symfony bundle that adds declarative security to controllers, letting you enforce authentication and authorization rules with attributes or annotations instead of repetitive boilerplate checks in every action.
Architecture fit — poor, and mis-scoped. Despite being filed against Laravel/PHP, leaseweb/secure-controller-bundle is a Symfony bundle, not a Laravel package. Its integration points are Symfony-specific: a Bundle class, DI extension, a kernel event listener hooking kernel.controller (or a security voter), and a Doctrine Annotations reader that parses @Secure(roles={"ROLE_ADMIN"}) docblocks on controller actions. Laravel has no equivalent controller-metadata dispatch layer — authorization is expressed through route middleware, Gate/Policy, and Authorize calls. There is no natural seam to drop this in.
Integration feasibility — effectively zero as a direct dependency. The concept (declare required roles adjacent to the action, enforce centrally) is sound and maps cleanly onto Laravel: a PHP 8 attribute (#[RequiresRole('admin')]) read by a middleware via ReflectionMethod, or — more idiomatically — the built-in can: middleware and Gate::authorize(). Reimplementing the pattern in Laravel is on the order of 50–100 lines plus tests. Vendoring or porting the bundle is strictly more work than writing the equivalent.
Technical risk — high, concentrated in abandonment and fail-open semantics. Last release 2016-03-08; that is roughly Symfony 2.7/3.0-era code and PHP 5.6/7.0 idioms. doctrine/annotations v1 is deprecated and docblock annotation scanning was dropped from Symfony 7; the mechanism itself is on a deprecation path in favor of attributes. Zero dependents and 14 stars indicate a single-vendor internal tool, so there is no downstream community absorbing breakage or reporting defects. The most serious risk is semantic rather than structural: docblock-driven authorization is fail-open by nature — a malformed annotation, a renamed method, a stripped docblock (e.g., by an opcache/minifier or a code generator), or a controller action the reader never visits results in an unprotected action with no error. For a security control that is the worst possible failure mode.
Key questions. (1) Is the underlying requirement genuinely "role metadata on controller actions," or is it "role/ability checks on routes"? If the latter, Laravel already solves it natively and this evaluation ends. (2) Does the team need per-action granularity beyond route-level middleware — i.e., multiple actions on one controller with different role requirements? (3) Is the org standardized on Symfony anywhere, in which case the pattern may be relevant there but not in the Laravel estate? (4) What is the appetite for owning an unmaintained security-adjacent component?
Stack fit — none. The bundle's Composer constraints (symfony/framework-bundle, symfony/http-kernel, doctrine/annotations in the 2.x/3.x line) are irreconcilable with a modern Laravel application running Symfony 6/7 components via illuminate/*. Installing it would produce a dependency-resolution conflict at best and a bootstrapping mismatch at worst.
Migration path — adopt the pattern, not the package. Recommended shape in Laravel 10/11/12:
#[RequiresRole] attribute (or #[RequiresPermission] if abilities, not roles, are the real unit).EnforceActionRoles middleware that reflects the route's controller method, reads the attribute, and calls Gate::authorize() / $user->can(), aborting with 403 otherwise.#[AllowAny] opt-out for genuinely public actions.->middleware('can:manage-users') per route or Route::can() in Laravel 11+.Compatibility. Target PHP 8.2+, Laravel 11/12, attributes only — no Doctrine Annotations. This eliminates the deprecated scanner entirely and removes a transitive dependency.
Sequencing. (a) Confirm whether route-level can: middleware already satisfies the requirement; if yes, stop. (b) If per-action granularity is required, prototype the attribute + middleware in a branch and validate against an existing controller. (c) Write tests covering every gated action and asserting unannotated actions are denied by default. (d) Document and roll out via a single PR — no dependency added, no supply-chain surface. Total elapsed effort: days, not weeks. The bundle is read only as a design reference for how it maps roles to actions.
Maintenance. The package is unmaintained; any fork for Laravel is a fork of Symfony-shaped code you would then own indefinitely. Writing the 60-line native equivalent is cheaper on day one and dramatically cheaper at year three, because it survives framework upgrades without a compatibility shim.
Support. There is no support channel: no recent releases, no issue triage from the vendor, no downstream users to learn from (0 dependents). Any defect becomes an internal ticket against code nobody on the team wrote.
Scaling. Enforcement cost is per-request reflection on the resolved controller method. Naive implementation adds a ReflectionMethod + attribute read on every request; mitigate by caching a controller@method → roles map at boot or in the route cache, after which overhead is negligible. The original bundle's annotation reader is cached similarly, so this is not a differentiator — but it is a place a hand-rolled port can silently regress.
Failure modes. Ranked by severity: (1) silent fail-open — an action escapes gating due to a typo, a stripped docblock, or an unreflected method; this is the dominant risk with annotation-based approaches and the reason to prefer attributes plus a fail-closed default and a coverage test. (2) Role-string drift — ROLE_ADMIN vs admin constants diverging between the attribute and the Gate definition, producing 403s in production. (3) Middleware ordering — gating running before authentication resolves the user, yielding null-user authorization decisions. (4) Debug-only bypass — environment-conditional registration accidentally shipping to production. All four are eliminable with the native implementation and a small test suite; none are addressable in the abandoned bundle.
Ramp-up. Learning the bundle's Symfony conventions is time spent on knowledge that transfers nowhere else in a Laravel estate. Learning the native pattern (attributes + middleware + Gates) is knowledge the team needs regardless and is reinforced by every other authorization decision in the codebase.
Recommendation. Given a 14-star, nine-years-stale Symfony bundle with zero dependents and an opportunity score of 3.52, do not adopt. Close the evaluation as "pattern reference only," and if the underlying need is real, implement the attribute + fail-closed middleware natively in Laravel — a small, testable, upgrade-safe equivalent that removes rather than adds a dependency.
How can I help you explore Laravel packages today?