action_authorization 0.3.4 → 0.4.1
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 +142 -2
- data/lib/action_authorization/testing/integration_test_policy_helpers.rb +15 -0
- data/lib/action_authorization/testing/open_policy.rb +30 -0
- data/lib/action_authorization/testing/policy_helpers.rb +25 -0
- data/lib/action_authorization/testing/policy_stub.rb +12 -0
- data/lib/action_authorization/testing/view_test_policy_helpers.rb +20 -0
- data/lib/action_authorization/testing.rb +8 -0
- data/lib/action_authorization/version.rb +1 -1
- data/lib/action_authorization.rb +12 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 430535be149722c29d3564c7ec5a15e98d25fa282e064554e981516b9664cc18
|
|
4
|
+
data.tar.gz: 5ad0d3792560a97d336461b6996fd4005d1f2449d96015a5ad22b7ea87b125a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe5421e3d72a1652917f4ee9c6a37bd6615bf51575505b8b68d0767ef0e4ebfabd7baf86643d6501efc9e3a3d5c2319209e4190b0d703924699f2da12871de38
|
|
7
|
+
data.tar.gz: 12d1ec2a0bc2e94ddb54c1c4dcc36020dd096b614fd389419e76554a10d9b7578ae9fb7a2c37e1caac5b8a533b0ab83293ee507511ce40ca403d770af0847216
|
data/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# ActionAuthorization
|
|
2
|
+
|
|
2
3
|
A base policy class for authorizing controller actions with access to the current_user and object.
|
|
3
4
|
|
|
4
5
|
## Installation
|
|
6
|
+
|
|
5
7
|
Add this line to your application's Gemfile:
|
|
6
8
|
|
|
7
9
|
```ruby
|
|
@@ -9,19 +11,23 @@ gem 'action_authorization'
|
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
And then execute:
|
|
14
|
+
|
|
12
15
|
```bash
|
|
13
16
|
bundle
|
|
14
17
|
```
|
|
15
18
|
|
|
16
19
|
Or install it with:
|
|
20
|
+
|
|
17
21
|
```bash
|
|
18
22
|
gem install action_authorization
|
|
19
23
|
```
|
|
20
24
|
|
|
21
25
|
## Requirements
|
|
26
|
+
|
|
22
27
|
ActionAuthorization requires a **current_user** method that returns the currently logged in user.
|
|
23
28
|
|
|
24
29
|
## Usage
|
|
30
|
+
|
|
25
31
|
Include the ActionAuthorization module in your ApplicationController (or indvidual controller(s))
|
|
26
32
|
|
|
27
33
|
```ruby
|
|
@@ -31,7 +37,8 @@ end
|
|
|
31
37
|
```
|
|
32
38
|
|
|
33
39
|
Create an authorization policy for a resource.
|
|
34
|
-
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
35
42
|
class DocumentPolicy < ActionAuthorization::BasePolicy
|
|
36
43
|
def show?
|
|
37
44
|
document.owner == user
|
|
@@ -40,6 +47,7 @@ end
|
|
|
40
47
|
```
|
|
41
48
|
|
|
42
49
|
Call **authorize** method in controller action.
|
|
50
|
+
|
|
43
51
|
```ruby
|
|
44
52
|
class DocumentController < ApplicationController
|
|
45
53
|
def show
|
|
@@ -49,6 +57,7 @@ end
|
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
Pass a **policy_class** to authorize to override the default resource based policy.
|
|
60
|
+
|
|
52
61
|
```ruby
|
|
53
62
|
class DocumentController < ApplicationController
|
|
54
63
|
def show
|
|
@@ -57,11 +66,142 @@ class DocumentController < ApplicationController
|
|
|
57
66
|
end
|
|
58
67
|
```
|
|
59
68
|
|
|
69
|
+
By default, an unauthorized action raises `ActionAuthorization::AuthorizationFailure`.
|
|
70
|
+
Applications can handle it in their base controller and choose an appropriate response:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class ApplicationController < ActionController::Base
|
|
74
|
+
rescue_from ActionAuthorization::AuthorizationFailure, with: :authorization_failure
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def authorization_failure
|
|
79
|
+
head :forbidden
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `policy`
|
|
85
|
+
|
|
60
86
|
Check if authorized before displaying a link in the view.
|
|
61
87
|
|
|
62
88
|
```erb
|
|
63
89
|
<%= link_to(@document.name, @document) if policy(@document).show? %>
|
|
64
90
|
```
|
|
65
91
|
|
|
92
|
+
### `policy_for`
|
|
93
|
+
|
|
94
|
+
An enhanced version of `policy` that also accepts a `[parent, model]` pair for authorizing a new
|
|
95
|
+
nested record.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
policy_for([folder, Document])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This creates the model with the parent assigned to its association and returns its policy. Unlike
|
|
102
|
+
`folder.documents.build`, it does not add the new record to the parent's cached association.
|
|
103
|
+
|
|
104
|
+
When passed a single object, `policy_for` behaves the same as `policy`:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
policy_for(document)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Testing support
|
|
111
|
+
|
|
112
|
+
Testing helpers are opt-in and are loaded separately from the runtime API:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
require "action_authorization/testing"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The shared `PolicyHelpers` module provides `allow` and `forbid`. It requires including modules to
|
|
119
|
+
define `policy_class_default`, so include the specialized module for the test type rather than
|
|
120
|
+
including `PolicyHelpers` directly:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
class ActionDispatch::IntegrationTest
|
|
124
|
+
include ActionAuthorization::Testing::IntegrationTestPolicyHelpers
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class ActionView::TestCase
|
|
128
|
+
include ActionAuthorization::Testing::ViewTestPolicyHelpers
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`IntegrationTestPolicyHelpers` derives the policy class from the test class name (for example,
|
|
133
|
+
`DocumentsControllerTest` uses `DocumentPolicy`). `ViewTestPolicyHelpers` defaults to `OpenPolicy`
|
|
134
|
+
and includes `PolicyStub`, so `policy` returns an open policy in view tests. Both modules provide
|
|
135
|
+
`allow` and `forbid`.
|
|
136
|
+
|
|
137
|
+
If needed, `PolicyStub` can also be included separately in another test class to make `policy`
|
|
138
|
+
return an open policy:
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
class SomeHelperTest < ActiveSupport::TestCase
|
|
142
|
+
include ActionAuthorization::Testing::PolicyStub
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`OpenPolicy` allows every action by default, including non-standard predicates such as
|
|
147
|
+
`activate?`. Use `forbid(:activate)` to override a specific action.
|
|
148
|
+
|
|
149
|
+
The expectation helpers use the `any_instance.expects` API and require Mocha to be configured by
|
|
150
|
+
the client application's test suite.
|
|
151
|
+
|
|
152
|
+
### Examples
|
|
153
|
+
|
|
154
|
+
In an integration test, `allow` and `forbid` set the result of the action predicate on the policy
|
|
155
|
+
class inferred from the test class name. For `DocumentsControllerTest`, that is `DocumentPolicy`:
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
class DocumentsControllerTest < ActionDispatch::IntegrationTest
|
|
159
|
+
test "shows an authorized document" do
|
|
160
|
+
allow(:show)
|
|
161
|
+
|
|
162
|
+
get document_url(documents(:one))
|
|
163
|
+
assert_response :success
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
test "raises when showing a forbidden document" do
|
|
167
|
+
forbid(:show)
|
|
168
|
+
|
|
169
|
+
assert_raises(ActionAuthorization::AuthorizationFailure) do
|
|
170
|
+
get document_url(documents(:one))
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
In a view test, `ViewTestPolicyHelpers` uses `OpenPolicy`, so the link is allowed by default. Use
|
|
177
|
+
`forbid(:show)` to exercise the hidden-link case:
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
class DocumentsHelperTest < ActionView::TestCase
|
|
181
|
+
test "returns a link to an authorized document" do
|
|
182
|
+
assert link_to_document(documents(:one))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
test "returns nil when showing the document is forbidden" do
|
|
186
|
+
forbid(:show)
|
|
187
|
+
|
|
188
|
+
assert_nil link_to_document(documents(:one))
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Helpers that render a template or partial that calls `policy` need the view object stubbed too.
|
|
194
|
+
Call `view_policy_stub` in those tests:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
test "returns the document header" do
|
|
198
|
+
view_policy_stub
|
|
199
|
+
|
|
200
|
+
assert_includes document_header(documents(:one)), "Document"
|
|
201
|
+
end
|
|
202
|
+
```
|
|
203
|
+
|
|
66
204
|
## License
|
|
67
|
-
|
|
205
|
+
|
|
206
|
+
The gem is available as open source under the terms of the
|
|
207
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAuthorization
|
|
4
|
+
module Testing
|
|
5
|
+
module IntegrationTestPolicyHelpers
|
|
6
|
+
include PolicyHelpers
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def policy_class_default
|
|
11
|
+
self.class.name.sub("ControllerTest", "").singularize.concat("Policy").constantize
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAuthorization
|
|
4
|
+
module Testing
|
|
5
|
+
class OpenPolicy < ActionAuthorization::BasePolicy
|
|
6
|
+
def initialize
|
|
7
|
+
super(nil, nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Any predicate (ending in "?") is allowed, including non-standard actions.
|
|
11
|
+
def method_missing(method_name, *, &)
|
|
12
|
+
predicate?(method_name) || super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
16
|
+
predicate?(method_name) || super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def authorized?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def predicate?(method_name)
|
|
26
|
+
method_name.end_with?("?")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAuthorization
|
|
4
|
+
module Testing
|
|
5
|
+
module PolicyHelpers
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def allow(action, **)
|
|
9
|
+
policy_action_expectation(action, **)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def forbid(action, **)
|
|
13
|
+
policy_action_expectation(action, **, value: false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def policy_action_expectation(action, policy_class: policy_class_default, value: true)
|
|
17
|
+
policy_class.any_instance.expects("#{action}?").returns(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def policy_class_default
|
|
21
|
+
raise NotImplementedError, "You must implement policy_class_default in your test class"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAuthorization
|
|
4
|
+
module Testing
|
|
5
|
+
module ViewTestPolicyHelpers
|
|
6
|
+
include PolicyHelpers
|
|
7
|
+
include PolicyStub
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def policy_class_default
|
|
12
|
+
OpenPolicy
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def view_policy_stub
|
|
16
|
+
view.stubs(:policy).returns(policy)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_authorization"
|
|
4
|
+
require "action_authorization/testing/open_policy"
|
|
5
|
+
require "action_authorization/testing/policy_stub"
|
|
6
|
+
require "action_authorization/testing/policy_helpers"
|
|
7
|
+
require "action_authorization/testing/integration_test_policy_helpers"
|
|
8
|
+
require "action_authorization/testing/view_test_policy_helpers"
|
data/lib/action_authorization.rb
CHANGED
|
@@ -10,7 +10,7 @@ require "action_authorization/authorization_failure"
|
|
|
10
10
|
module ActionAuthorization
|
|
11
11
|
def self.included(base)
|
|
12
12
|
base.class_eval do
|
|
13
|
-
helper_method :policy
|
|
13
|
+
helper_method :policy, :policy_for
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -26,7 +26,18 @@ module ActionAuthorization
|
|
|
26
26
|
(policy_class || policy_class_for(object)).new(current_user, object)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def policy_for(object)
|
|
30
|
+
policy(object.is_a?(Array) ? policy_record_for(object) : object)
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
def policy_class_for(object)
|
|
30
34
|
object.nil? ? NullPolicy : "#{object.model_name}Policy".constantize
|
|
31
35
|
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def policy_record_for(array)
|
|
40
|
+
parent, model = array
|
|
41
|
+
model.new(parent.class.base_class.model_name.element.to_sym => parent)
|
|
42
|
+
end
|
|
32
43
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: action_authorization
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aaron Baldwin
|
|
@@ -39,6 +39,12 @@ files:
|
|
|
39
39
|
- lib/action_authorization/authorization_failure.rb
|
|
40
40
|
- lib/action_authorization/base_policy.rb
|
|
41
41
|
- lib/action_authorization/null_policy.rb
|
|
42
|
+
- lib/action_authorization/testing.rb
|
|
43
|
+
- lib/action_authorization/testing/integration_test_policy_helpers.rb
|
|
44
|
+
- lib/action_authorization/testing/open_policy.rb
|
|
45
|
+
- lib/action_authorization/testing/policy_helpers.rb
|
|
46
|
+
- lib/action_authorization/testing/policy_stub.rb
|
|
47
|
+
- lib/action_authorization/testing/view_test_policy_helpers.rb
|
|
42
48
|
- lib/action_authorization/version.rb
|
|
43
49
|
homepage: https://github.com/wwidea/action_authorization
|
|
44
50
|
licenses:
|
|
@@ -60,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
60
66
|
- !ruby/object:Gem::Version
|
|
61
67
|
version: '0'
|
|
62
68
|
requirements: []
|
|
63
|
-
rubygems_version: 4.0.
|
|
69
|
+
rubygems_version: 4.0.17
|
|
64
70
|
specification_version: 4
|
|
65
71
|
summary: Rails controller object-level action authorization.
|
|
66
72
|
test_files: []
|