action_policy 0.4.2 → 0.4.3
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/.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
|