pundit 0.2.2 → 0.2.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 +1 -1
- data/CHANGELOG.md +9 -0
- data/README.md +50 -5
- data/lib/pundit.rb +18 -5
- data/lib/pundit/rspec.rb +2 -1
- data/lib/pundit/version.rb +1 -1
- data/pundit.gemspec +1 -1
- data/spec/pundit_spec.rb +50 -42
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cddd41b1da668bb3ff0c192972c1e69a590a66a
|
4
|
+
data.tar.gz: 3d21909c8ce034f4e5253045e10407b1a275bd01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7498ab09942c960b5acc81c7ab9b081e7013007621ed53968d52815abd33e9a67310ac46810b1f3ae754755df01b40d90936b99d247854bcebef16b4b4976e9c
|
7
|
+
data.tar.gz: de605bb11eed39677a26ec2426347205e134e1cad09044870705ca73dadf26a152328750387cce44b721add47a8992a700020a0bf4b63f940406e5dfa99a0d2d
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/elabs/pundit)
|
4
4
|
[](https://codeclimate.com/github/elabs/pundit)
|
5
|
+
[](http://inch-pages.github.io/github/elabs/pundit)
|
5
6
|
|
6
7
|
Pundit provides a set of helpers which guide you in leveraging regular Ruby
|
7
8
|
classes and object oriented design patterns to build a simple, robust and
|
@@ -57,13 +58,16 @@ As you can see, this is just a plain Ruby class. As a convenience, we can inheri
|
|
57
58
|
from Struct or use Struct.new to define the policy class:
|
58
59
|
|
59
60
|
``` ruby
|
60
|
-
PostPolicy
|
61
|
+
class PostPolicy < Struct.new(:user, :post)
|
61
62
|
def update?
|
62
63
|
user.admin? or not post.published?
|
63
64
|
end
|
64
65
|
end
|
65
66
|
```
|
66
67
|
|
68
|
+
You could also use the convenient
|
69
|
+
[attr_extras](https://github.com/barsoom/attr_extras) gem.
|
70
|
+
|
67
71
|
Pundit makes the following assumptions about this class:
|
68
72
|
|
69
73
|
- The class has the same name as some kind of model class, only suffixed
|
@@ -157,8 +161,8 @@ particular user has access to. When using Pundit, you are expected to
|
|
157
161
|
define a class called a policy scope. It can look something like this:
|
158
162
|
|
159
163
|
``` ruby
|
160
|
-
PostPolicy
|
161
|
-
|
164
|
+
class PostPolicy < Struct.new(:user, :post)
|
165
|
+
class Scope < Struct.new(:user, :scope)
|
162
166
|
def resolve
|
163
167
|
if user.admin?
|
164
168
|
scope.all
|
@@ -271,7 +275,10 @@ end
|
|
271
275
|
|
272
276
|
## Rescuing a denied Authorization in Rails
|
273
277
|
|
274
|
-
Pundit raises a `Pundit::NotAuthorizedError` you can
|
278
|
+
Pundit raises a `Pundit::NotAuthorizedError` you can
|
279
|
+
[rescue_from](http://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
|
280
|
+
in your `ApplicationController`. You can customize the `user_not_authorized`
|
281
|
+
method in every controller.
|
275
282
|
|
276
283
|
```ruby
|
277
284
|
class ApplicationController < ActionController::Base
|
@@ -284,11 +291,48 @@ class ApplicationController < ActionController::Base
|
|
284
291
|
|
285
292
|
def user_not_authorized
|
286
293
|
flash[:error] = "You are not authorized to perform this action."
|
287
|
-
redirect_to
|
294
|
+
redirect_to(request.referrer || root_path)
|
288
295
|
end
|
289
296
|
end
|
290
297
|
```
|
291
298
|
|
299
|
+
### Creating custom error messages
|
300
|
+
|
301
|
+
`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
|
302
|
+
record (e.g. an instance of `Post`), and what policy (e.g. an instance of
|
303
|
+
`PostPolicy`) caused the error to be raised.
|
304
|
+
|
305
|
+
One way to use these `query`, `record`, and `policy` properties is to connect
|
306
|
+
them with `I18n` to generate error messages. Here's how you might go about doing
|
307
|
+
that.
|
308
|
+
|
309
|
+
```ruby
|
310
|
+
class ApplicationController < ActionController::Base
|
311
|
+
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
|
312
|
+
|
313
|
+
private
|
314
|
+
|
315
|
+
def user_not_authorized(exception)
|
316
|
+
policy_name = exception.policy.class.to_s.underscore
|
317
|
+
|
318
|
+
flash[:error] = I18n.t "pundit.#{policy_name}.#{exception.query}",
|
319
|
+
default: 'You cannot perform this action.'
|
320
|
+
redirect_to(request.referrer || root_path)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
```yaml
|
326
|
+
en:
|
327
|
+
pundit:
|
328
|
+
post_policy:
|
329
|
+
update?: 'You cannot edit this post!'
|
330
|
+
create?: 'You cannot create posts!'
|
331
|
+
```
|
332
|
+
|
333
|
+
Of course, this is just an example. Pundit is agnostic as to how you implement
|
334
|
+
your error messaging.
|
335
|
+
|
292
336
|
## Manually retrieving policies and scopes
|
293
337
|
|
294
338
|
Sometimes you want to retrieve a policy for a record outside the controller or
|
@@ -422,6 +466,7 @@ based on what is or is not authorized.
|
|
422
466
|
|
423
467
|
# External Resources
|
424
468
|
|
469
|
+
- [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
|
425
470
|
- [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
|
426
471
|
- [Testing Pundit Policies with RSpec](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
|
427
472
|
|
data/lib/pundit.rb
CHANGED
@@ -5,7 +5,10 @@ require "active_support/core_ext/string/inflections"
|
|
5
5
|
require "active_support/core_ext/object/blank"
|
6
6
|
|
7
7
|
module Pundit
|
8
|
-
class NotAuthorizedError < StandardError
|
8
|
+
class NotAuthorizedError < StandardError
|
9
|
+
attr_accessor :query, :record, :policy
|
10
|
+
end
|
11
|
+
class AuthorizationNotPerformedError < StandardError; end
|
9
12
|
class NotDefinedError < StandardError; end
|
10
13
|
|
11
14
|
extend ActiveSupport::Concern
|
@@ -37,6 +40,10 @@ module Pundit
|
|
37
40
|
helper_method :pundit_user
|
38
41
|
end
|
39
42
|
if respond_to?(:hide_action)
|
43
|
+
hide_action :policy_scope
|
44
|
+
hide_action :policy_scope=
|
45
|
+
hide_action :policy
|
46
|
+
hide_action :policy=
|
40
47
|
hide_action :authorize
|
41
48
|
hide_action :verify_authorized
|
42
49
|
hide_action :verify_policy_scoped
|
@@ -45,19 +52,25 @@ module Pundit
|
|
45
52
|
end
|
46
53
|
|
47
54
|
def verify_authorized
|
48
|
-
raise
|
55
|
+
raise AuthorizationNotPerformedError unless @_policy_authorized
|
49
56
|
end
|
50
57
|
|
51
58
|
def verify_policy_scoped
|
52
|
-
raise
|
59
|
+
raise AuthorizationNotPerformedError unless @_policy_scoped
|
53
60
|
end
|
54
61
|
|
55
62
|
def authorize(record, query=nil)
|
56
63
|
query ||= params[:action].to_s + "?"
|
57
64
|
@_policy_authorized = true
|
58
|
-
|
59
|
-
|
65
|
+
|
66
|
+
policy = policy(record)
|
67
|
+
unless policy.public_send(query)
|
68
|
+
error = NotAuthorizedError.new("not allowed to #{query} this #{record}")
|
69
|
+
error.query, error.record, error.policy = query, record, policy
|
70
|
+
|
71
|
+
raise error
|
60
72
|
end
|
73
|
+
|
61
74
|
true
|
62
75
|
end
|
63
76
|
|
data/lib/pundit/rspec.rb
CHANGED
data/lib/pundit/version.rb
CHANGED
data/pundit.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_dependency "activesupport", ">= 3.0.0"
|
22
22
|
gem.add_development_dependency "activerecord", ">= 3.0.0"
|
23
23
|
gem.add_development_dependency "bundler", "~> 1.3"
|
24
|
-
gem.add_development_dependency "rspec", "~>
|
24
|
+
gem.add_development_dependency "rspec", "~>3.0.0.beta1"
|
25
25
|
gem.add_development_dependency "pry"
|
26
26
|
gem.add_development_dependency "rake"
|
27
27
|
gem.add_development_dependency "yard"
|
data/spec/pundit_spec.rb
CHANGED
@@ -66,25 +66,25 @@ describe Pundit do
|
|
66
66
|
|
67
67
|
describe ".policy_scope" do
|
68
68
|
it "returns an instantiated policy scope given a plain model class" do
|
69
|
-
Pundit.policy_scope(user, Post).
|
69
|
+
expect(Pundit.policy_scope(user, Post)).to eq :published
|
70
70
|
end
|
71
71
|
|
72
72
|
it "returns an instantiated policy scope given an active model class" do
|
73
|
-
Pundit.policy_scope(user, Comment).
|
73
|
+
expect(Pundit.policy_scope(user, Comment)).to eq Comment
|
74
74
|
end
|
75
75
|
|
76
76
|
it "returns nil if the given policy scope can't be found" do
|
77
|
-
Pundit.policy_scope(user, Article).
|
77
|
+
expect(Pundit.policy_scope(user, Article)).to be_nil
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
describe ".policy_scope!" do
|
82
82
|
it "returns an instantiated policy scope given a plain model class" do
|
83
|
-
Pundit.policy_scope!(user, Post).
|
83
|
+
expect(Pundit.policy_scope!(user, Post)).to eq :published
|
84
84
|
end
|
85
85
|
|
86
86
|
it "returns an instantiated policy scope given an active model class" do
|
87
|
-
Pundit.policy_scope!(user, Comment).
|
87
|
+
expect(Pundit.policy_scope!(user, Comment)).to eq Comment
|
88
88
|
end
|
89
89
|
|
90
90
|
it "throws an exception if the given policy scope can't be found" do
|
@@ -99,56 +99,56 @@ describe Pundit do
|
|
99
99
|
describe ".policy" do
|
100
100
|
it "returns an instantiated policy given a plain model instance" do
|
101
101
|
policy = Pundit.policy(user, post)
|
102
|
-
policy.user.
|
103
|
-
policy.post.
|
102
|
+
expect(policy.user).to eq user
|
103
|
+
expect(policy.post).to eq post
|
104
104
|
end
|
105
105
|
|
106
106
|
it "returns an instantiated policy given an active model instance" do
|
107
107
|
policy = Pundit.policy(user, comment)
|
108
|
-
policy.user.
|
109
|
-
policy.comment.
|
108
|
+
expect(policy.user).to eq user
|
109
|
+
expect(policy.comment).to eq comment
|
110
110
|
end
|
111
111
|
|
112
112
|
it "returns an instantiated policy given a plain model class" do
|
113
113
|
policy = Pundit.policy(user, Post)
|
114
|
-
policy.user.
|
115
|
-
policy.post.
|
114
|
+
expect(policy.user).to eq user
|
115
|
+
expect(policy.post).to eq Post
|
116
116
|
end
|
117
117
|
|
118
118
|
it "returns an instantiated policy given an active model class" do
|
119
119
|
policy = Pundit.policy(user, Comment)
|
120
|
-
policy.user.
|
121
|
-
policy.comment.
|
120
|
+
expect(policy.user).to eq user
|
121
|
+
expect(policy.comment).to eq Comment
|
122
122
|
end
|
123
123
|
|
124
124
|
it "returns nil if the given policy can't be found" do
|
125
|
-
Pundit.policy(user, article).
|
126
|
-
Pundit.policy(user, Article).
|
125
|
+
expect(Pundit.policy(user, article)).to be_nil
|
126
|
+
expect(Pundit.policy(user, Article)).to be_nil
|
127
127
|
end
|
128
128
|
|
129
129
|
describe "with .policy_class set on the model" do
|
130
130
|
it "returns an instantiated policy given a plain model instance" do
|
131
131
|
policy = Pundit.policy(user, artificial_blog)
|
132
|
-
policy.user.
|
133
|
-
policy.blog.
|
132
|
+
expect(policy.user).to eq user
|
133
|
+
expect(policy.blog).to eq artificial_blog
|
134
134
|
end
|
135
135
|
|
136
136
|
it "returns an instantiated policy given a plain model class" do
|
137
137
|
policy = Pundit.policy(user, ArtificialBlog)
|
138
|
-
policy.user.
|
139
|
-
policy.blog.
|
138
|
+
expect(policy.user).to eq user
|
139
|
+
expect(policy.blog).to eq ArtificialBlog
|
140
140
|
end
|
141
141
|
|
142
142
|
it "returns an instantiated policy given a plain model instance providing an anonymous class" do
|
143
143
|
policy = Pundit.policy(user, article_tag)
|
144
|
-
policy.user.
|
145
|
-
policy.tag.
|
144
|
+
expect(policy.user).to eq user
|
145
|
+
expect(policy.tag).to eq article_tag
|
146
146
|
end
|
147
147
|
|
148
148
|
it "returns an instantiated policy given a plain model class providing an anonymous class" do
|
149
149
|
policy = Pundit.policy(user, ArticleTag)
|
150
|
-
policy.user.
|
151
|
-
policy.tag.
|
150
|
+
expect(policy.user).to eq user
|
151
|
+
expect(policy.tag).to eq ArticleTag
|
152
152
|
end
|
153
153
|
end
|
154
154
|
end
|
@@ -156,26 +156,26 @@ describe Pundit do
|
|
156
156
|
describe ".policy!" do
|
157
157
|
it "returns an instantiated policy given a plain model instance" do
|
158
158
|
policy = Pundit.policy!(user, post)
|
159
|
-
policy.user.
|
160
|
-
policy.post.
|
159
|
+
expect(policy.user).to eq user
|
160
|
+
expect(policy.post).to eq post
|
161
161
|
end
|
162
162
|
|
163
163
|
it "returns an instantiated policy given an active model instance" do
|
164
164
|
policy = Pundit.policy!(user, comment)
|
165
|
-
policy.user.
|
166
|
-
policy.comment.
|
165
|
+
expect(policy.user).to eq user
|
166
|
+
expect(policy.comment).to eq comment
|
167
167
|
end
|
168
168
|
|
169
169
|
it "returns an instantiated policy given a plain model class" do
|
170
170
|
policy = Pundit.policy!(user, Post)
|
171
|
-
policy.user.
|
172
|
-
policy.post.
|
171
|
+
expect(policy.user).to eq user
|
172
|
+
expect(policy.post).to eq Post
|
173
173
|
end
|
174
174
|
|
175
175
|
it "returns an instantiated policy given an active model class" do
|
176
176
|
policy = Pundit.policy!(user, Comment)
|
177
|
-
policy.user.
|
178
|
-
policy.comment.
|
177
|
+
expect(policy.user).to eq user
|
178
|
+
expect(policy.comment).to eq Comment
|
179
179
|
end
|
180
180
|
|
181
181
|
it "throws an exception if the given policy can't be found" do
|
@@ -191,7 +191,7 @@ describe Pundit do
|
|
191
191
|
end
|
192
192
|
|
193
193
|
it "raises an exception when not authorized" do
|
194
|
-
expect { controller.verify_authorized }.to raise_error(Pundit::
|
194
|
+
expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
@@ -202,41 +202,49 @@ describe Pundit do
|
|
202
202
|
end
|
203
203
|
|
204
204
|
it "raises an exception when policy_scope is not used" do
|
205
|
-
expect { controller.verify_policy_scoped }.to raise_error(Pundit::
|
205
|
+
expect { controller.verify_policy_scoped }.to raise_error(Pundit::AuthorizationNotPerformedError)
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
209
209
|
describe "#authorize" do
|
210
210
|
it "infers the policy name and authorized based on it" do
|
211
|
-
controller.authorize(post).
|
211
|
+
expect(controller.authorize(post)).to be_truthy
|
212
212
|
end
|
213
213
|
|
214
214
|
it "can be given a different permission to check" do
|
215
|
-
controller.authorize(post, :show?).
|
215
|
+
expect(controller.authorize(post, :show?)).to be_truthy
|
216
216
|
expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
217
217
|
end
|
218
218
|
|
219
219
|
it "works with anonymous class policies" do
|
220
|
-
controller.authorize(article_tag, :show?).
|
220
|
+
expect(controller.authorize(article_tag, :show?)).to be_truthy
|
221
221
|
expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
222
222
|
end
|
223
223
|
|
224
224
|
it "raises an error when the permission check fails" do
|
225
225
|
expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
|
226
226
|
end
|
227
|
+
|
228
|
+
it "raises an error with a query and action" do
|
229
|
+
expect { controller.authorize(post, :destroy?) }.to raise_error do |error|
|
230
|
+
expect(error.query).to eq :destroy?
|
231
|
+
expect(error.record).to eq post
|
232
|
+
expect(error.policy).to eq controller.policy(post)
|
233
|
+
end
|
234
|
+
end
|
227
235
|
end
|
228
236
|
|
229
237
|
describe "#pundit_user" do
|
230
238
|
it 'returns the same thing as current_user' do
|
231
|
-
controller.pundit_user.
|
239
|
+
expect(controller.pundit_user).to eq controller.current_user
|
232
240
|
end
|
233
241
|
end
|
234
242
|
|
235
243
|
describe ".policy" do
|
236
244
|
it "returns an instantiated policy" do
|
237
245
|
policy = controller.policy(post)
|
238
|
-
policy.user.
|
239
|
-
policy.post.
|
246
|
+
expect(policy.user).to eq user
|
247
|
+
expect(policy.post).to eq post
|
240
248
|
end
|
241
249
|
|
242
250
|
it "throws an exception if the given policy can't be found" do
|
@@ -247,13 +255,13 @@ describe Pundit do
|
|
247
255
|
new_policy = OpenStruct.new
|
248
256
|
controller.policy = new_policy
|
249
257
|
|
250
|
-
controller.policy(post).
|
258
|
+
expect(controller.policy(post)).to eq new_policy
|
251
259
|
end
|
252
260
|
end
|
253
261
|
|
254
262
|
describe ".policy_scope" do
|
255
263
|
it "returns an instantiated policy scope" do
|
256
|
-
controller.policy_scope(Post).
|
264
|
+
expect(controller.policy_scope(Post)).to eq :published
|
257
265
|
end
|
258
266
|
|
259
267
|
it "throws an exception if the given policy can't be found" do
|
@@ -264,7 +272,7 @@ describe Pundit do
|
|
264
272
|
new_scope = OpenStruct.new
|
265
273
|
controller.policy_scope = new_scope
|
266
274
|
|
267
|
-
controller.policy_scope(post).
|
275
|
+
expect(controller.policy_scope(post)).to eq new_scope
|
268
276
|
end
|
269
277
|
end
|
270
278
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pundit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Nicklas
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - ~>
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
62
|
+
version: 3.0.0.beta1
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 3.0.0.beta1
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: pry
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +119,7 @@ extra_rdoc_files: []
|
|
119
119
|
files:
|
120
120
|
- .gitignore
|
121
121
|
- .travis.yml
|
122
|
+
- CHANGELOG.md
|
122
123
|
- Gemfile
|
123
124
|
- LICENSE.txt
|
124
125
|
- README.md
|