rails_admin_next 2.0.1 → 2.1.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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/app/controllers/rails_admin_next/application_controller.rb +7 -2
- data/app/controllers/rails_admin_next/main_controller.rb +2 -2
- data/docs/UPGRADING.md +38 -1
- data/docs/security.md +3 -0
- data/lib/rails_admin_next/config.rb +15 -2
- data/lib/rails_admin_next/extensions/action_policy/authorization_adapter.rb +16 -1
- data/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb +4 -0
- data/lib/rails_admin_next/version.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 821ddc2c14943ce137265d8c99d9784492cde1b2041dab27a1b5651795ce3cd4
|
|
4
|
+
data.tar.gz: f3c9d9906d117bb55e0eb4c2fcfc585d129b98c1bb2b32689fdd2a734f656d90
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 993ba29f1ae5ce41e276c0637660c791c0f3a6a47379082143b15079052c4f43ebc7a24a0d00f7433b4c0c1c37359154e0ef4ae3bf1c5a95340ac78472fca376
|
|
7
|
+
data.tar.gz: fa8ef20e12e1a670877e0d8f8719d87967e0398a11063d30e58a70a5bdff77984dc04e70695b7f12dfd7f632e04606388e4fd08c027b4e57dcc1121f2eec458c
|
data/README.md
CHANGED
|
@@ -192,7 +192,8 @@ The `admin.*` I18n namespace is likewise unchanged.
|
|
|
192
192
|
the same `<key>?` rules the Pundit adapter calls (`index?`, `show?`, `new?`, `edit?`,
|
|
193
193
|
`destroy?`, `export?`, `history?`, `show_in_app?`, `dashboard?`), and scopes lists, export
|
|
194
194
|
and bulk delete through the policy's default `relation_scope`. A model with no policy, or a
|
|
195
|
-
policy with no default relation scope, lists nothing.
|
|
195
|
+
policy with no default relation scope, lists nothing. Policies that declare context beyond
|
|
196
|
+
`user` get it from `authorize_with :action_policy, context: {...}`. Bundle `action_policy` yourself; see
|
|
196
197
|
the "ActionPolicy adapter" section of [docs/UPGRADING.md](docs/UPGRADING.md).
|
|
197
198
|
|
|
198
199
|
## Frontend and assets
|
|
@@ -28,12 +28,17 @@ module RailsAdminNext
|
|
|
28
28
|
|
|
29
29
|
def get_model
|
|
30
30
|
@model_name = to_model_name(params[:model_name])
|
|
31
|
-
raise RailsAdminNext::ModelNotFound unless (@abstract_model =
|
|
32
|
-
raise RailsAdminNext::ModelNotFound if (@model_config = @abstract_model.config).excluded?
|
|
31
|
+
raise RailsAdminNext::ModelNotFound unless (@abstract_model = visible_abstract_model(@model_name))
|
|
33
32
|
|
|
33
|
+
@model_config = @abstract_model.config
|
|
34
34
|
@properties = @abstract_model.properties
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def visible_abstract_model(model_name)
|
|
38
|
+
abstract_model = RailsAdminNext::AbstractModel.new(model_name)
|
|
39
|
+
abstract_model if abstract_model && !abstract_model.config.excluded?
|
|
40
|
+
end
|
|
41
|
+
|
|
37
42
|
def get_object
|
|
38
43
|
raise RailsAdminNext::ObjectNotFound unless (@object = @abstract_model.get(params[:id], @model_config.scope))
|
|
39
44
|
end
|
|
@@ -231,9 +231,9 @@ module RailsAdminNext
|
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
def get_association_scope_from_params
|
|
234
|
-
return nil unless params[:associated_collection].present?
|
|
234
|
+
return nil unless params[:associated_collection].present? && params[:source_abstract_model].present?
|
|
235
|
+
return nil unless (source_abstract_model = visible_abstract_model(to_model_name(params[:source_abstract_model])))
|
|
235
236
|
|
|
236
|
-
source_abstract_model = RailsAdminNext::AbstractModel.new(to_model_name(params[:source_abstract_model]))
|
|
237
237
|
source_model_config = source_abstract_model.config
|
|
238
238
|
action = params[:current_action].in?(%w[create update]) ? params[:current_action] : "edit"
|
|
239
239
|
field = source_model_config.send(action).fields.detect { |f| f.name == params[:associated_collection].to_sym }
|
data/docs/UPGRADING.md
CHANGED
|
@@ -74,13 +74,17 @@ end
|
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
The module defines an `AuthorizationAdapter` class. `authorize_with` builds it with
|
|
77
|
-
`new(controller, *args, &block)` and it answers:
|
|
77
|
+
`new(controller, *args, **options, &block)` and it answers:
|
|
78
78
|
|
|
79
79
|
- `authorize(action, abstract_model, object)`: raises when the action is denied.
|
|
80
80
|
- `authorized?(action, abstract_model, object)`: returns a boolean, for links and buttons.
|
|
81
81
|
- `query(action, abstract_model)`: returns the scope lists, export and bulk actions read from.
|
|
82
82
|
- `attributes_for(action, abstract_model)`: returns the attributes a new record starts with.
|
|
83
83
|
- `self.setup` (optional): runs once when `authorize_with` names the key.
|
|
84
|
+
- `self.validate_options!(*args, **options)` (optional): receives the extra arguments while the
|
|
85
|
+
config block runs; raise `ArgumentError` there so a misconfigured option fails at boot. A
|
|
86
|
+
subclass of a built-in adapter inherits its `validate_options!`, so one that takes options of
|
|
87
|
+
its own overrides it.
|
|
84
88
|
|
|
85
89
|
`lib/rails_admin_next/extensions/pundit/authorization_adapter.rb` in the gem is the template.
|
|
86
90
|
The removed CanCanCan adapter is a starting point you can copy:
|
|
@@ -596,6 +600,39 @@ class PlayerPolicy < ApplicationPolicy
|
|
|
596
600
|
end
|
|
597
601
|
```
|
|
598
602
|
|
|
603
|
+
### Authorization context beyond the user
|
|
604
|
+
|
|
605
|
+
A policy that declares more context than `user`, such as a tenant's `authorize :account`,
|
|
606
|
+
needs the adapter to supply it. Pass each key as a block:
|
|
607
|
+
|
|
608
|
+
```ruby
|
|
609
|
+
RailsAdminNext.config do |config|
|
|
610
|
+
config.authorize_with :action_policy, context: {account: -> { _current_user.account }}
|
|
611
|
+
end
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Each block runs on the admin controller once per request, the first time anything is
|
|
615
|
+
authorized, whichever policy is checked; a block should return `nil` rather than raise for a
|
|
616
|
+
user with no such context. A helper from your own controller, such as `current_account`, is
|
|
617
|
+
only available there when `config.parent_controller` names that controller:
|
|
618
|
+
|
|
619
|
+
```ruby
|
|
620
|
+
config.parent_controller = "::ApplicationController"
|
|
621
|
+
config.authorize_with :action_policy, context: {account: -> { current_account }}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
The value is an authorization input, so derive it from the signed-in user, or check the user's
|
|
625
|
+
membership when it comes from request data such as `params` or the subdomain. The relation scope
|
|
626
|
+
only narrows lists, export and bulk actions: show, edit, delete and history load the record by
|
|
627
|
+
id and check the rule alone, so a tenant rule such as `update?` must compare `record` with
|
|
628
|
+
`account` itself.
|
|
629
|
+
|
|
630
|
+
`user` always comes from `current_user_method` and is rejected inside `context:`. A key that is
|
|
631
|
+
not a Symbol, a value that is not a block taking no arguments, or an unknown option raises
|
|
632
|
+
`ArgumentError` while the config block runs. A key a policy declares that the host omits, or
|
|
633
|
+
whose block returns `nil`, raises `ActionPolicy::AuthorizationContextMissing` on the request
|
|
634
|
+
unless the policy declares it with `allow_nil:` or `optional:`.
|
|
635
|
+
|
|
599
636
|
## An `:enum` field with no option source offers no options
|
|
600
637
|
|
|
601
638
|
An `:enum` field takes its options from `enum_method`, which defaults to `<name>_enum` on the
|
data/docs/security.md
CHANGED
|
@@ -20,6 +20,9 @@ destructive-action controls itself; a few things are left for the host to opt in
|
|
|
20
20
|
`relation_scope`, lists no records. A rule the policy does not define (`history?`,
|
|
21
21
|
`show_in_app?`, `export?`, a custom authorization key) resolves to the policy's `manage?`, so a
|
|
22
22
|
permissive `manage?` grants every admin action the policy leaves undefined.
|
|
23
|
+
- **ActionPolicy context fails loudly.** A context key a policy declares that the host omits, or
|
|
24
|
+
whose block returns `nil`, raises `ActionPolicy::AuthorizationContextMissing` on every adapter
|
|
25
|
+
check rather than listing or allowing anything; `user` cannot be replaced through `context:`.
|
|
23
26
|
- **Destructive actions.** `delete` and `bulk_delete` render a confirmation page and additionally
|
|
24
27
|
carry `data-turbo-confirm` on the destructive submit (Turbo's confirmation, not `@rails/ujs`).
|
|
25
28
|
- **Mass assignment.** Submitted attributes are sliced to the model config's `visible_fields`
|
|
@@ -190,13 +190,25 @@ module RailsAdminNext
|
|
|
190
190
|
# config.authorize_with :action_policy
|
|
191
191
|
# end
|
|
192
192
|
#
|
|
193
|
+
# @example ActionPolicy with authorization context beyond the user
|
|
194
|
+
# RailsAdminNext.config do |config|
|
|
195
|
+
# config.authorize_with :action_policy, context: {account: -> { _current_user.account }}
|
|
196
|
+
# end
|
|
197
|
+
#
|
|
198
|
+
# An adapter may define a class method validate_options!, which receives the extra arguments
|
|
199
|
+
# while this config block runs, so a misconfigured option fails at boot.
|
|
200
|
+
#
|
|
193
201
|
# A custom adapter is registered with
|
|
194
202
|
# RailsAdminNext.add_extension(key, mod, authorization: true) before it is named here.
|
|
195
203
|
# See docs/UPGRADING.md for the adapter contract.
|
|
196
204
|
#
|
|
197
205
|
# @see RailsAdminNext::Config::DEFAULT_AUTHORIZE
|
|
198
|
-
def authorize_with(*args, &block)
|
|
206
|
+
def authorize_with(*args, **options, &block)
|
|
199
207
|
extension = args.shift
|
|
208
|
+
if extension.nil? && options.any?
|
|
209
|
+
raise ArgumentError, "authorize_with options need an adapter key, such as authorize_with(:action_policy, **options)"
|
|
210
|
+
end
|
|
211
|
+
|
|
200
212
|
if extension
|
|
201
213
|
klass = RailsAdminNext::AUTHORIZATION_ADAPTERS.fetch(extension) do
|
|
202
214
|
raise ArgumentError, "Unknown authorization adapter #{extension.inspect}. " \
|
|
@@ -206,8 +218,9 @@ module RailsAdminNext
|
|
|
206
218
|
"(no per-record checks or list scoping). See docs/UPGRADING.md."
|
|
207
219
|
end
|
|
208
220
|
klass.setup if klass.respond_to? :setup
|
|
221
|
+
klass.validate_options!(*args, **options) if klass.respond_to? :validate_options!
|
|
209
222
|
@authorize = proc do
|
|
210
|
-
@authorization_adapter = klass.new(*([self] + args).compact, &block)
|
|
223
|
+
@authorization_adapter = klass.new(*([self] + args).compact, **options, &block)
|
|
211
224
|
end
|
|
212
225
|
elsif block
|
|
213
226
|
@authorize = block
|
|
@@ -11,8 +11,19 @@ module RailsAdminNext
|
|
|
11
11
|
authorize :user, through: -> { @controller._current_user }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def self.validate_options!(context: {})
|
|
15
|
+
context.each do |key, value|
|
|
16
|
+
raise ArgumentError, "ActionPolicy context key #{key.inspect} must be a Symbol" unless key.is_a?(Symbol)
|
|
17
|
+
raise ArgumentError, "ActionPolicy context #{key.inspect} is the current user; set it with config.current_user_method" if key == :user
|
|
18
|
+
unless value.is_a?(Proc) && value.parameters.none? { |type, _| type in :req | :keyreq }
|
|
19
|
+
raise ArgumentError, "ActionPolicy context #{key.inspect} must be a block taking no arguments, run on the admin controller"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def initialize(controller, context: {})
|
|
15
25
|
@controller = controller
|
|
26
|
+
@context = context
|
|
16
27
|
end
|
|
17
28
|
|
|
18
29
|
def authorize(action, abstract_model = nil, model_object = nil)
|
|
@@ -45,6 +56,10 @@ module RailsAdminNext
|
|
|
45
56
|
|
|
46
57
|
private
|
|
47
58
|
|
|
59
|
+
def build_authorization_context
|
|
60
|
+
super.merge(@context.transform_values { @controller.instance_exec(&it) })
|
|
61
|
+
end
|
|
62
|
+
|
|
48
63
|
def policy_class_for(record)
|
|
49
64
|
::ActionPolicy.lookup(record, default: ::ApplicationPolicy)
|
|
50
65
|
end
|
|
@@ -12,6 +12,10 @@ module RailsAdminNext
|
|
|
12
12
|
RailsAdminNext::Extensions::ControllerExtension.include defined?(::Pundit::Authorization) ? ::Pundit::Authorization : ::Pundit
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
# Pundit takes no options, so any passed to authorize_with fail at boot.
|
|
16
|
+
def self.validate_options!
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
# See the +authorize_with+ config method for where the initialization happens.
|
|
16
20
|
def initialize(controller)
|
|
17
21
|
@controller = controller
|