rails_admin_next 1.2.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +32 -20
  4. data/app/assets/stylesheets/rails_admin/skin.css +12 -0
  5. data/app/controllers/rails_admin_next/main_controller.rb +53 -7
  6. data/app/helpers/rails_admin_next/form_builder.rb +23 -4
  7. data/app/helpers/rails_admin_next/main_helper.rb +12 -1
  8. data/app/views/layouts/rails_admin_next/_head.html.erb +1 -1
  9. data/app/views/layouts/rails_admin_next/_navigation.html.erb +1 -1
  10. data/app/views/layouts/rails_admin_next/application.html.erb +3 -0
  11. data/app/views/rails_admin_next/main/_form_nested_many.html.erb +9 -4
  12. data/app/views/rails_admin_next/main/_form_nested_one.html.erb +8 -3
  13. data/app/views/rails_admin_next/main/bulk_delete.html.erb +1 -1
  14. data/app/views/rails_admin_next/main/dashboard.html.erb +52 -50
  15. data/app/views/rails_admin_next/main/index.html.erb +3 -2
  16. data/docs/UPGRADING.md +263 -9
  17. data/docs/security.md +4 -0
  18. data/lib/generators/rails_admin_next/templates/initializer.erb +7 -3
  19. data/lib/rails_admin_next/abstract_model.rb +12 -4
  20. data/lib/rails_admin_next/adapters/active_record.rb +47 -3
  21. data/lib/rails_admin_next/config/actions/base.rb +0 -1
  22. data/lib/rails_admin_next/config/actions/bulk_delete.rb +4 -4
  23. data/lib/rails_admin_next/config/actions/index.rb +3 -19
  24. data/lib/rails_admin_next/config/actions.rb +5 -12
  25. data/lib/rails_admin_next/config/fields/association.rb +1 -1
  26. data/lib/rails_admin_next/config/fields/types/enum.rb +8 -2
  27. data/lib/rails_admin_next/config/fields/types/serialized.rb +17 -2
  28. data/lib/rails_admin_next/config/fields/types/timestamp.rb +1 -0
  29. data/lib/rails_admin_next/config/has_fields.rb +1 -1
  30. data/lib/rails_admin_next/config.rb +29 -9
  31. data/lib/rails_admin_next/extensions/action_policy/authorization_adapter.rb +60 -0
  32. data/lib/rails_admin_next/extensions/action_policy.rb +5 -0
  33. data/lib/rails_admin_next/extensions/paper_trail/auditing_adapter.rb +1 -1
  34. data/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb +17 -1
  35. data/lib/rails_admin_next/support/composite_keys_serializer.rb +8 -2
  36. data/lib/rails_admin_next/support/csv_converter.rb +45 -5
  37. data/lib/rails_admin_next/version.rb +3 -3
  38. data/lib/rails_admin_next.rb +5 -17
  39. data/skills/rails-admin-next/SKILL.md +5 -4
  40. data/src/rails_admin/controllers/nested_form_controller.js +1 -1
  41. metadata +3 -3
  42. data/lib/rails_admin_next/extensions/cancancan/authorization_adapter.rb +0 -90
  43. data/lib/rails_admin_next/extensions/cancancan.rb +0 -5
@@ -9,6 +9,7 @@ module RailsAdminNext
9
9
  class Timestamp < RailsAdminNext::Config::Fields::Types::Datetime
10
10
  # Register field type for the type loader
11
11
  RailsAdminNext::Config::Fields::Types.register(self)
12
+ RailsAdminNext::Config::Fields::Types.register(:timestamptz, self)
12
13
  end
13
14
  end
14
15
  end
@@ -10,7 +10,7 @@ module RailsAdminNext
10
10
 
11
11
  # some fields are hidden by default (belongs_to keys, has_many associations in list views.)
12
12
  # unhide them if config specifically defines them
13
- field.show if field && !field.instance_variable_get("@#{field.name}_registered").is_a?(Proc)
13
+ field.show if field && !field.instance_variable_get(:@visible_registered).is_a?(Proc)
14
14
  # Specify field as virtual if type is not specifically set and field was not
15
15
  # found in default stack
