Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Acl Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Core model is sound and portable. The domain model — ACE records keyed by (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.
  • The delivery vehicle is not portable, and 1.5.0 widens the gap. 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.
  • Cache coupling unchanged. The 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.
  • Net: for a Laravel codebase, fit is unchanged — low as a library, moderate-to-high as a reference design. For a Symfony codebase, fit is high and now explicitly targets Symfony 8.

Integration feasibility

  • The prior assessment's hedge — "the 1.5.0 line moves toward Symfony 8 / PHP 8.4–8.5" — is now confirmed rather than resolved. That removes the last plausible argument for a direct composer require in a Laravel app. Three paths remain, in ascending cost:
    1. Sidecar service — run the bundle in a small Symfony 8 app, expose the existing REST surface (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.
    2. Port the domain layer — lift the ACE entity, mask semantics, and repository interface into a first-party Laravel package. Retains behaviour, discards the Symfony plumbing.
    3. Reimplement against Laravel primitives — one table plus Gate::before/policy hooks. Cheapest; loses tested semantics and the REST contract.
  • Composer feasibility of "just require it" is now worse than previously assessed: the 1.5.0 constraint set (Symfony 8, presumably PHP 8.4+) collides with the host app's Symfony 7 components, and a bundle that never boots remains dead weight even when constraints are force-installed.

Technical risk

  • Bus factor 1 — now with a second data point. 1.5.0 is again a single-contributor change (@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.
  • Release-process risk (new, and the main finding of this release). A framework-major support bump shipped under a minor version (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.
  • Release notes carry zero migration information. "Symfony 8 by @4rthem" is the entire changelog. No PHP minimum stated, no deprecation list, no BC-break statement, no indication whether anything beyond composer.json changed. You cannot assess impact without reading the diff of PR #7. Until someone does, treat the scope as unknown.
  • Thin documentation, unchanged. The README still never defines the mask bit meanings; "mask": 7 is presented without a legend, and "grant or deny" is described while the API only documents mask. Undocumented contract, inherited either way.
  • Security-critical surface, unchanged. This component decides who can read/write tenant data. Ambiguity in deny precedence, NULL scoping, or cache staleness is a data-exposure defect, not a cosmetic bug.
  • Metadata still under-specified. Added in 1.1.0; shape and enforcement status remain undocumented. Verify in code before any compliance story depends on it.
  • Release metadata anomaly persists. 1.5.0 is dated 2026-10-06, which still does not reconcile with normal cadence. Continue to treat version/date signals as unreliable until verified against repo tags.
  • No GUI by design. API-first is a feature for headless use; the admin surface is your build either way.

Key questions

  1. What is the actual diff in PR #7? Composer constraints only, or code? This is now the top question — it determines whether 1.5.0 is a no-op for consumers or a hidden behavioural change.
  2. Is 1.4.0 still maintained for Symfony 7 consumers, or is it effectively frozen? If frozen, Symfony 7 adopters inherit an unpatched security component.
  3. What is the new minimum PHP, and does the Symfony 8 bump remove or rename anything in the REST surface we would be calling through a sidecar?
  4. Is the target codebase Laravel or Symfony? Symfony 8 → straightforward adopt. Laravel → we are buying a design, not a dependency; is that acceptable?
  5. What are the exact mask bit semantics, deny precedence rules, and inheritance behaviour between a global ACE (objectId: null) and an object-scoped ACE?
  6. Is 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.
  7. Is metadata enforced at authorization time, or audit-only?
  8. Are we willing to fork and own this — and who is the named maintainer?

Integration Approach

Stack fit

  • Best fit is now Symfony 8.x on PHP 8.4+ with Redis and Doctrine, and 1.5.0 makes that the only cleanly supported line. On that stack adoption is config-plus-alias and the assessment flips to a low-risk "buy."
  • Symfony 7 teams must pin to 1.4.x and accept a frozen upstream. Budget for a fork or an upgrade plan before adopting on Symfony 7 — do not assume patches will land on that line.
  • For a Laravel stack, treat the bundle as a specification. Three things worth keeping verbatim: (a) the ACE key structure including NULL scoping, (b) the REST verb/body contract, (c) the Redis-backed token cache pattern. Release 1.5.0 does not change that list.
  • Non-negotiables to stand up first: a Redis instance reachable from the app, and a principal-resolution service equivalent to UserRepositoryInterface — including group expansion if group ACLs are in scope.

Migration path

  • Phase 0 — Spike (1–2 days, now with one added task). Clone the repo, diff 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.
  • Phase 1 — Parallel run. Introduce the 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.
  • Phase 2 — Enforce on one entity. Reads first, then writes. Backfill ACEs from existing role/admin data with an idempotent script.
  • Phase 3 — Generalize. Additional configured objects; expose the REST endpoints under an authenticated admin route group.
  • Phase 4 — Admin surface. Minimum UI on GET /permissions/aces: filterable table plus grant/revoke. Not before Phase 2 — the contract may still move.

Compatibility

  • Pin an exact version, not a caret range. Given that 1.5.0 carried a framework-major jump under a minor tag, ^1.5 is an unsafe constraint; use 1.5.0 and review each bump deliberately.
  • If porting, pin the semantics spec, not the upstream version — and record the upstream tag the spec mirrors, so a future diff (including 1.5.0's undiffed changes) is traceable.
  • If sidecar: run it on its own PHP/Symfony constraints, decoupled from the Laravel app's Symfony 7 components. This is now the cleanest way to avoid the collision — and the main reason to prefer sidecar over in-process reuse.
  • Redis: use a dedicated logical DB or key prefix for accessToken.cache. Do not share with session or queue cache — invalidation collisions here are a permission bug.

Sequencing

  • Diff-and-spec → Redis + principal resolver → schema + backfill → shadow-mode evaluation → enforce on one entity → REST surface → admin UI → group ACLs / multi-tenant scoping.
  • Deliver the revocation path early, not last. Taking access away reliably is the higher-risk half of the feature and the one most likely to expose cache-invalidation gaps.

Operational Impact

Maintenance

  • If adopted on Symfony 8: low-to-moderate. Small surface area, one config file — but you must diff every release, because changelogs are one line and version numbers do not signal breaking framework requirements.
  • If adopted on Symfony 7: you are on a likely-frozen line. Plan the Symfony 8 upgrade or a fork.
  • If ported to Laravel: you own a fork of a security-relevant component. Budget a named owner, an upstream-diff review each release, and a regression suite over mask/precedence/cache behaviour. 1.5.0 is the concrete illustration of why that review step exists: a minor bump that materially changed platform requirements with no notes.
  • The 4-star / 0-dependent profile means no ecosystem pressure will surface bugs for you. Your test suite is the only safety net.

Support

  • No vendor, no SLA, no forum. Support model is "read the source," and as of 1.5.0 that includes "read the PR diff." Contractors and new hires will not know this package; assume it is unfamiliar to everyone outside the original implementer.
  • Escalation for a production permission bug: whoever owns the fork, immediately. Name that person in the runbook before go-live.

Scaling

  • The Redis token cache remains the scale lever, absorbing repeated point lookups for the same principal.
  • The risk is list filtering: a page of 100 objects is 100 authorization decisions unless you batch. Design the resolution API to accept a collection of object IDs from day one; do not let per-object checks leak into view loops.
  • Group ACLs multiply resolution cost (own + all groups' + globals). Cache the expanded effective ACE set per (user, objectType), not raw per-object rows.
  • Database: 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

  • Redis unavailable → authorization path fails. Decide now: fail-closed (deny everything, users see 403s) or fail-open (serve data to anyone). For permissions the answer is almost always fail-closed, with a circuit breaker and a loud alert. Document it; do not let the default emerge from a null check.
  • Stale cache after revocation. A 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.
  • Mask misinterpretation. Bridging numeric masks to Gate abilities invites off-by-one errors (read vs. write vs. delete bits). Centralize the mapping in one tested helper; never compare masks inline.
  • Principal resolution mismatch. If the 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.
  • Silent dependency drift (new). An unpinned constraint plus a maintainer who ships framework-major bumps in minors means a routine 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

  • Assume 1–2 days to productivity if the semantics spec exists; 1–2 weeks if the engineer must reverse-engineer mask and precedence from source. Write the spec.
  • Onboarding materials to produce: mask legend, ACE precedence diagram (object vs. global, user vs. group, deny vs. allow), the fail-closed decision record, the pinned-version policy, and three worked examples from the actual product.
  • Run one tabletop exercise before launch: "revoke a user's access to Publication 42, then verify denial within X seconds." If the team cannot answer that from the runbook, the operational story is not finished.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics