Class: CurrentScope::Role

Inherits:
ApplicationRecord show all
Defined in:
app/models/current_scope/role.rb

Overview

A named, editable bundle of permissions — a row, not a class. The same role means the same permission set whether held org-wide or scoped to a single record; only the reach differs.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#permission_keys_changeObject (readonly)

The grid diff computed by the last save: { added: [...], removed: [...], rejected: [...] }. Empty arrays on a no-op save; nil when no grid was staged (a programmatic save that never set permission_keys). added/removed describe what actually persisted; rejected names keys the scrub path threw away, so a caller that opted into silence can still log it. Controllers read this to record the change — the model itself records nothing (seeds must stay silent).



22
23
24
# File 'app/models/current_scope/role.rb', line 22

def permission_keys_change
  @permission_keys_change
end

Instance Method Details

#assign_permission_keys(keys, scrub: false) ⇒ Object

ponytail: one implementation, two intents — the setter is the strict front door, this is the same staging with the scrub opt-in named at the call site.

scrub: true is the ONLY sanctioned silent drop, and it exists for one real case: a controller was removed, so a role still holds keys that no longer route. Cleaning those up is legitimate and shouldn't require the operator to name every dead key. Everything else — typos, unrouted programmatic grants, the never-routed break-glass permission — is a mistake, and a security-grant API must not swallow mistakes.



51
52
53
54
55
56
57
58
59
60
# File 'app/models/current_scope/role.rb', line 51

def assign_permission_keys(keys, scrub: false)
  # Literal `true` only. Ruby truthiness would let the string "false" — or
  # any params/config value that found its way here — silently disable the
  # strict path, which is the exact hole this API exists to close. The
  # escape hatch opens for a caller that means it, not for one that passed
  # something along.
  @scrub_permission_keys = scrub == true
  # Blank entries are the grid's hidden-field padding, not typos (R2).
  @pending_permission_keys = Array(keys).map(&:to_s).reject(&:blank?).uniq
end

#grants?(permission_key) ⇒ Boolean

Returns:

  • (Boolean)


24
25
26
# File 'app/models/current_scope/role.rb', line 24

def grants?(permission_key)
  role_permissions.exists?(permission_key: permission_key)
end

#permission_keysObject



28
29
30
# File 'app/models/current_scope/role.rb', line 28

def permission_keys
  @pending_permission_keys || role_permissions.pluck(:permission_key)
end

#permission_keys=(keys) ⇒ Object

Stages a replacement permission set. STRICT: a key that isn't in the route-derived catalog makes the record invalid rather than disappearing. This is the mass-assignment writer (update!, strong params, the role grid), so it is deliberately the strict one — the scrub escape hatch must never be reachable from form params.



37
38
39
# File 'app/models/current_scope/role.rb', line 37

def permission_keys=(keys)
  assign_permission_keys(keys, scrub: false)
end

#reloadObject



62
63
64
65
66
# File 'app/models/current_scope/role.rb', line 62

def reload(...)
  @pending_permission_keys = nil
  @scrub_permission_keys = false
  super
end