16
16
  if field.nil? && type.nil?
@@ -73,6 +73,9 @@ module RailsAdminNext
73
73
  # show Gravatar in Navigation bar
74
74
  attr_accessor :show_gravatar
75
75
 
76
+ # show the "Made in Tokyo" footer under every admin page
77
+ attr_accessor :show_footer
78
+
76
79
  # accepts a hash of static links to be shown below the main navigation
77
80
  attr_accessor :navigation_static_links
78
81
  attr_accessor :navigation_static_label
@@ -171,21 +174,37 @@ module RailsAdminNext
171
174
  # end
172
175
  # end
173
176
  #
177
+ # The block form only gates entry: it runs before every action, but nothing checks
178
+ # individual records or scopes the list.
179
+ #
174
180
  # To use an authorization adapter, pass the name of the adapter. For example,
175
- # to use with CanCanCan[https://github.com/CanCanCommunity/cancancan/], pass it like this.
181
+ # to use with Pundit[https://github.com/varvet/pundit], pass it like this.
182
+ #
183
+ # @example Pundit
184
+ # RailsAdminNext.config do |config|
185
+ # config.authorize_with :pundit
186
+ # end
176
187
  #
177
- # @example CanCanCan
188
+ # @example ActionPolicy
178
189
  # RailsAdminNext.config do |config|
179
- # config.authorize_with :cancancan
190
+ # config.authorize_with :action_policy
180
191
  # end
181
192
  #
182
- # See the wiki[https://github.com/railsadminteam/rails_admin/wiki] for more on authorization.
193
+ # A custom adapter is registered with
194
+ # RailsAdminNext.add_extension(key, mod, authorization: true) before it is named here.
195
+ # See docs/UPGRADING.md for the adapter contract.
183
196
  #
184
197
  # @see RailsAdminNext::Config::DEFAULT_AUTHORIZE
185
198
  def authorize_with(*args, &block)
186
199
  extension = args.shift
187
200
  if extension
188
- klass = RailsAdminNext::AUTHORIZATION_ADAPTERS[extension]
201
+ klass = RailsAdminNext::AUTHORIZATION_ADAPTERS.fetch(extension) do
202
+ raise ArgumentError, "Unknown authorization adapter #{extension.inspect}. " \
203
+ "Registered adapters: #{RailsAdminNext::AUTHORIZATION_ADAPTERS.keys.map(&:inspect).join(", ")}. " \
204
+ "Register a custom adapter with RailsAdminNext.add_extension(key, mod, authorization: true) " \
205
+ "before this config block, or pass a block to authorize_with to gate admin entry only " \
206
+ "(no per-record checks or list scoping). See docs/UPGRADING.md."
207
+ end
189
208
  klass.setup if klass.respond_to? :setup
190
209
  @authorize = proc do
191
210
  @authorization_adapter = klass.new(*([self] + args).compact, &block)
@@ -249,7 +268,7 @@ module RailsAdminNext
249
268
 
250
269
  # pool of all found model names from the whole application
251
270
  def models_pool
252
- (viable_models - excluded_models.collect(&:to_s)).uniq.sort
271
+ (viable_models - excluded_models.collect { it.to_s.delete_prefix("::") }).uniq.sort
253
272
  end
254
273
 
255
274
  # Loads a model configuration instance from the registry or registers
@@ -272,7 +291,7 @@ module RailsAdminNext
272
291
  when Class, ConstLoadSuppressor::ConstProxy
273
292
  entity.name.to_sym
274
293
  when String, Symbol
275
- entity.to_sym
294
+ entity.to_s.delete_prefix("::").to_sym
276
295
  else
277
296
  entity.class.name.to_sym
278
297
  end
@@ -355,6 +374,7 @@ module RailsAdminNext
355
374
  @registry = {}
356
375
  @navbar_css_classes = %w[navbar-dark bg-primary border-bottom]
357
376
  @show_gravatar = true
377
+ @show_footer = true
358
378
  @navigation_static_links = {}
359
379
  @navigation_static_label = nil
360
380
  @composite_keys_serializer = RailsAdminNext::Support::CompositeKeysSerializer
