pundit 0.2.2 → 0.2.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 +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
|
[![Build Status](https://secure.travis-ci.org/elabs/pundit.png?branch=master)](https://travis-ci.org/elabs/pundit)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/elabs/pundit.png)](https://codeclimate.com/github/elabs/pundit)
|
5
|
+
[![Inline docs](http://inch-pages.github.io/github/elabs/pundit.png)](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
|