Skip to content

Releases: davidteren/current_scope

v0.3.1

Choose a tag to compare

@davidteren davidteren released this 19 Jul 16:42
afa7144

Post-0.3.0 patch: silent-security footguns and management-console lockout
guards from the solid-solution worklist (PR #100). No intended host API break.

Fixed

  • config.sod_actions normalizes symbols and freezes the list (#91).
    Writing [:approve] used to leave SoD off silently because the resolver
    matches action-segment strings. The writer now maps symbols → strings,
    freezes the list, and raises on full keys / non-name elements — same honesty
    as collection_read_actions=.
  • collection_read_actions= rejects Hash / nested-array junk and warns on
    destroy_all / update_all.
    A Hash coerced to a never-matching member
    silently restored pre-#65 semantics; bulk write names the docs cite as
    escalation examples now warn like create/update/destroy.
  • Last full-access console lockout guards. Demoting or deleting a
    full-access role, or clearing/destroying an org-wide assignment, is refused
    when it would leave zero full-access org holders. Checks use holder
    semantics (not “any empty spare full_access role row”), run under
    transactional locks, and allow deleting unassigned full-access roles.
    Demotion only treats an explicit full_access param as demoting.
  • Role destroy cascade audit no longer 500s on orphaned polymorphic
    subjects/resources
    — event targets resolve defensively, like the members
    page already did.
  • full_access toggle is recorded on role update audit events
    (full_access_from / full_access_to).
  • ambient_collection_model is private on the Permissions mixin (internal
    gate/view binding, not host API).

Changed

  • Management UI: role delete confirm names non-zero cascade holder counts and
    uses the danger button; scoped-picker labels associate with controls;
    subjects Set controls get subject-scoped aria-labels; lockout flashes
    include a recovery step.

v0.3.0

Choose a tag to compare

@davidteren davidteren released this 19 Jul 11:27
cef6176

Highlights

The record-less (collection) gate grew a type: this release closes the #19 scoped-index trap and the #50 cross-type escalation together.

  • Type-bound collection gates (#50). A controller opts scoped grants into collection actions by declaring both hooks: def current_scope_record = nil plus def current_scope_model = TheType. A scoped grant now only opens record-less gates of its own type (STI-normalized via base_class); with no declared type the gate fails closed with a diagnosable model_undeclared (or model_invalid) reason.
  • Listed reads derive from the scoped list (#65). For actions in config.collection_read_actions (default ["index"]), the gate asks scope_for(...).exists? — the same id-narrowed query the list renders from — so "Owner of Report #7" finally opens the reports index and sees exactly their record. Gate and list agree by construction; full_access scoped roles are honored here and only here.
  • Report mode (#37): config.enforcement = :report observes would-deny requests (access.would_deny ledger rows + X-Current-Scope-Reason: would_deny) instead of 403ing — the retrofit story for legacy apps. Note: only :no_grant is downgraded; model_undeclared/model_invalid still 403.
  • Strict permission_keys= (#20): unknown keys now raise instead of silently dropping (assign_permission_keys(..., scrub: true) is the deliberate opt-in to dropping).
  • Ungated-surface detection (#62): grid badges for provably ungated controllers, a rake task, and a production tripwire posture.
  • Console 403 says why (#23): not_full_access reason + a short explanatory page instead of a bare head :forbidden.
  • Dev diagnostics (#41), error-isolated subject_label (#22), and a stack of hardening/review fixes — including the 0.3.0 release-gate fixes (#93).

Upgrade-breaking route rename (#85, errata): org-wide role assignment is now resources :role_assignments (plural). Hosts POSTing /current_scope/role_assignment directly get a 404 — use /current_scope/role_assignments; helpers role_assignment_path (create) → role_assignments_path, remove_role_assignment_pathrole_assignment_path(id).

Full details, including the who-this-changes upgrade notes: CHANGELOG.

Verified by a three-part release gate (deep multi-lens review, test audit with hand-mutation probes, security review — records in docs/reviews/) and a six-host post-release shakedown (current_scope_test_scenarios + the showcase, all green against the published gem).

v0.2.0

Choose a tag to compare

@davidteren davidteren released this 14 Jul 20:02

Added

  • Impersonation / act-as: Current carries the real actor alongside the
    effective user; config.actor_method, config.sod_identity, and a
    read-only-while-impersonating MutationGuard.
  • Append-only audit ledger (current_scope_events) with a read-only index.
  • scope_for(Model) — the list-side complement to allowed_to?, from the same
    grants (STI-aware: normalizes to base_class).
  • Scoped-role picker (Role → Subject → Type → Record) + CurrentScope::Scopeable,
    with an opt-in current_scope_searchable_scope hook for indexed search.
  • Host test helpers grant_role! / grant_scoped_role! (survive the request cycle).
  • CurrentScope::GatingTripwire — opt-in mixin that catches ungated controllers.
  • CurrentScope.grant! + current_scope:grant rake task to bootstrap the first admin.
  • Pagination for the subjects page and events index.

Changed

  • Separation of duties is opt-in: config.sod_actions now defaults to [].
  • Declared Rails floor is >= 8.1 (the management UI relies on params.expect
    array semantics introduced in 8.1); the CI test job exercises it.
  • config.audit is tri-state: false | true | :strict.

Security

  • Production guardrail: config.allow_mutations_while_impersonating = true raises
    at boot in production unless CURRENT_SCOPE_ALLOW_PROD_IMPERSONATION_MUTATIONS
    is set.
  • The impersonation-boundary API raises when config.actor_method is unset,
    instead of silently running with inert act-as security.

Fixed

  • with_current_user restores the ambient context correctly across the whole
    >= 8.1 floor (was version-fragile).