@@ -369,7 +389,7 @@ module RailsAdminNext
369
389
  #
370
390
  # @see RailsAdminNext::Config.registry
371
391
  def reset_model(model)
372
- key = model.is_a?(Class) ? model.name.to_sym : model.to_sym
392
+ key = model.is_a?(Class) ? model.name.to_sym : model.to_s.delete_prefix("::").to_sym
373
393
  @registry.delete(key)
374
394
  end
375
395
 
@@ -395,7 +415,7 @@ module RailsAdminNext
395
415
  private
396
416
 
397
417
  def viable_models
398
- included_models.collect(&:to_s).presence || begin
418
+ included_models.collect { it.to_s.delete_prefix("::") }.presence || begin
399
419
  @@system_models ||= # memoization for tests
400
420
  ([Rails.application] + Rails::Engine.subclasses.collect(&:instance)).flat_map do |app|
401
421
  (app.paths["app/models"].to_a + app.config.eager_load_paths).collect do |load_path|
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdminNext
4
+ module Extensions
5
+ module ActionPolicy
6
+ class AuthorizationAdapter
7
+ # ActionPolicy is referenced only inside methods, so hosts without the gem still boot.
8
+ def self.setup
9
+ include ::ActionPolicy::Behaviour
10
+
11
+ authorize :user, through: -> { @controller._current_user }
12
+ end
13
+
14
+ def initialize(controller)
15
+ @controller = controller
16
+ end
17
+
18
+ def authorize(action, abstract_model = nil, model_object = nil)
19
+ return unless action
20
+
21
+ record = model_object || abstract_model&.model
22
+ authorize!(record, to: rule_for(action), with: policy_class_for(record))
23
+ @controller.skip_verify_authorized! if @controller.respond_to?(:skip_verify_authorized!)
24
+ end
25
+
26
+ def authorized?(action, abstract_model = nil, model_object = nil)
27
+ return unless action
28
+
29
+ record = model_object || abstract_model&.model
30
+ allowed_to?(rule_for(action), record, with: policy_class_for(record))
31
+ end
32
+
33
+ def query(_action, abstract_model)
34
+ @controller.skip_verify_authorized_scoped! if @controller.respond_to?(:skip_verify_authorized_scoped!)
35
+ model = abstract_model.model
36
+ authorized_scope(model.all, type: :active_record_relation, with: ::ActionPolicy.lookup(model))
37
+ rescue ::ActionPolicy::NotFound, ::ActionPolicy::UnknownScopeType, ::ActionPolicy::UnknownNamedScope
38
+ abstract_model.model.none
39
+ end
40
+
41
+ def attributes_for(action, abstract_model)
42
+ record = abstract_model&.model
43
+ policy_for(record:, with: policy_class_for(record)).try(:attributes_for, action) || {}
44
+ end
45
+
46
+ private
47
+
48
+ def policy_class_for(record)
49
+ ::ActionPolicy.lookup(record, default: ::ApplicationPolicy)
50
+ end
51
+
52
+ # A Symbol, because ActionPolicy keys its rule aliases (new? -> create?) by Symbol and a
53
+ # String would silently skip them.
54
+ def rule_for(action)
55
+ action.to_s.end_with?("?") ? action.to_sym : :"#{action}?"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/extensions/action_policy/authorization_adapter"
4
+
5
+ RailsAdminNext.add_extension(:action_policy, RailsAdminNext::Extensions::ActionPolicy, authorization: true)
@@ -143,7 +143,7 @@ module RailsAdminNext
143
143
 
144
144
  versions = object.nil? ? versions_for_model(model) : object.public_send(model.model.versions_association_name)
145
145
  versions = versions.where("event LIKE ?", "%#{query}%") if query.present?
146
- versions = versions.order(sort)
146
+ versions = versions.reorder(sort)
147
147
 
148
148
  paginate_versions(versions, all, (page.presence || 1).to_i, per_page.to_i)
149
149
  end
@@ -46,7 +46,7 @@ module RailsAdminNext
46
46
  def query(_action, abstract_model)
47
47
  @controller.send(:policy_scope, abstract_model.model.all)
