action_policy 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/palkan/action_policy.svg?branch=master)](https://travis-ci.org/palkan/action_policy)
|
3
3
|
[![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](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"
|