Class: CurrentScope::ScopedRoleAssignment
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- CurrentScope::ScopedRoleAssignment
- Includes:
- StorableKeys
- Defined in:
- app/models/current_scope/scoped_role_assignment.rb
Overview
A role held on ONE specific record: "Editor of Project #7" grants nothing on Project #8. Never touches the subject's org-wide role — the two are independent axes.
Rows survive host resource destruction by design (polymorphic, no dependent:). Since #65 those orphan grants open nothing (empty list = 403) but still rendered like live access until labeled (#90).
Class Method Summary collapse
-
.preload_resolvable_resources!(assignments) ⇒ Object
Batch-load polymorphic resources for resolvable types only.
Instance Method Summary collapse
-
#orphaned_resource? ⇒ Boolean
True when the pointed-at resource is gone (deleted row or unresolvable type).
Methods included from StorableKeys
#current_scope_resolved_record
Class Method Details
.preload_resolvable_resources!(assignments) ⇒ Object
Batch-load polymorphic resources for resolvable types only. A global includes(:resource) NameErrors when any resource_type is stale; this constantizes per type and skips unresolvable ones so they stay lazy and orphaned_resource? labels them inert (#90 / PR #104 review).
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'app/models/current_scope/scoped_role_assignment.rb', line 26 def self.preload_resolvable_resources!(assignments) list = Array(assignments) return list if list.empty? list.group_by(&:resource_type).each do |type, rows| next if type.blank? # Same canonical resolver the key guard uses: a namespaced, shortened or # custom polymorphic token must not read as missing here while passing # validation there. klass = CurrentScope.polymorphic_class(type, owner: self) next if klass.nil? next unless klass.respond_to?(:where) # Look up by the DECLARED primary key, and index on the string form of it. # resource_id is a string column (#151), so a record keyed on an integer # yields 1 while the grant holds "1"; matching them raw silently misses and # labels a live grant inert. `where` casts the strings back to the column's # own type, so the query is still correct on every adapter. key = klass.primary_key unless key.is_a?(String) mark_resources_loaded(rows, {}) next end # Only ids that are legal keys for THIS model reach the query. A legacy # collapsed value ("7", left by the pre-#151 integer column) sent at a # PostgreSQL uuid column does not come back empty — it RAISES # `invalid input syntax for type uuid`, and the console page an # administrator opens to find these very grants 500s instead of listing # them as inert. Same rule the resolver applies (CurrentScope # .canonical_key?); a dropped id simply stays unloaded, which is exactly # what orphaned_resource? then labels. ids = rows.map(&:resource_id).uniq.select { |id| CurrentScope.canonical_key?(klass, id) } # When NOTHING in the group is a legal key, skip the query but still mark # every row loaded-as-nil below. Returning early instead would leave the # associations lazy, so the first orphaned_resource? call would go and do # per-row exactly the load this method exists to do safely and in bulk. records = ids.empty? ? {} : klass.where(key => ids).index_by { |r| r[key].to_s } mark_resources_loaded(rows, records) end list end |
Instance Method Details
#orphaned_resource? ⇒ Boolean
True when the pointed-at resource is gone (deleted row or unresolvable type). The grant is inert for authorization (#65) but still a console row. Memoized: views call this plus the label helper once each; a reset-every- call would re-query the resource twice per row (PR #104 cubic follow-up).
76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'app/models/current_scope/scoped_role_assignment.rb', line 76 def orphaned_resource? return @orphaned_resource if defined?(@orphaned_resource) @orphaned_resource = if resource_id.blank? false else current_scope_resolved_record("resource").nil? end rescue NameError, ActiveRecord::RecordNotFound @orphaned_resource = true end |