48
48
  rescue ::Pundit::NotDefinedError
49
- abstract_model.model.none
49
+ host_policy_scope? ? abstract_model.model.none : model_scope(abstract_model.model)
50
50
  end
51
51
 
52
52
  # This is called in the new/create actions to determine the initial attributes for new
@@ -65,6 +65,22 @@ module RailsAdminNext
65
65
  ::ApplicationPolicy.new(@controller.send(:pundit_user), record)
66
66
  end
67
67
 
68
+ # action_policy defines ActiveRecord::Relation#policy_class as nil, which hides every
69
+ # Scope from Pundit's relation lookup when both gems are bundled; the model still finds it.
70
+ # A host override of policy_scope (e.g. namespacing) chose its own Scope, so it is never
71
+ # swapped for the model's.
72
+ def host_policy_scope?
73
+ @controller.method(:policy_scope).owner != (defined?(::Pundit::Authorization) ? ::Pundit::Authorization : ::Pundit)
74
+ end
75
+
76
+ def model_scope(model)
77
+ scope_class = ::Pundit::PolicyFinder.new(model).scope
78
+ return model.none unless scope_class
79
+
80
+ @controller.instance_variable_set(:@_pundit_policy_scoped, true)
81
+ scope_class.new(@controller.send(:pundit_user), model.all).resolve
82
+ end
83
+
68
84
  def action_for_pundit(action)
69
85
  (action[-1, 1] == "?") ? action : "#{action}?"
70
86
  end
@@ -3,12 +3,18 @@
3
3
  module RailsAdminNext
4
4
  module Support
5
5
  module CompositeKeysSerializer
6
+ # "_" joins the keys, so it is escaped as "~" plus its hex byte. "~" is escaped too so the
7
+ # escape cannot be forged, and both are URL-unreserved. Every other value serializes as-is,
8
+ # keeping existing links valid.
9
+ ESCAPED = /[_~]/
10
+ ESCAPE = /~(5F|7E)/
11
+
6
12
  def self.serialize(keys)
7
- keys.map { |key| key&.to_s&.gsub("_", "__") }.join("_")
13
+ keys.map { |key| key&.to_s&.gsub(ESCAPED) { format("~%02X", it.ord) } }.join("_")
8
14
  end
9
15
 
10
16
  def self.deserialize(string)
11
- string.split("_").map { |key| key&.gsub("__", "_") }
17
+ string.split("_").map { |key| key.gsub(ESCAPE) { $1.hex.chr } }
12
18
  end
13
19
  end
14
20
  end
@@ -4,14 +4,17 @@ require "csv"
4
4
 
5
5
  module RailsAdminNext
6
6
  class CSVConverter
7
+ BATCH_SIZE = 1000
8
+
7
9
  def initialize(objects = [], schema = nil)
8
10
  @fields = []
9
11
  @associations = []
10
12
  schema ||= {}
11
13
 
12
- return if (@objects = objects).blank?
14
+ @objects = objects
15
+ @model = objects.is_a?(ActiveRecord::Relation) ? objects.klass : objects.first&.class
16
+ return unless @model
13
17
 
14
- @model = objects.dup.first.class
15
18
  @abstract_model = RailsAdminNext::AbstractModel.new(@model)
16
19
  @model_config = @abstract_model.config
17
20
  @methods = [(schema[:only] || []) + (schema[:methods] || [])].flatten.compact
@@ -63,17 +66,54 @@ module RailsAdminNext
63
66
 
64
67
  def generate_csv_string(options)
65
68
  generator_options = (options[:generator] || {}).symbolize_keys.delete_if { |_, value| value.blank? }
66
- method = @objects.respond_to?(:find_each) ? :find_each : :each
67
-
68
69
  CSV.generate(**generator_options) do |csv|
69
70
  csv << generate_csv_header unless options[:skip_header]
70
71
 
71
- @objects.send(method) do |object|
72
+ each_object do |object|
72
73
  csv << generate_csv_row(object)
73
74
  end
74
75
  end
75
76
  end
76
77
 
