Class: CurrentScope::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/current_scope/configuration.rb

Constant Summary collapse

PROD_MUTATIONS_ENV =

Env var that opts a PRODUCTION deploy into impersonated writes. Any value (even "false" or "0" — presence is what counts) lifts the production refusal; unset in production means allow_mutations_while_impersonating=true raises at boot. development/test/staging never consult it.

"CURRENT_SCOPE_ALLOW_PROD_IMPERSONATION_MUTATIONS"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/current_scope/configuration.rb', line 132

def initialize
  @user_method = :current_user
  @actor_method = nil
  @sod_actions = []
  @sod_identity = :either
  @allow_sod_bypass = false
  @sod_bypass_permission = "bypass_sod"
  @allow_mutations_while_impersonating = false
  @excluded_controllers = [
    %r{\Arails/}, %r{\Aactive_storage/}, %r{\Aaction_mailbox/},
    %r{\Aturbo/}, %r{\Acurrent_scope/}
  ]
  @parent_controller = "::ApplicationController"
  @subject_class = "User"
  @audit = true
  @warn_on_nil_sod_record = false
  @permission_grid_groups = {
    "read"    => %w[index show],
    "create"  => %w[new create],
    "update"  => %w[edit update],
    "destroy" => %w[destroy]
  }
end

Instance Attribute Details

#actor_methodObject

Host controller method returning the REAL actor while impersonating (the pretender's true_user). nil means actor == user (no impersonation), so the actor is never resolved and falls back to the subject.



9
10
11
# File 'lib/current_scope/configuration.rb', line 9

def actor_method
  @actor_method
end

#allow_mutations_while_impersonatingObject

When false (the default), an impersonated session is read-only: any non-GET/HEAD request is denied while a real actor acts as a different subject — INCLUDING the engine's own management UI. The host's stop-impersonation, sign-out, and sign-in endpoints must opt out with skip_before_action :current_scope_mutation_guard!, or impersonation can never end. The gate runs BEFORE the permission check, so sod_identity is only observable once mutations are allowed (or on a GET-listed sod_action).

Setting this true is refused in production unless the env opt-in below is set — letting a real actor write as someone else is a privilege-escalation and audit-integrity risk, so a prod deploy must acknowledge it explicitly. See the custom writer.



43
44
45
# File 'lib/current_scope/configuration.rb', line 43

def allow_mutations_while_impersonating
  @allow_mutations_while_impersonating
end

#allow_sod_bypassObject

Break-glass override for the SoD veto. Default false — OFF preserves v0.1 exactly: the separation-of-duties veto is absolute and this hook is never consulted. When true, the veto is lifted for a record ONLY when all three hold, re-checked live at decision time: this flag is on, the record's current_scope_sod_bypassed? hook returns true, AND the record's initiator holds the bypass permission (sod_bypass_permission). Every lifted veto is recorded by the engine (sod.bypassed) and surfaced on X-Current-Scope-Reason.

HONEST FRAMING: this converts SoD from a structural guarantee into an audited policy override — it's break-glass, not SoD. Its legitimacy rests on being default-off, privilege-gated, and always audited. Unlike allow_mutations_while_impersonating there is NO production env-gate: the feature is per-record, privilege-scoped, and audited-by-construction, so production is its intended home.



89
90
91
# File 'lib/current_scope/configuration.rb', line 89

def allow_sod_bypass
  @allow_sod_bypass
end

#auditObject

Tri-state: false | true (default) | :strict — controls CurrentScope::Event.record!. false — record! is a silent no-op; hosts that don't want the ledger set this and skip the events migration. true — append a row for every event; if the current_scope_events table hasn't been migrated yet, degrade gracefully (skip + warn once), so an existing host never breaks on first mutation. :strict — an audit-mandatory host: a missing events table RAISES instead of degrading, so a mutation-wrapping transaction rolls back rather than committing an unaudited grant. (Impersonation- boundary events have no mutation to roll back — a raise there is a loud 500 on a mis-migrated host.) Read as == :strict, never == true — don't flatten the tri-state.



111
112
113
# File 'lib/current_scope/configuration.rb', line 111

def audit
  @audit
end

#excluded_controllersObject

Regexps matched against controller paths to keep infrastructure controllers out of the permission grid. An excluded controller cannot be granted, so it must also skip the gate (skip_before_action :current_scope_check!) — Guard raises otherwise.



55
56
57
# File 'lib/current_scope/configuration.rb', line 55

def excluded_controllers
  @excluded_controllers
end

#parent_controllerObject

Class the management UI's controllers inherit from, so they pick up the host's authentication and layout.



59
60
61
# File 'lib/current_scope/configuration.rb', line 59

def parent_controller
  @parent_controller
end

#permission_grid_groupsObject

How the role-editor grid folds RESTful actions into columns. An ordered Hash of { column_label => [action names] }: ticking a group column grants every routed action in it. The default collapses the seven RESTful verbs into CRUD — new/create and edit/update pair up (the "new"/"edit" actions just render the form for their mutation), index+show read as one. Actions not in any group (e.g. "approve") get their own column. Set to nil (or {}) to show every raw action as its own column instead. Either way the grid renders ALIGNED columns — a controller that doesn't route a column's actions shows a blank cell, never a shifted one.



130
131
132
# File 'lib/current_scope/configuration.rb', line 130

def permission_grid_groups
  @permission_grid_groups
end

#sod_actionsObject

Action names subject to the separation-of-duties veto: whoever initiated a record can never perform these actions on it. Not editable in the UI by design — SoD is a structural guarantee, not a preference. Records hit by these actions must define current_scope_initiator (return nil to exempt a record type).

EMPTY BY DEFAULT — SoD is opt-in. The engine's baseline is scoped RBAC; many hosts want nothing to do with four-eyes. Enable it by listing the actions to gate, e.g. config.sod_actions = %w[approve].



20
21
22
# File 'lib/current_scope/configuration.rb', line 20

def sod_actions
  @sod_actions
end

#sod_bypass_permissionObject

The grantable permission the record's initiator must hold for a break-glass bypass to lift the veto (default "bypass_sod"). Resolved against the record's route key like any permission, so it's editable in the role grid — never a hardcoded role. Must NOT be listed in sod_actions (it isn't an SoD action; keeping it out also bounds the bypass re-entrancy).



