action_policy 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG.md +11 -0
- data/docs/README.md +2 -0
- data/docs/caching.md +21 -3
- data/gemfiles/rails42.gemfile +1 -0
- data/lib/action_policy/ext/policy_cache_key.rb +8 -6
- data/lib/action_policy/policy/authorization.rb +7 -11
- data/lib/action_policy/policy/cache.rb +26 -4
- data/lib/action_policy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6530513fc2b087beae97b3b33e662f421a0f12911a1e21b2cce9778dc7f693c9
|
4
|
+
data.tar.gz: a915127b22da7b43cb0fc1aa1048c8a4d9c0b5a51f458039594eac17b29e92bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a34ac6a7ab8289521e2ccb977ed6c6b6e655a185deb053676aad2d78674b7367d04c75abd552dafd59b1ae46465cfa117ae97239b9fd2e3cc6947d020d80775
|
7
|
+
data.tar.gz: 991f5b553314cf11dedaab9964d5d4b4ee384877474ed6dc814fa0c4d4bdf383b8288df46bf987a437b82a33f859ac5314a1c4bd75b1df28d8a2438886f37c40
|
data/.travis.yml
CHANGED
@@ -18,11 +18,11 @@ matrix:
|
|
18
18
|
gemfile: gemfiles/railsmaster.gemfile
|
19
19
|
- rvm: jruby-9.2.8.0
|
20
20
|
gemfile: gemfiles/jruby.gemfile
|
21
|
-
- rvm: 2.6.
|
21
|
+
- rvm: 2.6.5
|
22
22
|
gemfile: gemfiles/rails6.gemfile
|
23
23
|
- rvm: 2.5.3
|
24
24
|
gemfile: Gemfile
|
25
|
-
- rvm: 2.
|
25
|
+
- rvm: 2.5.3
|
26
26
|
gemfile: gemfiles/rails42.gemfile
|
27
27
|
allow_failures:
|
28
28
|
- rvm: ruby-head
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.4.3 (2019-12-14)
|
6
|
+
|
7
|
+
- Add `#cache(*parts, **options) { ... }` method. ([@palkan][])
|
8
|
+
|
9
|
+
Allows you to cache anything in policy classes using the Action Policy
|
10
|
+
cache key generation mechanism.
|
11
|
+
|
12
|
+
- Handle versioned Rails cache keys. ([@palkan][])
|
13
|
+
|
14
|
+
Use `#cache_with_version` as a cache key if defined.
|
15
|
+
|
5
16
|
## 0.4.2 (2019-12-13)
|
6
17
|
|
7
18
|
- Fix regression introduced in 0.4.0 which broke testing Class targets. ([@palkan][])
|
data/docs/README.md
CHANGED
@@ -65,6 +65,8 @@ Learn more about the motivation behind the Action Policy and its features by wat
|
|
65
65
|
|
66
66
|
## Resources
|
67
67
|
|
68
|
+
- RubyRussia, 2019 "Welcome, or access denied?" talk ([video](https://www.youtube.com/watch?v=y15a2g7v8i0) [RU], [slides](https://speakerdeck.com/palkan/rubyrussia-2019-welcome-or-access-denied))
|
69
|
+
|
68
70
|
- Seattle.rb, 2019 "A Denial!" talk [[slides](https://speakerdeck.com/palkan/seattle-dot-rb-2019-a-denial)]
|
69
71
|
|
70
72
|
- RailsConf, 2018 "Access Denied" talk [[video](https://www.youtube.com/watch?v=NVwx0DARDis), [slides](https://speakerdeck.com/palkan/railsconf-2018-access-denied-the-missing-guide-to-authorization-in-rails)]
|
data/docs/caching.md
CHANGED
@@ -166,7 +166,7 @@ Cache store must provide at least a `#read(key)` and `write(key, value, **option
|
|
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
|
|
169
|
-
By default, Action Policy builds a cache key using the following scheme:
|
169
|
+
By default, Action Policy builds a cache key using the following scheme (defined in `#rule_cache_key(rule)` method):
|
170
170
|
|
171
171
|
```ruby
|
172
172
|
"#{cache_namespace}/#{context_cache_key}" \
|
@@ -175,11 +175,29 @@ By default, Action Policy builds a cache key using the following scheme:
|
|
175
175
|
|
176
176
|
Where `cache_namespace` is equal to `"acp:#{MAJOR_GEM_VERSION}.#{MINOR_GEM_VERSION}"`, and `context_cache_key` is a concatenation of all authorization contexts cache keys (in the same order as they are defined in the policy class).
|
177
177
|
|
178
|
-
If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key
|
178
|
+
If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key` (or `#cache_key_with_version` for modern Rails versions). If `#cache_key` is not defined, an `ArgumentError` is raised.
|
179
179
|
|
180
180
|
**NOTE:** if your `#cache_key` method is performance-heavy (e.g. like the `ActiveRecord::Relation`'s one), we recommend to explicitly define the `#policy_cache_key` method on the corresponding class to avoid unnecessary load. See also [action_policy#55](https://github.com/palkan/action_policy/issues/55).
|
181
181
|
|
182
|
-
You can define your own `
|
182
|
+
You can define your own `rule_cache_key` / `cache_namespace` / `context_cache_key` methods for policy class to override this logic.
|
183
|
+
|
184
|
+
You can also use the `#cache` instance method to cache arbitrary values in you policies:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class ApplicationPolicy < ActionPolicy::Base
|
188
|
+
# Suppose that a user has many roles each having an array of permissions
|
189
|
+
def permissions
|
190
|
+
cache(user) { user.roles.pluck(:permissions).flatten.uniq }
|
191
|
+
end
|
192
|
+
|
193
|
+
# You can pass multiple cache key "parts"
|
194
|
+
def account_permissions(account)
|
195
|
+
cache(user, account) { user.account_roles.where(account: account).pluck(:permissions).flatten.uniq }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
**NOTE:** `#cache` method uses the same cache key generation logic as rules caching (described above).
|
183
201
|
|
184
202
|
#### Invalidation
|
185
203
|
|
data/gemfiles/rails42.gemfile
CHANGED
@@ -13,19 +13,15 @@ module ActionPolicy
|
|
13
13
|
module ObjectExt
|
14
14
|
def _policy_cache_key(use_object_id: false)
|
15
15
|
return policy_cache_key if respond_to?(:policy_cache_key)
|
16
|
+
return cache_key_with_version if respond_to?(:cache_key_with_version)
|
16
17
|
return cache_key if respond_to?(:cache_key)
|
17
18
|
|
18
|
-
return object_id if use_object_id == true
|
19
|
+
return object_id.to_s if use_object_id == true
|
19
20
|
|
20
21
|
raise ArgumentError, "object is not cacheable"
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
# JRuby doesn't support _global_ modules refinements (see https://github.com/jruby/jruby/issues/5220)
|
25
|
-
# Fallback to monkey-patching.
|
26
|
-
# TODO: remove after 9.2.7.0 (See https://github.com/jruby/jruby/pull/5627)
|
27
|
-
::Object.include(ObjectExt) if RUBY_PLATFORM =~ /java/i
|
28
|
-
|
29
25
|
refine Object do
|
30
26
|
include ObjectExt
|
31
27
|
end
|
@@ -85,6 +81,12 @@ module ActionPolicy
|
|
85
81
|
to_s
|
86
82
|
end
|
87
83
|
end
|
84
|
+
|
85
|
+
refine Module do
|
86
|
+
def _policy_cache_key(*)
|
87
|
+
name
|
88
|
+
end
|
89
|
+
end
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
@@ -43,21 +43,17 @@ module ActionPolicy
|
|
43
43
|
|
44
44
|
attr_reader :authorization_context
|
45
45
|
|
46
|
-
def initialize(
|
47
|
-
super(
|
46
|
+
def initialize(record = nil, **params)
|
47
|
+
super(record)
|
48
48
|
|
49
49
|
@authorization_context = {}
|
50
50
|
|
51
51
|
self.class.authorization_targets.each do |id, opts|
|
52
|
-
|
53
|
-
val = params.fetch(id, nil)
|
54
|
-
else
|
55
|
-
raise AuthorizationContextMissing, id unless params.key?(id)
|
52
|
+
raise AuthorizationContextMissing, id unless params.key?(id) || opts[:optional]
|
56
53
|
|
57
|
-
|
54
|
+
val = params.fetch(id, nil)
|
58
55
|
|
59
|
-
|
60
|
-
end
|
56
|
+
raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
|
61
57
|
|
62
58
|
authorization_context[id] = instance_variable_set("@#{id}", val)
|
63
59
|
end
|
@@ -66,9 +62,9 @@ module ActionPolicy
|
|
66
62
|
end
|
67
63
|
|
68
64
|
module ClassMethods # :nodoc:
|
69
|
-
def authorize(*ids,
|
65
|
+
def authorize(*ids, allow_nil: false, optional: false)
|
70
66
|
ids.each do |id|
|
71
|
-
authorization_targets[id] =
|
67
|
+
authorization_targets[id] = {allow_nil: allow_nil || optional, optional: optional}
|
72
68
|
end
|
73
69
|
|
74
70
|
attr_reader(*ids)
|
@@ -30,9 +30,20 @@ module ActionPolicy # :nodoc:
|
|
30
30
|
ActionPolicy::CACHE_NAMESPACE
|
31
31
|
end
|
32
32
|
|
33
|
-
def cache_key(
|
34
|
-
|
35
|
-
|
33
|
+
def cache_key(*parts)
|
34
|
+
[
|
35
|
+
cache_namespace,
|
36
|
+
*parts
|
37
|
+
].map { |part| part._policy_cache_key }.join("/")
|
38
|
+
end
|
39
|
+
|
40
|
+
def rule_cache_key(rule)
|
41
|
+
cache_key(
|
42
|
+
context_cache_key,
|
43
|
+
record,
|
44
|
+
self.class,
|
45
|
+
rule
|
46
|
+
)
|
36
47
|
end
|
37
48
|
|
38
49
|
def context_cache_key
|
@@ -41,7 +52,7 @@ module ActionPolicy # :nodoc:
|
|
41
52
|
|
42
53
|
def apply_with_cache(rule)
|
43
54
|
options = self.class.cached_rules.fetch(rule)
|
44
|
-
key =
|
55
|
+
key = rule_cache_key(rule)
|
45
56
|
|
46
57
|
ActionPolicy.cache_store.then do |store|
|
47
58
|
@result = store.read(key)
|
@@ -62,6 +73,17 @@ module ActionPolicy # :nodoc:
|
|
62
73
|
apply_with_cache(rule) { super }
|
63
74
|
end
|
64
75
|
|
76
|
+
def cache(*parts, **options)
|
77
|
+
key = cache_key(*parts)
|
78
|
+
ActionPolicy.cache_store.then do |store|
|
79
|
+
res = store.read(key)
|
80
|
+
next res unless res.nil?
|
81
|
+
res = yield
|
82
|
+
store.write(key, res, **options)
|
83
|
+
res
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
65
87
|
module ClassMethods # :nodoc:
|
66
88
|
def cache(*rules, **options)
|
67
89
|
rules.each do |rule|
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ammeter
|