78
+ def each_object(&)
79
+ return @objects.each(&) unless @objects.is_a?(ActiveRecord::Relation)
80
+ # Batching sets its own limit and offset, which would drop the relation's.
81
+ return @objects.each(&) if @objects.limit_value || @objects.offset_value
82
+
83
+ if (direction = primary_key_direction)
84
+ @objects.unscope(:order).find_each(order: direction, batch_size: BATCH_SIZE, &)
85
+ else
86
+ each_in_offset_batches(&)
87
+ end
88
+ end
89
+
90
+ # sort_scope writes the list sort as a single "<table>.<column> <direction>" string.
91
+ def primary_key_direction
92
+ primary_key = @model.primary_key
93
+ return unless primary_key.is_a?(String)
94
+
95
+ orders = @objects.order_values
96
+ return :asc if orders.empty?
97
+ return unless orders.one? && orders.first.is_a?(String)
98
+
99
+ table = Regexp.escape(@model.table_name)
100
+ column = Regexp.escape(primary_key)
101
+ orders.first[/\A(?:\W?#{table}\W?\.)?\W?#{column}\W? (asc|desc)\z/i, 1]&.downcase&.to_sym
102
+ end
103
+
104
+ # The primary-key tie-break makes the order total, so rows that tie on the sort
105
+ # column cannot shift between batches. A short batch is not the last one: sorting
106
+ # on an eager-loaded has-many column returns fewer records than the limit.
107
+ def each_in_offset_batches(&)
108
+ ordered = @objects.order(Array(@model.primary_key).index_with(:asc))
109
+ 0.step(by: BATCH_SIZE) do |offset|
110
+ batch = ordered.limit(BATCH_SIZE).offset(offset).to_a
111
+ break if batch.empty?
112
+
113
+ batch.each(&)
114
+ end
115
+ end
116
+
77
117
  def generate_csv_header
78
118
  @fields.collect do |field|
79
119
  ::I18n.t("admin.export.csv.header_for_root_methods", name: field.label, model: @abstract_model.pretty_name)
@@ -5,9 +5,9 @@ module RailsAdminNext
5
5
  # Releases are cut by publishing a Release-Drafter draft: the gem-push workflow stamps
6
6
  # the tag's version over these constants at build time, so the in-repo values are only
7
7
  # the floor a local `gem build` produces.
8
- MAJOR = 1
9
- MINOR = 2
10
- PATCH = 1
8
+ MAJOR = 2
9
+ MINOR = 0
10
+ PATCH = 0
11
11
  PRE = nil
12
12
 
13
13
  class << self
@@ -6,8 +6,8 @@ require "rails_admin_next/config"
6
6
  require "rails_admin_next/config/const_load_suppressor"
7
7
  require "rails_admin_next/icons"
8
8
  require "rails_admin_next/extension"
9
- require "rails_admin_next/extensions/cancancan"
10
9
  require "rails_admin_next/extensions/pundit"
10
+ require "rails_admin_next/extensions/action_policy"
11
11
  require "rails_admin_next/extensions/paper_trail"
12
12
  require "rails_admin_next/support/csv_converter"
13
13
  require "rails_admin_next/support/hash_helper"
@@ -49,25 +49,13 @@ module RailsAdminNext
49
49
  @deprecator ||= ActiveSupport::Deprecation.new(RailsAdminNext::Version.to_s, "RailsAdminNext")
50
50
  end
51
51
 
52
- # Backwards-compatible with safe_yaml/load when SafeYAML isn't available.
53
- # Evaluates available YAML loaders at boot and creates appropriate method,
54
- # so no conditionals are required at runtime.
55
- begin
56
- require "safe_yaml/load"
57
- def self.yaml_load(yaml)
58
- SafeYAML.load(yaml)
59
- end
60
- rescue LoadError
61
- if YAML.respond_to?(:safe_load)
62
- def self.yaml_load(yaml)
63
- YAML.safe_load(yaml)
64
- end
65
- else
66
- raise LoadError.new "Safe-loading of YAML is not available. Please install 'safe_yaml' or install Psych 2.0+"
67
- end
52
+ def self.yaml_load(yaml)
53
+ deprecator.warn("RailsAdminNext.yaml_load is deprecated and will be removed. Call YAML.safe_load directly.")
54
+ YAML.safe_load(yaml)
68
55
  end
69
56
 
70
57
  def self.yaml_dump(object)
58
+ deprecator.warn("RailsAdminNext.yaml_dump is deprecated and will be removed. Call YAML.dump directly.")
71
59
  YAML.dump(object)
72
60
  end
73
61
  end
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rails-admin-next
3
- description: Configure or extend the rails_admin_next admin in a host Rails app (models, fields, custom actions and field types, CanCanCan/Pundit authorization, PaperTrail history, theming, CSP). Use instead of upstream RailsAdmin knowledge, which is wrong for this fork.
3
+ description: Configure or extend the rails_admin_next admin in a host Rails app (models, fields, custom actions and field types, Pundit, ActionPolicy or custom-adapter authorization, PaperTrail history, theming, CSP). Use instead of upstream RailsAdmin knowledge, which is wrong for this fork.
4
4
  ---
5
5
 
6
6
  # rails_admin_next
@@ -35,7 +35,7 @@ Renamed (write the new form):
35
35
 
36
36
  Not renamed (leave as is):
37
37
 
38
- - The authorization subject is still `:rails_admin`. CanCanCan abilities grant `can :access, :rails_admin`. Never rewrite it to `:rails_admin_next`, which fails authorization.
38
+ - The authorization subject is still `:rails_admin`. A custom adapter that keeps CanCanCan grants `can :access, :rails_admin`. Never rewrite it to `:rails_admin_next`, which fails authorization.
39
39
  - Action authorization keys (`:dashboard`, `:index`, `:show`, `:new`, `:edit`, `:export`, `:destroy`, `:history`, `:show_in_app`).
40
40
  - The `admin.*` I18n namespace.
41
41
 
@@ -75,9 +75,10 @@ Icons take logical names, never Font Awesome classes: `link_icon :show_in_app` o
75
75
 
76
76
  ## Authorization and history
77
77
 
78
- - `config.authorize_with :cancancan` uses the app's `Ability`. Keep the `:rails_admin` subject (see Naming).
78
+ - Pundit and ActionPolicy are the built-in adapters. `RailsAdminNext.add_extension(key, mod, authorization: true)` registers a custom one before the config block names its key; copy `<gem>/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb` as the template. An unregistered key raises `ArgumentError` at boot.
79
79
  - `config.authorize_with :pundit` calls the record's policy with `<action>?` methods (`index?`, `edit?`, `dashboard?`) and scopes lists with `policy_scope`.
80
- - `config.authorize_with do ... end` runs a custom block as a before filter.
80
+ - `config.authorize_with :action_policy` checks the same `<action>?` rules with `allowed_to?` and scopes lists with the policy's default `relation_scope`. `new?` resolves to `create?` and undefined rules to `manage?`. A model with no policy is checked against `ApplicationPolicy`, and one with no default relation scope lists nothing.
81
+ - `config.authorize_with do ... end` runs a custom block as a before filter. It gates admin entry only: no per-record checks or list scoping.
81
82
  - `config.authenticate_with do ... end` and `config.current_user_method do ... end` wire in the app's authentication, such as Devise or Warden.
82
83
  - `config.audit_with :paper_trail` turns on the history action. Read the "History / audit table" section of `<gem>/docs/UPGRADING.md` first.
83
84
 
@@ -7,7 +7,7 @@ import { Controller } from "@hotwired/stimulus";
7
7
  //
8
8
  // - add: clones a blank subform from the <template>. For a has_many
9
9
  // association the template carries a child-index placeholder
10
- // (e.g. "new_nested_field_tests") which is swapped for a unique value so
10
+ // (e.g. "new_1_nested_field_tests") which is swapped for a unique value so
11
11
  // each new row submits under its own index; has_one/belongs_to subforms
12
12
  // have no placeholder. Focus then moves to the first visible field.
13
13
  // - remove on an unsaved subform deletes it from the DOM.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_admin_next
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel López Prat
@@ -285,8 +285,8 @@ files:
285
285
  - lib/rails_admin_next/config/sections/update.rb
286
286
  - lib/rails_admin_next/engine.rb
287
287
  - lib/rails_admin_next/extension.rb
288
- - lib/rails_admin_next/extensions/cancancan.rb
289
- - lib/rails_admin_next/extensions/cancancan/authorization_adapter.rb
288
+ - lib/rails_admin_next/extensions/action_policy.rb
289
+ - lib/rails_admin_next/extensions/action_policy/authorization_adapter.rb
290
290
  - lib/rails_admin_next/extensions/controller_extension.rb
291
291
  - lib/rails_admin_next/extensions/paper_trail.rb
292
292
  - lib/rails_admin_next/extensions/paper_trail/auditing_adapter.rb
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RailsAdminNext
4
- module Extensions
5
- module CanCanCan
6
- # This adapter is for the CanCanCan[https://github.com/CanCanCommunity/cancancan] authorization library.
7
- class AuthorizationAdapter
8
- module ControllerExtension
9
- def current_ability
10
- # use _current_user instead of default current_user so it works with
11
- # whatever current user method is defined with RailsAdminNext
12
- @current_ability ||= ability_class.new(_current_user)
13
- end
14
- end
15
-
16
- include RailsAdminNext::Config::Configurable
17
-
18
- def self.setup
19
- RailsAdminNext::Extensions::ControllerExtension.include ControllerExtension
20
- end
21
-
22
- # See the +authorize_with+ config method for where the initialization happens.
23
- def initialize(controller, ability = nil, &block)
24
- @controller = controller
25
- ability_class { ability } if ability
26
- instance_eval(&block) if block
27
-
28
- adapter = self
29
- ControllerExtension.define_method(:ability_class) do
30
- adapter.ability_class
31
- end
32
- @controller.current_ability.authorize! :access, :rails_admin
33
- end
34
-
35
- register_instance_option :ability_class do
36
- Ability
37
- end
38
-
39
- # This method is called in every controller action and should raise an exception
40
- # when the authorization fails. The first argument is the name of the controller
41
- # action as a symbol (:create, :bulk_delete, etc.). The second argument is the
42
- # AbstractModel instance that applies. The third argument is the actual model
43
- # instance if it is available.
44
- def authorize(action, abstract_model = nil, model_object = nil)
45
- return unless action
46
-
47
- action, subject = resolve_action_and_subject(action, abstract_model, model_object)
48
- @controller.current_ability.authorize!(action, subject)
49
- end
50
-
51
- # This method is called primarily from the view to determine whether the given user
52
- # has access to perform the action on a given model. It should return true when authorized.
53
- # This takes the same arguments as +authorize+. The difference is that this will
54
- # return a boolean whereas +authorize+ will raise an exception when not authorized.
55
- def authorized?(action, abstract_model = nil, model_object = nil)
56
- return unless action
57
-
58
- action, subject = resolve_action_and_subject(action, abstract_model, model_object)
59
- @controller.current_ability.can?(action, subject)
60
- end
61
-
62
- # This is called when needing to scope a database query. It is called within the list
63
- # and bulk_delete/destroy actions and should return a scope which limits the records
64
- # to those which the user can perform the given action on.
65
- def query(action, abstract_model)
66
- abstract_model.model.accessible_by(@controller.current_ability, action)
67
- end
68
-
69
- # This is called in the new/create actions to determine the initial attributes for new
70
- # records. It should return a hash of attributes which match what the user
71
- # is authorized to create.
72
- def attributes_for(action, abstract_model)
73
- @controller.current_ability.attributes_for(action, abstract_model&.model)
74
- end
75
-
76
- private
77
-
78
- def resolve_action_and_subject(action, abstract_model, model_object)
79
- subject = model_object || abstract_model&.model
80
- if subject
81
- [action, subject]
82
- else
83
- # For :dashboard compatibility
84
- [:read, action]
85
- end
86
- end
87
- end
88
- end
89
- end
90
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails_admin_next/extensions/cancancan/authorization_adapter"
4
-
5
- RailsAdminNext.add_extension(:cancancan, RailsAdminNext::Extensions::CanCanCan, authorization: true)