action_policy 0.4.3 → 0.4.4
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/.github/ISSUE_TEMPLATE.md +4 -1
- data/.github/bug_report_template.rb +175 -0
- data/CHANGELOG.md +18 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -7
- data/benchmarks/namespaced_lookup_cache.rb +8 -5
- data/benchmarks/pre_checks.rb +73 -0
- data/docs/caching.md +1 -1
- data/docs/lookup_chain.md +6 -1
- data/docs/namespaces.md +2 -2
- data/docs/quick_start.md +1 -1
- data/docs/testing.md +57 -0
- data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
- data/lib/action_policy/lookup_chain.rb +15 -9
- data/lib/action_policy/policy/reasons.rb +1 -1
- data/lib/action_policy/rails/ext/active_record.rb +7 -0
- data/lib/action_policy/rspec/dsl.rb +2 -2
- data/lib/action_policy/version.rb +1 -1
- data/lib/generators/action_policy/install/templates/application_policy.rb +1 -1
- metadata +5 -4
- data/.github/FUNDING.yml +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa234de7b74f58df4707ec00f9f4d67bee0d89fb721f2b0e4df90627970530e6
|
4
|
+
data.tar.gz: e6e4ba56d3be720b8ddac3a236f42c84eec2472d04b95213914d89e23d291bf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 450572c0987f8d4174ff6c51fdd188d62bd5dfd288b381a593030ee9ef8575df74852b1600706f6dfb1c115fddb9a45e518f03b2dd0a118e653c0a13c0efc05a
|
7
|
+
data.tar.gz: 1fa6dac85f5fe5f014026d23357c08e9dba0101b68a07a96315275a1ce2aa28762ab82abffbf0ae985a9a277d942de23c521223d485b7acfe1fdceeae9c4179d
|
data/.github/ISSUE_TEMPLATE.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<!--
|
2
|
-
This template is for bug reports. If you are reporting a bug, please continue on. If you are here for another reason,
|
2
|
+
This template is for bug reports. If you are reporting a bug, please continue on. If you are here for another reason,
|
3
3
|
feel free to skip the rest of this template.
|
4
4
|
-->
|
5
5
|
|
@@ -11,6 +11,9 @@
|
|
11
11
|
|
12
12
|
**Action Policy Version:**
|
13
13
|
|
14
|
+
**Reproduction Script:** Use [this template](https://github.com/palkan/action_policy/blob/master/.github/bug_report_template.rb) to
|
15
|
+
create a standalone reproduction script. That would help us to fix the problem quicker. Thanks!
|
16
|
+
|
14
17
|
### What did you do?
|
15
18
|
|
16
19
|
### What did you expect to happen?
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/inline"
|
4
|
+
|
5
|
+
# This reproduction script allows you to test Action Policy with Rails.
|
6
|
+
# It contains:
|
7
|
+
# - Headless User model
|
8
|
+
# - UserPolicy
|
9
|
+
# - UsersController
|
10
|
+
# - Example tests for the controller.
|
11
|
+
#
|
12
|
+
# Update the classes to reproduce the failing case.
|
13
|
+
#
|
14
|
+
# Run the script as follows:
|
15
|
+
#
|
16
|
+
# $ ruby bug_report_template.rb
|
17
|
+
gemfile(true) do
|
18
|
+
source "https://rubygems.org"
|
19
|
+
|
20
|
+
gem "rails", "~> 6.0"
|
21
|
+
gem "action_policy", "~> 0.4"
|
22
|
+
|
23
|
+
gem "pry-byebug", platform: :mri
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rails"
|
27
|
+
require "action_controller/railtie"
|
28
|
+
require "action_policy"
|
29
|
+
|
30
|
+
require "minitest/autorun"
|
31
|
+
|
32
|
+
module Buggy
|
33
|
+
class Application < Rails::Application
|
34
|
+
config.logger = Logger.new("/dev/null")
|
35
|
+
config.eager_load = false
|
36
|
+
|
37
|
+
initializer "routes" do
|
38
|
+
Rails.application.routes.draw do
|
39
|
+
get ":controller(/:action)"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Rails.application.initialize!
|
46
|
+
|
47
|
+
class User
|
48
|
+
include Comparable
|
49
|
+
|
50
|
+
attr_reader :name
|
51
|
+
|
52
|
+
def initialize(name)
|
53
|
+
@name = name
|
54
|
+
end
|
55
|
+
|
56
|
+
def admin?
|
57
|
+
name == "admin"
|
58
|
+
end
|
59
|
+
|
60
|
+
def <=>(other)
|
61
|
+
return super unless other.is_a?(User)
|
62
|
+
name <=> other.name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class UserPolicy < ActionPolicy::Base
|
67
|
+
def index?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def create?
|
72
|
+
user.admin?
|
73
|
+
end
|
74
|
+
|
75
|
+
def show?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def manage?
|
80
|
+
user.admin? && !record.admin?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class UsersController < ActionController::Base
|
85
|
+
authorize :user, through: :current_user
|
86
|
+
|
87
|
+
before_action :set_user, only: [:update, :show]
|
88
|
+
|
89
|
+
def index
|
90
|
+
authorize!
|
91
|
+
render plain: "OK"
|
92
|
+
end
|
93
|
+
|
94
|
+
def create
|
95
|
+
authorize!
|
96
|
+
render plain: "OK"
|
97
|
+
end
|
98
|
+
|
99
|
+
def update
|
100
|
+
render plain: "OK"
|
101
|
+
end
|
102
|
+
|
103
|
+
def show
|
104
|
+
if allowed_to?(:update?, @user)
|
105
|
+
render plain: "OK"
|
106
|
+
else
|
107
|
+
render plain: "Read-only"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def current_user
|
112
|
+
@current_user ||= User.new(params[:user])
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def set_user
|
118
|
+
@user = User.new(params[:target])
|
119
|
+
authorize! @user
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class TestBugReproduction < ActionController::TestCase
|
124
|
+
tests UsersController
|
125
|
+
|
126
|
+
def before_setup
|
127
|
+
@routes = Rails.application.routes
|
128
|
+
super
|
129
|
+
end
|
130
|
+
|
131
|
+
def teardown
|
132
|
+
ActionPolicy::PerThreadCache.clear_all
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_index
|
136
|
+
get :index, params: {user: "guest"}
|
137
|
+
assert_equal "OK", response.body
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_create_failed
|
141
|
+
e = assert_raises(ActionPolicy::Unauthorized) do
|
142
|
+
post :create, params: {user: "guest"}
|
143
|
+
end
|
144
|
+
|
145
|
+
assert_equal UserPolicy, e.policy
|
146
|
+
assert_equal :create?, e.rule
|
147
|
+
assert e.result.reasons.is_a?(::ActionPolicy::Policy::FailureReasons)
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_create_succeed
|
151
|
+
post :create, params: {user: "admin"}
|
152
|
+
assert_equal "OK", response.body
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_update_failed
|
156
|
+
assert_raises(ActionPolicy::Unauthorized) do
|
157
|
+
patch :update, params: {user: "admin", target: "admin"}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_update_succeed
|
162
|
+
patch :update, params: {user: "admin", target: "guest"}
|
163
|
+
assert_equal "OK", response.body
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_show
|
167
|
+
get :show, params: {user: "admin", target: "guest"}
|
168
|
+
assert_equal "OK", response.body
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_show_admin
|
172
|
+
get :show, params: {user: "admin", target: "admin"}
|
173
|
+
assert_equal "Read-only", response.body
|
174
|
+
end
|
175
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,23 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.4.4 (2020-07-07)
|
6
|
+
|
7
|
+
- Fix symbol lookup with namespaces. ([@palkan][])
|
8
|
+
|
9
|
+
Fixes [#122](https://github.com/palkan/action_policy/issues/122).
|
10
|
+
|
11
|
+
- Separated `#classify`-based and `#camelize`-based symbol lookups. ([Be-ngt-oH][])
|
12
|
+
|
13
|
+
Only affects Rails apps. Now lookup for `:users` tries to find `UsersPolicy` first (camelize),
|
14
|
+
and only then search for `UserPolicy` (classify).
|
15
|
+
|
16
|
+
See [PR#118](https://github.com/palkan/action_policy/pull/118).
|
17
|
+
|
18
|
+
- Fix calling rules with `allowed_to?` directly. ([@palkan][])
|
19
|
+
|
20
|
+
Fixes [#113](https://github.com/palkan/action_policy/issues/113)
|
21
|
+
|
5
22
|
## 0.4.3 (2019-12-14)
|
6
23
|
|
7
24
|
- Add `#cache(*parts, **options) { ... }` method. ([@palkan][])
|
@@ -382,3 +399,4 @@
|
|
382
399
|
[@korolvs]: https://github.com/korolvs
|
383
400
|
[@nicolas-brousse]: https://github.com/nicolas-brousse
|
384
401
|
[@somenugget]: https://github.com/somenugget
|
402
|
+
[@Be-ngt-oH]: https://github.com/Be-ngt-oH
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2018-
|
3
|
+
Copyright (c) 2018-2020 Vladimir Dementyev
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
[](https://travis-ci.org/palkan/action_policy)
|
3
3
|
[](https://actionpolicy.evilmartians.io)
|
4
4
|
|
5
|
-
#
|
5
|
+
# Action Policy
|
6
6
|
|
7
7
|
Authorization framework for Ruby and Rails applications.
|
8
8
|
|
@@ -98,7 +98,7 @@ There is also an `allowed_to?` method which returns `true` or `false`, and could
|
|
98
98
|
<% @posts.each do |post| %>
|
99
99
|
<li><%= post.title %>
|
100
100
|
<% if allowed_to?(:edit?, post) %>
|
101
|
-
|
101
|
+
<%= link_to post, "Edit">
|
102
102
|
<% end %>
|
103
103
|
</li>
|
104
104
|
<% end %>
|
@@ -121,8 +121,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan
|
|
121
121
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
122
122
|
|
123
123
|
[Documentation]: http://actionpolicy.evilmartians.io
|
124
|
-
|
125
|
-
## Security Contact
|
126
|
-
|
127
|
-
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
|
128
|
-
|
@@ -39,6 +39,9 @@ if ENV["NO_CACHE"]
|
|
39
39
|
ActionPolicy::LookupChain.namespace_cache_enabled = false
|
40
40
|
end
|
41
41
|
|
42
|
+
results_path = File.join(__dir__, "../tmp/#{__FILE__.sub(/\.rb$/, ".txt")}")
|
43
|
+
FileUtils.mkdir_p File.dirname(results_path)
|
44
|
+
|
42
45
|
Benchmark.ips do |x|
|
43
46
|
x.warmup = 0
|
44
47
|
|
@@ -58,14 +61,14 @@ Benchmark.ips do |x|
|
|
58
61
|
ActionPolicy.lookup(b, namespace: X::Y::Z)
|
59
62
|
end
|
60
63
|
|
61
|
-
x.hold!
|
64
|
+
x.hold! results_path
|
62
65
|
|
63
66
|
x.compare!
|
64
67
|
end
|
65
68
|
|
66
69
|
#
|
67
70
|
# Comparison:
|
68
|
-
# cache
|
69
|
-
# cache
|
70
|
-
#
|
71
|
-
#
|
71
|
+
# cache A: 204788.3 i/s
|
72
|
+
# cache B: 202536.9 i/s - same-ish: difference falls within error
|
73
|
+
# no cache A: 127772.8 i/s - same-ish: difference falls within error
|
74
|
+
# no cache B: 63487.2 i/s - 3.23x slower
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This benchmark measures the difference between catch/throw and jump-less implementation.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
6
|
+
|
7
|
+
require "action_policy"
|
8
|
+
require "benchmark/ips"
|
9
|
+
|
10
|
+
GC.disable
|
11
|
+
|
12
|
+
class A; end
|
13
|
+
|
14
|
+
class B; end
|
15
|
+
|
16
|
+
class APolicy < ActionPolicy::Base
|
17
|
+
authorize :user, optional: true
|
18
|
+
|
19
|
+
pre_check :do_nothing, :deny_all
|
20
|
+
|
21
|
+
def show?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_nothing
|
26
|
+
end
|
27
|
+
|
28
|
+
def deny_all
|
29
|
+
deny!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class BPolicy < APolicy
|
34
|
+
def run_pre_checks(rule)
|
35
|
+
catch :policy_fulfilled do
|
36
|
+
self.class.pre_checks.each do |check|
|
37
|
+
next unless check.applicable?(rule)
|
38
|
+
check.call(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
return yield if block_given?
|
42
|
+
end
|
43
|
+
|
44
|
+
result.value
|
45
|
+
end
|
46
|
+
|
47
|
+
def deny!
|
48
|
+
result.load false
|
49
|
+
throw :policy_fulfilled
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
a = A.new
|
54
|
+
|
55
|
+
(APolicy.new(record: a).apply(:show?) == BPolicy.new(record: a).apply(:show?)) || raise("Implementations are not equal")
|
56
|
+
|
57
|
+
Benchmark.ips do |x|
|
58
|
+
x.warmup = 0
|
59
|
+
|
60
|
+
x.report("loop pre-check") do
|
61
|
+
APolicy.new(record: a).apply(:show?)
|
62
|
+
end
|
63
|
+
|
64
|
+
x.report("catch/throw pre-check") do
|
65
|
+
BPolicy.new(record: a).apply(:show?)
|
66
|
+
end
|
67
|
+
|
68
|
+
x.compare!
|
69
|
+
end
|
70
|
+
|
71
|
+
# Comparison:
|
72
|
+
# catch/throw pre-check: 286094.2 i/s
|
73
|
+
# loop pre-check: 184786.1 i/s - 1.55x slower
|
data/docs/caching.md
CHANGED
@@ -162,7 +162,7 @@ Rails.application.configure do |config|
|
|
162
162
|
end
|
163
163
|
```
|
164
164
|
|
165
|
-
Cache store must provide at least a `#read(key)` and
|
165
|
+
Cache store must provide at least a `#read(key)` and `#write(key, value, **options)` methods.
|
166
166
|
|
167
167
|
**NOTE:** cache store also should take care of serialiation/deserialization since the `value` is `ExecutionResult` instance (which contains also some additional information, e.g. failure reasons). Rails cache store supports serialization/deserialization out-of-the-box.
|
168
168
|
|
data/docs/lookup_chain.md
CHANGED
@@ -2,7 +2,12 @@
|
|
2
2
|
|
3
3
|
Action Policy tries to automatically infer policy class from the target using the following _probes_:
|
4
4
|
|
5
|
-
1. If the target is a `Symbol
|
5
|
+
1. If the target is a `Symbol`:
|
6
|
+
|
7
|
+
a) Try `"#{target.to_s.camelize}Policy"` as a `policy_name` (see below);
|
8
|
+
|
9
|
+
b) If `String#classify` is available, e.g. when using Rails' ActiveSupport, try `"#{target.to_s.classify}Policy"`;
|
10
|
+
|
6
11
|
2. If the target responds to `policy_class`, then use it;
|
7
12
|
3. If the target's class responds to `policy_class`, then use it;
|
8
13
|
4. If the target or the target's class responds to `policy_name`, then use it (the `policy_name` should end with `Policy` as it's not appended automatically);
|
data/docs/namespaces.md
CHANGED
@@ -6,7 +6,7 @@ Consider an example:
|
|
6
6
|
|
7
7
|
```ruby
|
8
8
|
module Admin
|
9
|
-
class UsersController <
|
9
|
+
class UsersController < ApplicationController
|
10
10
|
def index
|
11
11
|
# uses Admin::UserPolicy if any, otherwise fallbacks to UserPolicy
|
12
12
|
authorize!
|
@@ -20,7 +20,7 @@ Module nesting is also supported:
|
|
20
20
|
```ruby
|
21
21
|
module Admin
|
22
22
|
module Client
|
23
|
-
class UsersController <
|
23
|
+
class UsersController < ApplicationController
|
24
24
|
def index
|
25
25
|
# lookup for Admin::Client::UserPolicy -> Admin::UserPolicy -> UserPolicy
|
26
26
|
authorize!
|
data/docs/quick_start.md
CHANGED
@@ -98,7 +98,7 @@ There is also an `allowed_to?` method which returns `true` or `false` and could
|
|
98
98
|
<% @posts.each do |post| %>
|
99
99
|
<li><%= post.title %>
|
100
100
|
<% if allowed_to?(:edit?, post) %>
|
101
|
-
|
101
|
+
<%= link_to "Edit", post %>
|
102
102
|
<% end %>
|
103
103
|
</li>
|
104
104
|
<% end %>
|
data/docs/testing.md
CHANGED
@@ -112,6 +112,8 @@ OR
|
|
112
112
|
|
113
113
|
### Testing scopes
|
114
114
|
|
115
|
+
#### Active Record relation example
|
116
|
+
|
115
117
|
There is no single rule on how to test scopes, 'cause it dependes on the _nature_ of the scope.
|
116
118
|
|
117
119
|
Here's an example of RSpec tests for Active Record scoping rules:
|
@@ -156,6 +158,35 @@ describe PostPolicy do
|
|
156
158
|
end
|
157
159
|
```
|
158
160
|
|
161
|
+
#### Action Controller params example
|
162
|
+
|
163
|
+
Here's an example of RSpec tests for Action Controller parameters scoping rules:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
describe PostPolicy do
|
167
|
+
describe "params scope" do
|
168
|
+
let(:user) { build_stubbed :user }
|
169
|
+
let(:context) { {user: user} }
|
170
|
+
|
171
|
+
let(:params) { {name: "a", password: "b"} }
|
172
|
+
let(:target) { ActionController::Parameters.new(params) }
|
173
|
+
|
174
|
+
# it's easier to asses the hash representation, not the AC::Params object
|
175
|
+
subject { policy.apply_scope(target, type: :action_controller_params).to_h }
|
176
|
+
|
177
|
+
context "as user" do
|
178
|
+
it { is_expected.to eq({name: "a"}) }
|
179
|
+
end
|
180
|
+
|
181
|
+
context "as manager" do
|
182
|
+
before { user.update!(role: :manager) }
|
183
|
+
|
184
|
+
it { is_expected.to eq({name: "a", password: "b"}) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
159
190
|
## Testing authorization
|
160
191
|
|
161
192
|
To test the act of authorization you have to make sure that the `authorize!` method is called with the appropriate arguments.
|
@@ -331,3 +362,29 @@ expect { subject }.to have_authorized_scope(:scope)
|
|
331
362
|
expect(target).to eq(User.all)
|
332
363
|
}
|
333
364
|
```
|
365
|
+
|
366
|
+
|
367
|
+
## Testing views
|
368
|
+
|
369
|
+
When you test views that call policies methods as `allowed_to?`, your may have `Missing policy authorization context: user` error.
|
370
|
+
You may need to stub `current_user` to resolve the issue.
|
371
|
+
|
372
|
+
Consider an RSpec example:
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
describe "users/index.html.slim" do
|
376
|
+
let(:user) { build_stubbed :user }
|
377
|
+
let(:users) { create_list(:user, 2) }
|
378
|
+
|
379
|
+
before do
|
380
|
+
allow(controller).to receive(:current_user).and_return(user)
|
381
|
+
|
382
|
+
assign :users, users
|
383
|
+
render
|
384
|
+
end
|
385
|
+
|
386
|
+
describe "displays user#index correctly" do
|
387
|
+
it { expect(rendered).to have_link(users.first.email, href: edit_user_path(users.first)) }
|
388
|
+
end
|
389
|
+
end
|
390
|
+
```
|
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module ActionPolicy
|
4
4
|
module Ext
|
5
|
-
# Add `
|
6
|
-
module
|
5
|
+
# Add `camelize` to Symbol
|
6
|
+
module SymbolCamelize
|
7
7
|
refine Symbol do
|
8
|
-
if "".respond_to?(:
|
9
|
-
def
|
10
|
-
to_s.
|
8
|
+
if "".respond_to?(:camelize)
|
9
|
+
def camelize
|
10
|
+
to_s.camelize
|
11
11
|
end
|
12
12
|
else
|
13
|
-
def
|
13
|
+
def camelize
|
14
14
|
word = to_s.capitalize
|
15
15
|
word.gsub!(/(?:_)([a-z\d]*)/) { $1.capitalize }
|
16
16
|
word
|
@@ -12,8 +12,8 @@ module ActionPolicy
|
|
12
12
|
using ActionPolicy::Ext::StringConstantize
|
13
13
|
end
|
14
14
|
|
15
|
-
require "action_policy/ext/
|
16
|
-
using ActionPolicy::Ext::
|
15
|
+
require "action_policy/ext/symbol_camelize"
|
16
|
+
using ActionPolicy::Ext::SymbolCamelize
|
17
17
|
|
18
18
|
require "action_policy/ext/module_namespace"
|
19
19
|
using ActionPolicy::Ext::ModuleNamespace
|
@@ -54,6 +54,7 @@ module ActionPolicy
|
|
54
54
|
private
|
55
55
|
|
56
56
|
def lookup_within_namespace(policy_name, namespace)
|
57
|
+
return unless namespace
|
57
58
|
NamespaceCache.fetch(namespace.name, policy_name) do
|
58
59
|
mod = namespace
|
59
60
|
|
@@ -114,20 +115,25 @@ module ActionPolicy
|
|
114
115
|
SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
|
115
116
|
next unless record.is_a?(Symbol)
|
116
117
|
|
117
|
-
policy_name = "#{record.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
policy_name = "#{record.camelize}Policy"
|
119
|
+
lookup_within_namespace(policy_name, namespace) || policy_name.safe_constantize
|
120
|
+
}
|
121
|
+
|
122
|
+
# (Optional) Infer using String#classify if available
|
123
|
+
CLASSIFY_SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
|
124
|
+
next unless record.is_a?(Symbol)
|
125
|
+
|
126
|
+
policy_name = "#{record.to_s.classify}Policy"
|
127
|
+
lookup_within_namespace(policy_name, namespace) || policy_name.safe_constantize
|
123
128
|
}
|
124
129
|
|
125
130
|
self.chain = [
|
126
131
|
SYMBOL_LOOKUP,
|
132
|
+
(CLASSIFY_SYMBOL_LOOKUP if String.method_defined?(:classify)),
|
127
133
|
INSTANCE_POLICY_CLASS,
|
128
134
|
CLASS_POLICY_CLASS,
|
129
135
|
NAMESPACE_LOOKUP,
|
130
136
|
INFER_FROM_CLASS
|
131
|
-
]
|
137
|
+
].compact
|
132
138
|
end
|
133
139
|
end
|
@@ -7,4 +7,11 @@ ActiveRecord::Relation.include(Module.new do
|
|
7
7
|
def policy_cache_key
|
8
8
|
object_id
|
9
9
|
end
|
10
|
+
|
11
|
+
# Explicitly define the policy_class method to avoid
|
12
|
+
# making Relation immutable (by initializing `arel` in `#respond_to_missing?).
|
13
|
+
# See https://github.com/palkan/action_policy/issues/101
|
14
|
+
def policy_class
|
15
|
+
nil
|
16
|
+
end
|
10
17
|
end)
|
@@ -68,7 +68,7 @@ if defined?(::RSpec)
|
|
68
68
|
|
69
69
|
::RSpec.shared_examples_for "action_policy:policy_rule_example" do |success, the_caller|
|
70
70
|
if success
|
71
|
-
specify do
|
71
|
+
specify "is allowed" do
|
72
72
|
next if subject.success?
|
73
73
|
raise(
|
74
74
|
RSpec::Expectations::ExpectationNotMetError,
|
@@ -77,7 +77,7 @@ if defined?(::RSpec)
|
|
77
77
|
)
|
78
78
|
end
|
79
79
|
else
|
80
|
-
specify do
|
80
|
+
specify "is denied" do
|
81
81
|
next if subject.fail?
|
82
82
|
raise(
|
83
83
|
RSpec::Expectations::ExpectationNotMetError,
|
@@ -5,7 +5,7 @@ class ApplicationPolicy < ActionPolicy::Base
|
|
5
5
|
#
|
6
6
|
# authorize :account, optional: true
|
7
7
|
#
|
8
|
-
# Read more about
|
8
|
+
# Read more about authorization context: https://actionpolicy.evilmartians.io/#/authorization_context
|
9
9
|
|
10
10
|
private
|
11
11
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_policy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ammeter
|
@@ -158,9 +158,9 @@ extensions: []
|
|
158
158
|
extra_rdoc_files: []
|
159
159
|
files:
|
160
160
|
- ".gitattributes"
|
161
|
-
- ".github/FUNDING.yml"
|
162
161
|
- ".github/ISSUE_TEMPLATE.md"
|
163
162
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
163
|
+
- ".github/bug_report_template.rb"
|
164
164
|
- ".gitignore"
|
165
165
|
- ".rubocop.yml"
|
166
166
|
- ".tidelift.yml"
|
@@ -172,6 +172,7 @@ files:
|
|
172
172
|
- Rakefile
|
173
173
|
- action_policy.gemspec
|
174
174
|
- benchmarks/namespaced_lookup_cache.rb
|
175
|
+
- benchmarks/pre_checks.rb
|
175
176
|
- bin/console
|
176
177
|
- bin/setup
|
177
178
|
- docs/.nojekyll
|
@@ -235,7 +236,7 @@ files:
|
|
235
236
|
- lib/action_policy/ext/string_constantize.rb
|
236
237
|
- lib/action_policy/ext/string_match.rb
|
237
238
|
- lib/action_policy/ext/string_underscore.rb
|
238
|
-
- lib/action_policy/ext/
|
239
|
+
- lib/action_policy/ext/symbol_camelize.rb
|
239
240
|
- lib/action_policy/ext/yield_self_then.rb
|
240
241
|
- lib/action_policy/i18n.rb
|
241
242
|
- lib/action_policy/lookup_chain.rb
|
data/.github/FUNDING.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
tidelift: "rubygems/action_policy"
|