96
97
98
# File 'lib/current_scope/configuration.rb', line 96

def sod_bypass_permission
  @sod_bypass_permission
end

#sod_identityObject

Which identities the separation-of-duties veto weighs:

:either  (default) — veto if the effective subject OR (while
                   impersonating) the REAL actor initiated the record,
                   so impersonation can never approve the actor's own
                   record.
:subject           — veto only on the effective subject.

The two are identical when not impersonating (actor == subject).



29
30
31
# File 'lib/current_scope/configuration.rb', line 29

def sod_identity
  @sod_identity
end

#subject_classObject

Host class acting as the subject, used by the management UI to list assignable subjects.



63
64
65
# File 'lib/current_scope/configuration.rb', line 63

def subject_class
  @subject_class
end

#subject_labelObject

How a subject is identified in the management UI (subjects table, picker, bulk bar). A subject id is meaningless with UUID keys, so pick something human. Accepts:

- a Symbol — a method on the subject, e.g. :email or :name
- a Proc   — subject -> String, e.g. ->(u) { "#{u.first_name} #{u.last_name}" }
- nil (default) — best-effort, people-first: email, else name, else
first+last, else the generic current_scope_label / "Class #id".


72
73
74
# File 'lib/current_scope/configuration.rb', line 72

def subject_label
  @subject_label
end

#user_methodObject

Host controller method that returns the authenticated subject.



4
5
6
# File 'lib/current_scope/configuration.rb', line 4

def user_method
  @user_method
end

#warn_on_nil_sod_recordObject

A5 (opt-in, dev/test aid): when true, the gate logs a nudge if an SoD action is gated with a nil record and the request is allowed — i.e. the SoD veto was silently skipped because current_scope_record returned nil on a member action. Off by default; prod behavior never changes. Emitted from the Guard seam (not the shared resolver), so it doesn't fire on advisory allowed_to?/scope_for calls.



119
120
121
# File 'lib/current_scope/configuration.rb', line 119

def warn_on_nil_sod_record
  @warn_on_nil_sod_record
end