alchemy/acl-bundle
Alchemy ACL bundle adds configurable access control entries to Symfony entities, with REST endpoints to list, create, update, and remove permissions per user, group, or object. Supports Redis-cached access tokens and flexible query filters.
Architecture fit
(userType, userId, objectType, objectId, mask) with NULL meaning "all" — is stack-agnostic and maps cleanly onto an aces table plus a Laravel Gate bridge. That model is the real asset here, not the code. 1.5.0 changes nothing about this.alchemy/acl-bundle is a Symfony bundle: Bundle class, DI extension, config tree, tagged services, router-loaded routes, and a required Alchemy\AclBundle\Repository\UserRepositoryInterface alias. None of that boots under Laravel's container. Release 1.5.0 pins the bundle to Symfony 8, while Laravel 11/12 ship Symfony 7.x components — so the bundle's target framework line and the host app's transitive Symfony line are now a full major apart, not merely adjacent.accessToken.cache pool name remains a hard-coded auto-wiring contract; under Laravel it must be replaced with a Redis facade/store binding. Reimplementable, not reusable.Integration feasibility
composer require in a Laravel app. Three paths remain, in ascending cost:
GET /permissions/aces, PUT /permissions/ace, DELETE /permissions/ace), call it from Laravel over HTTP. This is now the recommended fidelity path, precisely because the sidecar can pin Symfony 8 independently of the Laravel app's Symfony 7 components. Cost: one deployable plus a network hop on the authorization path.Gate::before/policy hooks. Cheapest; loses tested semantics and the REST contract.Technical risk
@4rthem, PR #7), consistent with the 4-star / 0 dependents profile. There is no community to absorb a breakage and no reviewer redundancy on the security-critical path.1.4.0 → 1.5.0) is a semver smell: it tells you the maintainer does not treat framework-major compatibility as breaking. Anyone on a ^1.4 constraint and running composer update can be silently moved onto a Symfony 8 / PHP 8.4 requirement. Mitigation is exact-version pinning, but the underlying signal — that future framework jumps may arrive unannounced — is a standing maintenance tax.composer.json changed. You cannot assess impact without reading the diff of PR #7. Until someone does, treat the scope as unknown."mask": 7 is presented without a legend, and "grant or deny" is described while the API only documents mask. Undocumented contract, inherited either way.NULL scoping, or cache staleness is a data-exposure defect, not a cosmetic bug.Key questions
objectId: null) and an object-scoped ACE?userType: group backed by an explicit group-membership resolver, or does it rely on our UserRepositoryInterface implementation to expand groups? If the latter, we own the expansion logic and its N+1 behaviour.Stack fit
NULL scoping, (b) the REST verb/body contract, (c) the Redis-backed token cache pattern. Release 1.5.0 does not change that list.UserRepositoryInterface — including group expansion if group ACLs are in scope.Migration path
1.4.0...1.5.0 and record what actually changed, then read the mask constants and the ACE resolution query and write a one-page internal spec of the semantics (bit meanings, precedence, cache keys, invalidation). Without this nothing downstream is safe.aces table and resolution service behind a feature flag. Keep existing if ($user->isAdmin()) checks live and run ACL evaluation in shadow mode, logging disagreements rather than enforcing. Target one high-value entity only.GET /permissions/aces: filterable table plus grant/revoke. Not before Phase 2 — the contract may still move.Compatibility
^1.5 is an unsafe constraint; use 1.5.0 and review each bump deliberately.accessToken.cache. Do not share with session or queue cache — invalidation collisions here are a permission bug.Sequencing
Maintenance
Support
Scaling
aces is read-hot, write-cold. Index (objectType, objectId) and (userType, userId); consider a partial index or an objectId IS NULL branch for global rows.Failure modes
DELETE /permissions/ace that does not evict the token cache silently leaves access in place. The single most dangerous defect class here. Require invalidate-on-write and an integration test that revokes and immediately asserts denial.NULL scoping accidents. A malformed write with objectId: null silently grants across every object of that type. Validate, require explicit intent for global grants, and surface them distinctly in the UI.Gate abilities invites off-by-one errors (read vs. write vs. delete bits). Centralize the mapping in one tested helper; never compare masks inline.userId in an ACE does not match the identifier the app resolves at check time (UUID vs. integer, group vs. user), every check silently denies or silently over-grants.composer update can take production onto a PHP/Symfony combination nobody tested. Pin exact versions and let dependency bots open PRs rather than auto-merging.Ramp-up
How can I help you explore Laravel packages today?