Class: Plutonium::Resource::Policy
- Inherits:
-
ActionPolicy::Base
- Object
- ActionPolicy::Base
- Plutonium::Resource::Policy
- Defined in:
- lib/plutonium/resource/policy.rb
Overview
Policy class to define permissions and attributes for a resource. This class provides methods to check permissions for various actions and to retrieve permitted attributes for these actions.
Direct Known Subclasses
Class Method Summary collapse
-
.method_added(name) ⇒ Object
relation_scopeis a class-level macro: it registers a scope handler named__scoping__active_record_relation__default, and never defines an instance method calledrelation_scope.
Instance Method Summary collapse
-
#apply_scope(relation, type:, **options) ⇒ Object
Wraps apply_scope to verify default_relation_scope was called.
-
#create? ⇒ Boolean
Checks if the create action is permitted.
-
#default_relation_scope(relation) ⇒ ActiveRecord::Relation
Applies Plutonium's default scoping (parent or entity) to a relation.
-
#destroy? ⇒ Boolean
Checks if the destroy action is permitted.
-
#edit? ⇒ Boolean
Checks if the edit action is permitted.
-
#export_csv? ⇒ Boolean
Checks if CSV export is permitted.
-
#index? ⇒ Boolean
Checks if the index action is permitted.
-
#kanban_move? ⇒ Boolean
Authorizes a kanban board move.
-
#new? ⇒ Boolean
Checks if the new action is permitted.
-
#permitted_associations ⇒ Array<Symbol>
Returns the permitted associations.
-
#permitted_attributes_for_create ⇒ Array<Symbol>
Returns the permitted attributes for the create action.
-
#permitted_attributes_for_edit ⇒ Array<Symbol>
Returns the permitted attributes for the edit action.
-
#permitted_attributes_for_export ⇒ Array<Symbol>
Returns the attributes included in an export (e.g. CSV columns).
-
#permitted_attributes_for_index ⇒ Array<Symbol>
Returns the permitted attributes for the index action.
-
#permitted_attributes_for_new ⇒ Array<Symbol>
Returns the permitted attributes for the new action.
-
#permitted_attributes_for_read ⇒ Array<Symbol>
Returns the permitted attributes for the read action.
-
#permitted_attributes_for_show ⇒ Array<Symbol>
Returns the permitted attributes for the show action.
-
#permitted_attributes_for_update ⇒ Array<Symbol>
Returns the permitted attributes for the update action.
-
#read? ⇒ Boolean
Checks if the read action is permitted.
-
#reposition? ⇒ Boolean
Authorizes a drag-reorder drop on a table or grid.
-
#search? ⇒ Boolean
Checks if record search is permitted.
-
#send_with_report(method) ⇒ Object
Sends a method and raises an error if the method is not implemented.
-
#show? ⇒ Boolean
Checks if the show action is permitted.
-
#skip_default_relation_scope! ⇒ Object
Explicitly skip the default relation scope verification.
-
#typeahead? ⇒ Boolean
Checks if typeahead/autocomplete queries are permitted.
-
#update? ⇒ Boolean
Checks if the update action is permitted.
Class Method Details
.method_added(name) ⇒ Object
relation_scope is a class-level macro: it registers a scope handler named
__scoping__active_record_relation__default, and never defines an instance
method called relation_scope. So def relation_scope(relation) overrides
nothing — apply_scope keeps dispatching to the inherited handler, the author's
narrowing never runs, and verify_default_relation_scope_applied! is happy
because the base handler did call default_relation_scope. Fail-open and silent,
so catch it the moment the method is defined rather than at request time.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/plutonium/resource/policy.rb', line 31 def self.method_added(name) super return unless name == :relation_scope raise <<~MSG #{self} defines `relation_scope` as an instance method, which Plutonium never calls. `relation_scope` is a macro, so this scoping silently does nothing. Use the block form instead: relation_scope do |relation| default_relation_scope(relation).your_scope end MSG end |
Instance Method Details
#apply_scope(relation, type:, **options) ⇒ Object
Wraps apply_scope to verify default_relation_scope was called. This prevents accidental multi-tenancy leaks when overriding relation_scope.
50 51 52 53 54 55 |
# File 'lib/plutonium/resource/policy.rb', line 50 def apply_scope(relation, type:, **) @_default_relation_scope_applied = false result = super verify_default_relation_scope_applied! if type == :active_record_relation result end |
#create? ⇒ Boolean
Checks if the create action is permitted.
150 151 152 |
# File 'lib/plutonium/resource/policy.rb', line 150 def create? false end |
#default_relation_scope(relation) ⇒ ActiveRecord::Relation
Applies Plutonium's default scoping (parent or entity) to a relation.
This method MUST be called in any custom relation_scope to ensure proper parent/entity scoping. Failure to call it will raise an error.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/plutonium/resource/policy.rb', line 92 def default_relation_scope(relation) @_default_relation_scope_applied = true if parent || parent_association unless parent && parent_association raise ArgumentError, "parent and parent_association must both be provided together" end # Parent association scoping (nested routes). # # The parent context is set on the policy for the whole request, so it # leaks into sibling lookups too — e.g. a SecureAssociation field on # the child's form authorizes an unrelated resource scope while # parent/parent_association are still set. Only apply parent scoping # when the relation actually corresponds to the parent's named # association; otherwise fall through to entity scoping so we don't # produce an incoherent (and silently empty) result. assoc_reflection = parent.class.reflect_on_association(parent_association) if assoc_reflection && relation.klass <= assoc_reflection.klass # The parent was already entity-scoped during authorization, so # children accessed through the parent don't need additional # entity scoping. if assoc_reflection.collection? # has_many: merge with the association's scope parent.public_send(parent_association).merge(relation) else # has_one: scope by foreign key relation.where(assoc_reflection.foreign_key => parent.id) end elsif entity_scope relation.associated_with(entity_scope) else relation end elsif entity_scope # Entity scoping (multi-tenancy) relation.associated_with(entity_scope) else relation end end |
#destroy? ⇒ Boolean
Checks if the destroy action is permitted.
171 172 173 |
# File 'lib/plutonium/resource/policy.rb', line 171 def destroy? create? end |
#edit? ⇒ Boolean
Checks if the edit action is permitted.
201 202 203 |
# File 'lib/plutonium/resource/policy.rb', line 201 def edit? update? end |
#export_csv? ⇒ Boolean
Checks if CSV export is permitted.
Defaults to false so export is strictly opt-in. Enable it per resource by overriding to return true (or delegating to index?).
242 243 244 |
# File 'lib/plutonium/resource/policy.rb', line 242 def export_csv? false end |
#index? ⇒ Boolean
Checks if the index action is permitted.
180 181 182 |
# File 'lib/plutonium/resource/policy.rb', line 180 def index? read? end |
#kanban_move? ⇒ Boolean
Authorizes a kanban board move. Delegates to update? by default — override to allow board drags without granting full edit-form access.
209 210 211 |
# File 'lib/plutonium/resource/policy.rb', line 209 def kanban_move? update? end |
#new? ⇒ Boolean
Checks if the new action is permitted.
187 188 189 |
# File 'lib/plutonium/resource/policy.rb', line 187 def new? create? end |
#permitted_associations ⇒ Array<Symbol>
Returns the permitted associations.
316 317 318 |
# File 'lib/plutonium/resource/policy.rb', line 316 def permitted_associations [] end |
#permitted_attributes_for_create ⇒ Array<Symbol>
Returns the permitted attributes for the create action.
251 252 253 254 255 256 |
# File 'lib/plutonium/resource/policy.rb', line 251 def permitted_attributes_for_create autodetect_permitted_fields(:permitted_attributes_for_create) - [ resource_class.primary_key.to_sym, # primary_key :created_at, :updated_at # timestamps ] end |
#permitted_attributes_for_edit ⇒ Array<Symbol>
Returns the permitted attributes for the edit action.
309 310 311 |
# File 'lib/plutonium/resource/policy.rb', line 309 def permitted_attributes_for_edit permitted_attributes_for_update end |
#permitted_attributes_for_export ⇒ Array<Symbol>
Returns the attributes included in an export (e.g. CSV columns).
Format-agnostic on purpose (named _export, not _export_csv) so a
future export format can reuse the same column set. Defaults to the
index columns; override to tailor the exported columns.
295 296 297 |
# File 'lib/plutonium/resource/policy.rb', line 295 def permitted_attributes_for_export permitted_attributes_for_index end |
#permitted_attributes_for_index ⇒ Array<Symbol>
Returns the permitted attributes for the index action.
277 278 279 |
# File 'lib/plutonium/resource/policy.rb', line 277 def permitted_attributes_for_index permitted_attributes_for_read end |
#permitted_attributes_for_new ⇒ Array<Symbol>
Returns the permitted attributes for the new action.
302 303 304 |
# File 'lib/plutonium/resource/policy.rb', line 302 def permitted_attributes_for_new permitted_attributes_for_create end |
#permitted_attributes_for_read ⇒ Array<Symbol>
Returns the permitted attributes for the read action.
261 262 263 |
# File 'lib/plutonium/resource/policy.rb', line 261 def permitted_attributes_for_read autodetect_permitted_fields(:permitted_attributes_for_read) end |
#permitted_attributes_for_show ⇒ Array<Symbol>
Returns the permitted attributes for the show action.
284 285 286 |
# File 'lib/plutonium/resource/policy.rb', line 284 def permitted_attributes_for_show permitted_attributes_for_read end |
#permitted_attributes_for_update ⇒ Array<Symbol>
Returns the permitted attributes for the update action.
268 269 270 |
# File 'lib/plutonium/resource/policy.rb', line 268 def permitted_attributes_for_update permitted_attributes_for_create end |
#read? ⇒ Boolean
Checks if the read action is permitted.
157 158 159 |
# File 'lib/plutonium/resource/policy.rb', line 157 def read? false end |
#reposition? ⇒ Boolean
Authorizes a drag-reorder drop on a table or grid. Delegates to update? by default — override to allow (or forbid) reordering independently of full edit-form access.
218 219 220 |
# File 'lib/plutonium/resource/policy.rb', line 218 def reposition? update? end |
#search? ⇒ Boolean
Checks if record search is permitted.
225 226 227 |
# File 'lib/plutonium/resource/policy.rb', line 225 def search? index? end |
#send_with_report(method) ⇒ Object
Sends a method and raises an error if the method is not implemented.
137 138 139 140 141 142 143 |
# File 'lib/plutonium/resource/policy.rb', line 137 def send_with_report(method) unless respond_to?(method) raise NotImplementedError, "#{self.class.name} does not implement the required #{method}" end public_send(method) end |
#show? ⇒ Boolean
Checks if the show action is permitted.
194 195 196 |
# File 'lib/plutonium/resource/policy.rb', line 194 def show? read? end |
#skip_default_relation_scope! ⇒ Object
Explicitly skip the default relation scope verification.
Call this when you intentionally want to bypass parent/entity scoping. This should be rare - consider using a separate portal instead.
67 68 69 |
# File 'lib/plutonium/resource/policy.rb', line 67 def skip_default_relation_scope! @_default_relation_scope_applied = true end |
#typeahead? ⇒ Boolean
Checks if typeahead/autocomplete queries are permitted.
232 233 234 |
# File 'lib/plutonium/resource/policy.rb', line 232 def typeahead? index? end |
#update? ⇒ Boolean
Checks if the update action is permitted.
164 165 166 |
# File 'lib/plutonium/resource/policy.rb', line 164 def update? create? end |