pundit 1.0.1 → 1.1.0
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/.rubocop.yml +95 -0
- data/.travis.yml +2 -2
- data/.yardopts +1 -0
- data/CHANGELOG.md +9 -1
- data/Gemfile +1 -2
- data/README.md +38 -15
- data/Rakefile +10 -8
- data/lib/generators/rspec/templates/policy_spec.rb +1 -1
- data/lib/pundit.rb +132 -8
- data/lib/pundit/policy_finder.rb +58 -11
- data/lib/pundit/rspec.rb +19 -13
- data/lib/pundit/version.rb +1 -1
- data/pundit.gemspec +6 -5
- data/spec/pundit_spec.rb +144 -22
- data/spec/spec_helper.rb +89 -23
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2c188098c86ad5a0804044f80219014564501ef
|
4
|
+
data.tar.gz: 2ab7f79275d9ae66e5d04cf980c11e2c2983a4ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55fbbf71ad514c0cfe4f8933dea59915314f749efa53ab5579f2da9dfcf2b4786343cefa53d3a35e26f4a346776c1c513884595a39561d280b259e6b6fb9b31a
|
7
|
+
data.tar.gz: bbcf9417801b22deac78afe2d5ea8a268193daf0e011f861006c25b1d0124d1f462aa37d4d38e623b9d438cb29ceb52b250575118e053862b74561795bbdd7a4
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- "gemfiles/**/*"
|
4
|
+
- "vendor/**/*"
|
5
|
+
- "lib/generators/**/*"
|
6
|
+
|
7
|
+
Metrics/MethodLength:
|
8
|
+
Max: 40
|
9
|
+
|
10
|
+
Metrics/ModuleLength:
|
11
|
+
Max: 200
|
12
|
+
|
13
|
+
Metrics/LineLength:
|
14
|
+
Max: 120
|
15
|
+
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/CyclomaticComplexity:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Metrics/PerceivedComplexity:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/StructInheritance:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/AlignParameters:
|
29
|
+
EnforcedStyle: with_fixed_indentation
|
30
|
+
|
31
|
+
Style/StringLiterals:
|
32
|
+
EnforcedStyle: double_quotes
|
33
|
+
|
34
|
+
Style/StringLiteralsInInterpolation:
|
35
|
+
EnforcedStyle: double_quotes
|
36
|
+
|
37
|
+
Style/ClosingParenthesisIndentation:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/OneLineConditional:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/AndOr:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/Not:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Documentation:
|
50
|
+
Enabled: false # TODO: Enable again once we have more docs
|
51
|
+
|
52
|
+
Style/CaseIndentation:
|
53
|
+
IndentWhenRelativeTo: case
|
54
|
+
SupportedStyles:
|
55
|
+
- case
|
56
|
+
- end
|
57
|
+
IndentOneStep: true
|
58
|
+
|
59
|
+
Style/PercentLiteralDelimiters:
|
60
|
+
PreferredDelimiters:
|
61
|
+
'%w': "[]"
|
62
|
+
'%W': "[]"
|
63
|
+
|
64
|
+
Style/AccessModifierIndentation:
|
65
|
+
EnforcedStyle: outdent
|
66
|
+
|
67
|
+
Style/SignalException:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/IndentationWidth:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Style/TrivialAccessors:
|
74
|
+
ExactNameMatch: true
|
75
|
+
|
76
|
+
Lint/EndAlignment:
|
77
|
+
AlignWith: variable
|
78
|
+
|
79
|
+
Lint/DefEndAlignment:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
Lint/HandleExceptions:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
Style/SpecialGlobalVars:
|
86
|
+
Enabled: false
|
87
|
+
|
88
|
+
Style/TrivialAccessors:
|
89
|
+
Enabled: false
|
90
|
+
|
91
|
+
Style/IndentHash:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
Style/DoubleNegation:
|
95
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--api public --hide-void-return --markup markdown
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
+
## 1.1.0 (2016-01-14)
|
4
|
+
|
5
|
+
- Can retrieve policies via an array of symbols/objects.
|
6
|
+
- Add autodetection of param key to `permitted_attributes` helper.
|
7
|
+
- Hide some methods which should not be actions.
|
8
|
+
- Permitted attributes should be expanded.
|
9
|
+
- Generator uses `RSpec.describe` according to modern best practices.
|
10
|
+
|
3
11
|
## 1.0.1 (2015-05-27)
|
4
12
|
|
5
13
|
- Fixed a regression where NotAuthorizedError could not be ininitialized with a string.
|
@@ -17,7 +25,7 @@
|
|
17
25
|
- Add `skip_authorization` and `skip_policy_scope` helpers.
|
18
26
|
- Better errors when checking multiple permissions in RSpec tests.
|
19
27
|
- Better errors in case `nil` is passed to `policy` or `policy_scope`.
|
20
|
-
- Use `
|
28
|
+
- Use `inspect` when printing object for better errors.
|
21
29
|
- Dropped official support for Ruby 1.9.3
|
22
30
|
|
23
31
|
## 0.3.0 (2014-08-22)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
-
[](https://travis-ci.org/elabs/pundit)
|
4
|
+
[](https://codeclimate.com/github/elabs/pundit)
|
5
|
+
[](http://inch-ci.org/github/elabs/pundit)
|
6
|
+
[](http://badge.fury.io/rb/pundit)
|
6
7
|
|
7
8
|
Pundit provides a set of helpers which guide you in leveraging regular Ruby
|
8
9
|
classes and object oriented design patterns to build a simple, robust and
|
@@ -130,6 +131,26 @@ def publish
|
|
130
131
|
end
|
131
132
|
```
|
132
133
|
|
134
|
+
If you don't have an instance for the first argument to `authorize`, then you can pass
|
135
|
+
the class. For example:
|
136
|
+
|
137
|
+
Policy:
|
138
|
+
```ruby
|
139
|
+
class PostPolicy < ApplicationPolicy
|
140
|
+
def admin_list?
|
141
|
+
user.admin?
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Controller:
|
147
|
+
```ruby
|
148
|
+
def admin_list
|
149
|
+
authorize Post # we don't have a particular post to authorize
|
150
|
+
# Rest of controller action
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
133
154
|
You can easily get a hold of an instance of the policy through the `policy`
|
134
155
|
method in both the view and controller. This is especially useful for
|
135
156
|
conditionally showing links or buttons in the view:
|
@@ -172,7 +193,7 @@ forgotten to authorize the action. For example:
|
|
172
193
|
|
173
194
|
``` ruby
|
174
195
|
class ApplicationController < ActionController::Base
|
175
|
-
after_action :verify_authorized
|
196
|
+
after_action :verify_authorized
|
176
197
|
end
|
177
198
|
```
|
178
199
|
|
@@ -184,7 +205,8 @@ authorize individual instances.
|
|
184
205
|
|
185
206
|
``` ruby
|
186
207
|
class ApplicationController < ActionController::Base
|
187
|
-
after_action :
|
208
|
+
after_action :verify_authorized, except: :index
|
209
|
+
after_action :verify_policy_scoped, only: :index
|
188
210
|
end
|
189
211
|
```
|
190
212
|
|
@@ -222,7 +244,7 @@ class PostPolicy < ApplicationPolicy
|
|
222
244
|
attr_reader :user, :scope
|
223
245
|
|
224
246
|
def initialize(user, scope)
|
225
|
-
@user
|
247
|
+
@user = user
|
226
248
|
@scope = scope
|
227
249
|
end
|
228
250
|
|
@@ -230,7 +252,7 @@ class PostPolicy < ApplicationPolicy
|
|
230
252
|
if user.admin?
|
231
253
|
scope.all
|
232
254
|
else
|
233
|
-
scope.where(:
|
255
|
+
scope.where(published: true)
|
234
256
|
end
|
235
257
|
end
|
236
258
|
end
|
@@ -263,7 +285,7 @@ class PostPolicy < ApplicationPolicy
|
|
263
285
|
if user.admin?
|
264
286
|
scope.all
|
265
287
|
else
|
266
|
-
scope.where(:
|
288
|
+
scope.where(published: true)
|
267
289
|
end
|
268
290
|
end
|
269
291
|
end
|
@@ -351,7 +373,7 @@ got through. This way you can fail more gracefully.
|
|
351
373
|
class ApplicationPolicy
|
352
374
|
def initialize(user, record)
|
353
375
|
raise Pundit::NotAuthorizedError, "must be logged in" unless user
|
354
|
-
@user
|
376
|
+
@user = user
|
355
377
|
@record = record
|
356
378
|
end
|
357
379
|
end
|
@@ -470,7 +492,7 @@ class UserContext
|
|
470
492
|
|
471
493
|
def initialize(user, ip)
|
472
494
|
@user = user
|
473
|
-
@ip
|
495
|
+
@ip = ip
|
474
496
|
end
|
475
497
|
end
|
476
498
|
|
@@ -559,24 +581,24 @@ Then put your policy specs in `spec/policies`, and make them look somewhat like
|
|
559
581
|
describe PostPolicy do
|
560
582
|
subject { described_class }
|
561
583
|
|
562
|
-
permissions :update? do
|
584
|
+
permissions :update?, :edit? do
|
563
585
|
it "denies access if post is published" do
|
564
|
-
expect(subject).not_to permit(User.new(:
|
586
|
+
expect(subject).not_to permit(User.new(admin: false), Post.new(published: true))
|
565
587
|
end
|
566
588
|
|
567
589
|
it "grants access if post is published and user is an admin" do
|
568
|
-
expect(subject).to permit(User.new(:
|
590
|
+
expect(subject).to permit(User.new(admin: true), Post.new(published: true))
|
569
591
|
end
|
570
592
|
|
571
593
|
it "grants access if post is unpublished" do
|
572
|
-
expect(subject).to permit(User.new(:
|
594
|
+
expect(subject).to permit(User.new(admin: false), Post.new(published: false))
|
573
595
|
end
|
574
596
|
end
|
575
597
|
end
|
576
598
|
```
|
577
599
|
|
578
600
|
An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
|
579
|
-
[excellent post](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/).
|
601
|
+
[excellent post](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) and implemented in the third party [pundit-matchers](https://github.com/chrisalley/pundit-matchers) gem.
|
580
602
|
|
581
603
|
# External Resources
|
582
604
|
|
@@ -584,6 +606,7 @@ An alternative approach to Pundit policy specs is scoping them to a user context
|
|
584
606
|
- [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
|
585
607
|
- [Testing Pundit Policies with RSpec](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
|
586
608
|
- [Using Pundit outside of a Rails controller](https://github.com/elabs/pundit/pull/136)
|
609
|
+
- [Straightforward Rails Authorization with Pundit](http://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
|
587
610
|
|
588
611
|
# License
|
589
612
|
|
data/Rakefile
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require "yard"
|
5
|
+
require "rubocop/rake_task"
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new
|
5
8
|
|
6
9
|
desc "Run all examples"
|
7
10
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
-
#t.rspec_path = 'bin/rspec'
|
9
11
|
t.rspec_opts = %w[--color]
|
10
12
|
end
|
11
13
|
|
12
14
|
YARD::Rake::YardocTask.new do |t|
|
13
|
-
t.files
|
14
|
-
#t.options = ['--any', '--extra', '--opts'] # optional
|
15
|
+
t.files = ["lib/**/*.rb"]
|
15
16
|
end
|
16
17
|
|
17
|
-
task :
|
18
|
+
task default: :spec
|
19
|
+
task default: :rubocop unless RUBY_ENGINE == "rbx"
|
data/lib/pundit.rb
CHANGED
@@ -6,10 +6,17 @@ require "active_support/core_ext/object/blank"
|
|
6
6
|
require "active_support/core_ext/module/introspection"
|
7
7
|
require "active_support/dependencies/autoload"
|
8
8
|
|
9
|
+
# @api public
|
9
10
|
module Pundit
|
10
11
|
SUFFIX = "Policy"
|
11
12
|
|
13
|
+
# @api private
|
14
|
+
module Generators; end
|
15
|
+
|
16
|
+
# @api private
|
12
17
|
class Error < StandardError; end
|
18
|
+
|
19
|
+
# Error that will be raiser when authorization has failed
|
13
20
|
class NotAuthorizedError < Error
|
14
21
|
attr_reader :query, :record, :policy
|
15
22
|
|
@@ -27,42 +34,86 @@ module Pundit
|
|
27
34
|
super(message)
|
28
35
|
end
|
29
36
|
end
|
37
|
+
|
38
|
+
# Error that will be raised if a controller action has not called the
|
39
|
+
# `authorize` or `skip_authorization` methods.
|
30
40
|
class AuthorizationNotPerformedError < Error; end
|
41
|
+
|
42
|
+
# Error that will be raised if a controller action has not called the
|
43
|
+
# `policy_scope` or `skip_policy_scope` methods.
|
31
44
|
class PolicyScopingNotPerformedError < AuthorizationNotPerformedError; end
|
45
|
+
|
46
|
+
# Error that will be raised if a policy or policy scope is not defined.
|
32
47
|
class NotDefinedError < Error; end
|
33
48
|
|
34
49
|
extend ActiveSupport::Concern
|
35
50
|
|
36
51
|
class << self
|
52
|
+
# Retrieves the policy for the given record, initializing it with the
|
53
|
+
# record and user and finally throwing an error if the user is not
|
54
|
+
# authorized to perform the given action.
|
55
|
+
#
|
56
|
+
# @param user [Object] the user that initiated the action
|
57
|
+
# @param record [Object] the object we're checking permissions of
|
58
|
+
# @param record [Symbol] the query method to check on the policy (e.g. `:show?`)
|
59
|
+
# @raise [NotAuthorizedError] if the given query method returned false
|
60
|
+
# @return [true] Always returns true
|
37
61
|
def authorize(user, record, query)
|
38
62
|
policy = policy!(user, record)
|
39
63
|
|
40
64
|
unless policy.public_send(query)
|
41
|
-
raise NotAuthorizedError
|
65
|
+
raise NotAuthorizedError, query: query, record: record, policy: policy
|
42
66
|
end
|
43
67
|
|
44
68
|
true
|
45
69
|
end
|
46
70
|
|
71
|
+
# Retrieves the policy scope for the given record.
|
72
|
+
#
|
73
|
+
# @see https://github.com/elabs/pundit#scopes
|
74
|
+
# @param user [Object] the user that initiated the action
|
75
|
+
# @param record [Object] the object we're retrieving the policy scope for
|
76
|
+
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
|
47
77
|
def policy_scope(user, scope)
|
48
78
|
policy_scope = PolicyFinder.new(scope).scope
|
49
79
|
policy_scope.new(user, scope).resolve if policy_scope
|
50
80
|
end
|
51
81
|
|
82
|
+
# Retrieves the policy scope for the given record.
|
83
|
+
#
|
84
|
+
# @see https://github.com/elabs/pundit#scopes
|
85
|
+
# @param user [Object] the user that initiated the action
|
86
|
+
# @param record [Object] the object we're retrieving the policy scope for
|
87
|
+
# @raise [NotDefinedError] if the policy scope cannot be found
|
88
|
+
# @return [Scope{#resolve}] instance of scope class which can resolve to a scope
|
52
89
|
def policy_scope!(user, scope)
|
53
90
|
PolicyFinder.new(scope).scope!.new(user, scope).resolve
|
54
91
|
end
|
55
92
|
|
93
|
+
# Retrieves the policy for the given record.
|
94
|
+
#
|
95
|
+
# @see https://github.com/elabs/pundit#policies
|
96
|
+
# @param user [Object] the user that initiated the action
|
97
|
+
# @param record [Object] the object we're retrieving the policy for
|
98
|
+
# @return [Object, nil] instance of policy class with query methods
|
56
99
|
def policy(user, record)
|
57
100
|
policy = PolicyFinder.new(record).policy
|
58
101
|
policy.new(user, record) if policy
|
59
102
|
end
|
60
103
|
|
104
|
+
# Retrieves the policy for the given record.
|
105
|
+
#
|
106
|
+
# @see https://github.com/elabs/pundit#policies
|
107
|
+
# @param user [Object] the user that initiated the action
|
108
|
+
# @param record [Object] the object we're retrieving the policy for
|
109
|
+
# @raise [NotDefinedError] if the policy cannot be found
|
110
|
+
# @return [Object] instance of policy class with query methods
|
61
111
|
def policy!(user, record)
|
62
112
|
PolicyFinder.new(record).policy!.new(user, record)
|
63
113
|
end
|
64
114
|
end
|
65
115
|
|
116
|
+
# @api private
|
66
117
|
module Helper
|
67
118
|
def policy_scope(scope)
|
68
119
|
pundit_policy_scope(scope)
|
@@ -88,68 +139,141 @@ module Pundit
|
|
88
139
|
hide_action :pundit_user
|
89
140
|
hide_action :skip_authorization
|
90
141
|
hide_action :skip_policy_scope
|
142
|
+
hide_action :pundit_policy_authorized?
|
143
|
+
hide_action :pundit_policy_scoped?
|
91
144
|
end
|
92
145
|
end
|
93
146
|
|
147
|
+
# @return [Boolean] whether authorization has been performed, i.e. whether
|
148
|
+
# one {#authorize} or {#skip_authorization} has been called
|
94
149
|
def pundit_policy_authorized?
|
95
150
|
!!@_pundit_policy_authorized
|
96
151
|
end
|
97
152
|
|
153
|
+
# @return [Boolean] whether policy scoping has been performed, i.e. whether
|
154
|
+
# one {#policy_scope} or {#skip_policy_scope} has been called
|
98
155
|
def pundit_policy_scoped?
|
99
156
|
!!@_pundit_policy_scoped
|
100
157
|
end
|
101
158
|
|
159
|
+
# Raises an error if authorization has not been performed, usually used as an
|
160
|
+
# `after_action` filter to prevent programmer error in forgetting to call
|
161
|
+
# {#authorize} or {#skip_authorization}.
|
162
|
+
#
|
163
|
+
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
|
164
|
+
# @raise [AuthorizationNotPerformedError] if authorization has not been performed
|
165
|
+
# @return [void]
|
102
166
|
def verify_authorized
|
103
|
-
raise AuthorizationNotPerformedError unless pundit_policy_authorized?
|
167
|
+
raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
|
104
168
|
end
|
105
169
|
|
170
|
+
# Raises an error if policy scoping has not been performed, usually used as an
|
171
|
+
# `after_action` filter to prevent programmer error in forgetting to call
|
172
|
+
# {#policy_scope} or {#skip_policy_scope} in index actions.
|
173
|
+
#
|
174
|
+
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
|
175
|
+
# @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
|
176
|
+
# @return [void]
|
106
177
|
def verify_policy_scoped
|
107
|
-
raise PolicyScopingNotPerformedError unless pundit_policy_scoped?
|
178
|
+
raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
|
108
179
|
end
|
109
180
|
|
110
|
-
|
181
|
+
# Retrieves the policy for the given record, initializing it with the record
|
182
|
+
# and current user and finally throwing an error if the user is not
|
183
|
+
# authorized to perform the given action.
|
184
|
+
#
|
185
|
+
# @param record [Object] the object we're checking permissions of
|
186
|
+
# @param record [Symbol, nil] the query method to check on the policy (e.g. `:show?`)
|
187
|
+
# @raise [NotAuthorizedError] if the given query method returned false
|
188
|
+
# @return [true] Always returns true
|
189
|
+
def authorize(record, query = nil)
|
111
190
|
query ||= params[:action].to_s + "?"
|
112
191
|
|
113
192
|
@_pundit_policy_authorized = true
|
114
193
|
|
115
194
|
policy = policy(record)
|
195
|
+
|
116
196
|
unless policy.public_send(query)
|
117
|
-
raise NotAuthorizedError
|
197
|
+
raise NotAuthorizedError, query: query, record: record, policy: policy
|
118
198
|
end
|
119
199
|
|
120
200
|
true
|
121
201
|
end
|
122
202
|
|
203
|
+
# Allow this action not to perform authorization.
|
204
|
+
#
|
205
|
+
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
|
206
|
+
# @return [void]
|
123
207
|
def skip_authorization
|
124
208
|
@_pundit_policy_authorized = true
|
125
209
|
end
|
126
210
|
|
211
|
+
# Allow this action not to perform policy scoping.
|
212
|
+
#
|
213
|
+
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
|
214
|
+
# @return [void]
|
127
215
|
def skip_policy_scope
|
128
216
|
@_pundit_policy_scoped = true
|
129
217
|
end
|
130
218
|
|
219
|
+
# Retrieves the policy scope for the given record.
|
220
|
+
#
|
221
|
+
# @see https://github.com/elabs/pundit#scopes
|
222
|
+
# @param record [Object] the object we're retrieving the policy scope for
|
223
|
+
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
|
131
224
|
def policy_scope(scope)
|
132
225
|
@_pundit_policy_scoped = true
|
133
226
|
pundit_policy_scope(scope)
|
134
227
|
end
|
135
228
|
|
229
|
+
# Retrieves the policy for the given record.
|
230
|
+
#
|
231
|
+
# @see https://github.com/elabs/pundit#policies
|
232
|
+
# @param record [Object] the object we're retrieving the policy for
|
233
|
+
# @return [Object, nil] instance of policy class with query methods
|
136
234
|
def policy(record)
|
137
235
|
policies[record] ||= Pundit.policy!(pundit_user, record)
|
138
236
|
end
|
139
237
|
|
140
|
-
|
141
|
-
|
142
|
-
|
238
|
+
# Retrieves a set of permitted attributes from the policy by instantiating
|
239
|
+
# the policy class for the given record and calling `permitted_attributes` on
|
240
|
+
# it, or `permitted_attributes_for_{action}` if it is defined. It then infers
|
241
|
+
# what key the record should have in the params hash and retrieves the
|
242
|
+
# permitted attributes from the params hash under that key.
|
243
|
+
#
|
244
|
+
# @see https://github.com/elabs/pundit#strong-parameters
|
245
|
+
# @param record [Object] the object we're retrieving permitted attributes for
|
246
|
+
# @return [Hash{String => Object}] the permitted attributes
|
247
|
+
def permitted_attributes(record, action = params[:action])
|
248
|
+
param_key = PolicyFinder.new(record).param_key
|
249
|
+
policy = policy(record)
|
250
|
+
method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
|
251
|
+
"permitted_attributes_for_#{action}"
|
252
|
+
else
|
253
|
+
"permitted_attributes"
|
254
|
+
end
|
255
|
+
params.require(param_key).permit(policy.public_send(method_name))
|
143
256
|
end
|
144
257
|
|
258
|
+
# Cache of policies. You should not rely on this method.
|
259
|
+
#
|
260
|
+
# @api private
|
145
261
|
def policies
|
146
262
|
@_pundit_policies ||= {}
|
147
263
|
end
|
148
264
|
|
265
|
+
# Cache of policy scope. You should not rely on this method.
|
266
|
+
#
|
267
|
+
# @api private
|
149
268
|
def policy_scopes
|
150
269
|
@_pundit_policy_scopes ||= {}
|
151
270
|
end
|
152
271
|
|
272
|
+
# Hook method which allows customizing which user is passed to policies and
|
273
|
+
# scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
|
274
|
+
#
|
275
|
+
# @see https://github.com/elabs/pundit#customize-pundit-user
|
276
|
+
# @return [Object] the user object to be used with pundit
|
153
277
|
def pundit_user
|
154
278
|
current_user
|
155
279
|
end
|
data/lib/pundit/policy_finder.rb
CHANGED
@@ -1,17 +1,40 @@
|
|
1
1
|
module Pundit
|
2
|
+
# Finds policy and scope classes for given object.
|
3
|
+
# @api public
|
4
|
+
# @example
|
5
|
+
# user = User.find(params[:id])
|
6
|
+
# finder = PolicyFinder.new(user)
|
7
|
+
# finder.policy #=> UserPolicy
|
8
|
+
# finder.scope #=> UserPolicy::Scope
|
9
|
+
#
|
2
10
|
class PolicyFinder
|
3
11
|
attr_reader :object
|
4
12
|
|
13
|
+
# @param object [any] the object to find policy and scope classes for
|
14
|
+
#
|
5
15
|
def initialize(object)
|
6
16
|
@object = object
|
7
17
|
end
|
8
18
|
|
19
|
+
# @return [nil, Scope{#resolve}] scope class which can resolve to a scope
|
20
|
+
# @see https://github.com/elabs/pundit#scopes
|
21
|
+
# @example
|
22
|
+
# scope = finder.scope #=> UserPolicy::Scope
|
23
|
+
# scope.resolve #=> <#ActiveRecord::Relation ...>
|
24
|
+
#
|
9
25
|
def scope
|
10
26
|
policy::Scope if policy
|
11
27
|
rescue NameError
|
12
28
|
nil
|
13
29
|
end
|
14
30
|
|
31
|
+
# @return [nil, Class] policy class with query methods
|
32
|
+
# @see https://github.com/elabs/pundit#policies
|
33
|
+
# @example
|
34
|
+
# policy = finder.policy #=> UserPolicy
|
35
|
+
# policy.show? #=> true
|
36
|
+
# policy.update? #=> false
|
37
|
+
#
|
15
38
|
def policy
|
16
39
|
klass = find
|
17
40
|
klass = klass.constantize if klass.is_a?(String)
|
@@ -20,16 +43,34 @@ module Pundit
|
|
20
43
|
nil
|
21
44
|
end
|
22
45
|
|
46
|
+
# @return [Scope{#resolve}] scope class which can resolve to a scope
|
47
|
+
# @raise [NotDefinedError] if scope could not be determined
|
48
|
+
#
|
23
49
|
def scope!
|
24
50
|
raise NotDefinedError, "unable to find policy scope of nil" if object.nil?
|
25
51
|
scope or raise NotDefinedError, "unable to find scope `#{find}::Scope` for `#{object.inspect}`"
|
26
52
|
end
|
27
53
|
|
54
|
+
# @return [Class] policy class with query methods
|
55
|
+
# @raise [NotDefinedError] if policy could not be determined
|
56
|
+
#
|
28
57
|
def policy!
|
29
58
|
raise NotDefinedError, "unable to find policy of nil" if object.nil?
|
30
59
|
policy or raise NotDefinedError, "unable to find policy `#{find}` for `#{object.inspect}`"
|
31
60
|
end
|
32
61
|
|
62
|
+
# @return [String] the name of the key this object would have in a params hash
|
63
|
+
#
|
64
|
+
def param_key
|
65
|
+
if object.respond_to?(:model_name)
|
66
|
+
object.model_name.param_key.to_s
|
67
|
+
elsif object.is_a?(Class)
|
68
|
+
object.to_s.demodulize.underscore
|
69
|
+
else
|
70
|
+
object.class.to_s.demodulize.underscore
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
33
74
|
private
|
34
75
|
|
35
76
|
def find
|
@@ -40,21 +81,27 @@ module Pundit
|
|
40
81
|
elsif object.class.respond_to?(:policy_class)
|
41
82
|
object.class.policy_class
|
42
83
|
else
|
43
|
-
klass = if object.
|
44
|
-
object.
|
45
|
-
elsif object.class.respond_to?(:model_name)
|
46
|
-
object.class.model_name
|
47
|
-
elsif object.is_a?(Class)
|
48
|
-
object
|
49
|
-
elsif object.is_a?(Symbol)
|
50
|
-
object.to_s.camelize
|
51
|
-
elsif object.is_a?(Array)
|
52
|
-
object.join('/').camelize
|
84
|
+
klass = if object.is_a?(Array)
|
85
|
+
object.map { |x| find_class_name(x) }.join("::")
|
53
86
|
else
|
54
|
-
object
|
87
|
+
find_class_name(object)
|
55
88
|
end
|
56
89
|
"#{klass}#{SUFFIX}"
|
57
90
|
end
|
58
91
|
end
|
92
|
+
|
93
|
+
def find_class_name(subject)
|
94
|
+
if subject.respond_to?(:model_name)
|
95
|
+
subject.model_name
|
96
|
+
elsif subject.class.respond_to?(:model_name)
|
97
|
+
subject.class.model_name
|
98
|
+
elsif subject.is_a?(Class)
|
99
|
+
subject
|
100
|
+
elsif subject.is_a?(Symbol)
|
101
|
+
subject.to_s.camelize
|
102
|
+
else
|
103
|
+
subject.class
|
104
|
+
end
|
105
|
+
end
|
59
106
|
end
|
60
107
|
end
|
data/lib/pundit/rspec.rb
CHANGED
@@ -7,23 +7,29 @@ module Pundit
|
|
7
7
|
|
8
8
|
matcher :permit do |user, record|
|
9
9
|
match_proc = lambda do |policy|
|
10
|
-
@violating_permissions = permissions.find_all
|
10
|
+
@violating_permissions = permissions.find_all do |permission|
|
11
|
+
not policy.new(user, record).public_send(permission)
|
12
|
+
end
|
11
13
|
@violating_permissions.empty?
|
12
14
|
end
|
13
15
|
|
14
16
|
match_when_negated_proc = lambda do |policy|
|
15
|
-
@violating_permissions = permissions.find_all
|
17
|
+
@violating_permissions = permissions.find_all do |permission|
|
18
|
+
policy.new(user, record).public_send(permission)
|
19
|
+
end
|
16
20
|
@violating_permissions.empty?
|
17
21
|
end
|
18
22
|
|
19
23
|
failure_message_proc = lambda do |policy|
|
20
24
|
was_were = @violating_permissions.count > 1 ? "were" : "was"
|
21
|
-
"Expected #{policy} to grant #{permissions.to_sentence} on
|
25
|
+
"Expected #{policy} to grant #{permissions.to_sentence} on \
|
26
|
+
#{record} but #{@violating_permissions.to_sentence} #{was_were} not granted"
|
22
27
|
end
|
23
28
|
|
24
29
|
failure_message_when_negated_proc = lambda do |policy|
|
25
30
|
was_were = @violating_permissions.count > 1 ? "were" : "was"
|
26
|
-
"Expected #{policy} not to grant #{permissions.to_sentence} on
|
31
|
+
"Expected #{policy} not to grant #{permissions.to_sentence} on \
|
32
|
+
#{record} but #{@violating_permissions.to_sentence} #{was_were} granted"
|
27
33
|
end
|
28
34
|
|
29
35
|
if respond_to?(:match_when_negated)
|
@@ -47,7 +53,7 @@ module Pundit
|
|
47
53
|
|
48
54
|
module DSL
|
49
55
|
def permissions(*list, &block)
|
50
|
-
describe(list.to_sentence, :
|
56
|
+
describe(list.to_sentence, permissions: list, caller: caller) { instance_eval(&block) }
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
@@ -65,14 +71,14 @@ end
|
|
65
71
|
|
66
72
|
RSpec.configure do |config|
|
67
73
|
if RSpec::Core::Version::STRING.split(".").first.to_i >= 3
|
68
|
-
config.include(Pundit::RSpec::PolicyExampleGroup,
|
69
|
-
:
|
70
|
-
:
|
71
|
-
|
74
|
+
config.include(Pundit::RSpec::PolicyExampleGroup,
|
75
|
+
type: :policy,
|
76
|
+
file_path: %r{spec/policies}
|
77
|
+
)
|
72
78
|
else
|
73
|
-
config.include(Pundit::RSpec::PolicyExampleGroup,
|
74
|
-
:
|
75
|
-
:
|
76
|
-
|
79
|
+
config.include(Pundit::RSpec::PolicyExampleGroup,
|
80
|
+
type: :policy,
|
81
|
+
example_group: { file_path: %r{spec/policies} }
|
82
|
+
)
|
77
83
|
end
|
78
84
|
end
|
data/lib/pundit/version.rb
CHANGED
data/pundit.gemspec
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "pundit/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "pundit"
|
8
8
|
gem.version = Pundit::VERSION
|
9
9
|
gem.authors = ["Jonas Nicklas", "Elabs AB"]
|
10
10
|
gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se"]
|
11
|
-
gem.description =
|
12
|
-
gem.summary =
|
11
|
+
gem.description = "Object oriented authorization for Rails applications"
|
12
|
+
gem.summary = "OO authorization for Rails"
|
13
13
|
gem.homepage = "https://github.com/elabs/pundit"
|
14
14
|
gem.license = "MIT"
|
15
15
|
|
16
16
|
gem.files = `git ls-files`.split($/)
|
17
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
@@ -26,4 +26,5 @@ Gem::Specification.new do |gem|
|
|
26
26
|
gem.add_development_dependency "pry"
|
27
27
|
gem.add_development_dependency "rake"
|
28
28
|
gem.add_development_dependency "yard"
|
29
|
+
gem.add_development_dependency "rubocop"
|
29
30
|
end
|
data/spec/pundit_spec.rb
CHANGED
@@ -3,13 +3,18 @@ require "spec_helper"
|
|
3
3
|
describe Pundit do
|
4
4
|
let(:user) { double }
|
5
5
|
let(:post) { Post.new(user) }
|
6
|
+
let(:customer_post) { Customer::Post.new(user) }
|
7
|
+
let(:post_four_five_six) { PostFourFiveSix.new(user) }
|
6
8
|
let(:comment) { Comment.new }
|
9
|
+
let(:comment_four_five_six) { CommentFourFiveSix.new }
|
7
10
|
let(:article) { Article.new }
|
8
|
-
let(:controller) { Controller.new(user,
|
11
|
+
let(:controller) { Controller.new(user, action: "update") }
|
9
12
|
let(:artificial_blog) { ArtificialBlog.new }
|
10
13
|
let(:article_tag) { ArticleTag.new }
|
11
14
|
let(:comments_relation) { CommentsRelation.new }
|
12
15
|
let(:empty_comments_relation) { CommentsRelation.new(true) }
|
16
|
+
let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) }
|
17
|
+
let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new }
|
13
18
|
|
14
19
|
describe ".authorize" do
|
15
20
|
it "infers the policy and authorizes based on it" do
|
@@ -22,7 +27,10 @@ describe Pundit do
|
|
22
27
|
end
|
23
28
|
|
24
29
|
it "raises an error with a query and action" do
|
25
|
-
|
30
|
+
# rubocop:disable Style/MultilineBlockChain
|
31
|
+
expect do
|
32
|
+
Pundit.authorize(user, post, :destroy?)
|
33
|
+
end.to raise_error(Pundit::NotAuthorizedError, "not allowed to destroy? this #<Post>") do |error|
|
26
34
|
expect(error.query).to eq :destroy?
|
27
35
|
expect(error.record).to eq post
|
28
36
|
expect(error.policy).to eq Pundit.policy(user, post)
|
@@ -74,7 +82,9 @@ describe Pundit do
|
|
74
82
|
end
|
75
83
|
|
76
84
|
it "throws an exception if the given policy scope is nil" do
|
77
|
-
expect
|
85
|
+
expect do
|
86
|
+
Pundit.policy_scope!(user, nil)
|
87
|
+
end.to raise_error(Pundit::NotDefinedError, "unable to find policy scope of nil")
|
78
88
|
end
|
79
89
|
end
|
80
90
|
|
@@ -103,6 +113,93 @@ describe Pundit do
|
|
103
113
|
expect(policy.comment).to eq Comment
|
104
114
|
end
|
105
115
|
|
116
|
+
it "returns an instantiated policy given a symbol" do
|
117
|
+
policy = Pundit.policy(user, :criteria)
|
118
|
+
expect(policy.class).to eq CriteriaPolicy
|
119
|
+
expect(policy.user).to eq user
|
120
|
+
expect(policy.criteria).to eq :criteria
|
121
|
+
end
|
122
|
+
|
123
|
+
it "returns an instantiated policy given an array of symbols" do
|
124
|
+
policy = Pundit.policy(user, [:project, :criteria])
|
125
|
+
expect(policy.class).to eq Project::CriteriaPolicy
|
126
|
+
expect(policy.user).to eq user
|
127
|
+
expect(policy.criteria).to eq [:project, :criteria]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns an instantiated policy given an array of a symbol and plain model instance" do
|
131
|
+
policy = Pundit.policy(user, [:project, post])
|
132
|
+
expect(policy.class).to eq Project::PostPolicy
|
133
|
+
expect(policy.user).to eq user
|
134
|
+
expect(policy.post).to eq [:project, post]
|
135
|
+
end
|
136
|
+
|
137
|
+
it "returns an instantiated policy given an array of a symbol and an active model instance" do
|
138
|
+
policy = Pundit.policy(user, [:project, comment])
|
139
|
+
expect(policy.class).to eq Project::CommentPolicy
|
140
|
+
expect(policy.user).to eq user
|
141
|
+
expect(policy.post).to eq [:project, comment]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns an instantiated policy given an array of a symbol and a plain model class" do
|
145
|
+
policy = Pundit.policy(user, [:project, Post])
|
146
|
+
expect(policy.class).to eq Project::PostPolicy
|
147
|
+
expect(policy.user).to eq user
|
148
|
+
expect(policy.post).to eq [:project, Post]
|
149
|
+
end
|
150
|
+
|
151
|
+
it "returns an instantiated policy given an array of a symbol and an active model class" do
|
152
|
+
policy = Pundit.policy(user, [:project, Comment])
|
153
|
+
expect(policy.class).to eq Project::CommentPolicy
|
154
|
+
expect(policy.user).to eq user
|
155
|
+
expect(policy.post).to eq [:project, Comment]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns correct policy class for an array of a multi-word symbols" do
|
159
|
+
policy = Pundit.policy(user, [:project_one_two_three, :criteria_four_five_six])
|
160
|
+
expect(policy.class).to eq ProjectOneTwoThree::CriteriaFourFiveSixPolicy
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model instance" do
|
164
|
+
policy = Pundit.policy(user, [:project_one_two_three, post_four_five_six])
|
165
|
+
expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns correct policy class for an array of a multi-word symbol and a multi-word active model instance" do
|
169
|
+
policy = Pundit.policy(user, [:project_one_two_three, comment_four_five_six])
|
170
|
+
expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
|
171
|
+
end
|
172
|
+
|
173
|
+
it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model class" do
|
174
|
+
policy = Pundit.policy(user, [:project_one_two_three, PostFourFiveSix])
|
175
|
+
expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
|
176
|
+
end
|
177
|
+
|
178
|
+
it "returns correct policy class for an array of a multi-word symbol and a multi-word active model class" do
|
179
|
+
policy = Pundit.policy(user, [:project_one_two_three, CommentFourFiveSix])
|
180
|
+
expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
|
181
|
+
end
|
182
|
+
|
183
|
+
it "returns correct policy class for a multi-word scoped plain model class" do
|
184
|
+
policy = Pundit.policy(user, ProjectOneTwoThree::TagFourFiveSix)
|
185
|
+
expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
|
186
|
+
end
|
187
|
+
|
188
|
+
it "returns correct policy class for a multi-word scoped plain model instance" do
|
189
|
+
policy = Pundit.policy(user, tag_four_five_six)
|
190
|
+
expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
|
191
|
+
end
|
192
|
+
|
193
|
+
it "returns correct policy class for a multi-word scoped active model class" do
|
194
|
+
policy = Pundit.policy(user, ProjectOneTwoThree::AvatarFourFiveSix)
|
195
|
+
expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
|
196
|
+
end
|
197
|
+
|
198
|
+
it "returns correct policy class for a multi-word scoped active model instance" do
|
199
|
+
policy = Pundit.policy(user, avatar_four_five_six)
|
200
|
+
expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
|
201
|
+
end
|
202
|
+
|
106
203
|
it "returns nil if the given policy can't be found" do
|
107
204
|
expect(Pundit.policy(user, article)).to be_nil
|
108
205
|
expect(Pundit.policy(user, Article)).to be_nil
|
@@ -136,20 +233,6 @@ describe Pundit do
|
|
136
233
|
expect(policy.user).to eq user
|
137
234
|
expect(policy.tag).to eq ArticleTag
|
138
235
|
end
|
139
|
-
|
140
|
-
it "returns an instantiated policy given a symbol" do
|
141
|
-
policy = Pundit.policy(user, :criteria)
|
142
|
-
expect(policy.class).to eq CriteriaPolicy
|
143
|
-
expect(policy.user).to eq user
|
144
|
-
expect(policy.criteria).to eq :criteria
|
145
|
-
end
|
146
|
-
|
147
|
-
it "returns an instantiated policy given an array" do
|
148
|
-
policy = Pundit.policy(user, [:project, :criteria])
|
149
|
-
expect(policy.class).to eq Project::CriteriaPolicy
|
150
|
-
expect(policy.user).to eq user
|
151
|
-
expect(policy.criteria).to eq [:project, :criteria]
|
152
|
-
end
|
153
236
|
end
|
154
237
|
end
|
155
238
|
|
@@ -185,7 +268,7 @@ describe Pundit do
|
|
185
268
|
expect(policy.criteria).to eq :criteria
|
186
269
|
end
|
187
270
|
|
188
|
-
it "returns an instantiated policy given an array" do
|
271
|
+
it "returns an instantiated policy given an array of symbols" do
|
189
272
|
policy = Pundit.policy!(user, [:project, :criteria])
|
190
273
|
expect(policy.class).to eq Project::CriteriaPolicy
|
191
274
|
expect(policy.user).to eq user
|
@@ -295,7 +378,7 @@ describe Pundit do
|
|
295
378
|
end
|
296
379
|
|
297
380
|
describe "#pundit_user" do
|
298
|
-
it
|
381
|
+
it "returns the same thing as current_user" do
|
299
382
|
expect(controller.pundit_user).to eq controller.current_user
|
300
383
|
end
|
301
384
|
end
|
@@ -338,10 +421,49 @@ describe Pundit do
|
|
338
421
|
|
339
422
|
describe "#permitted_attributes" do
|
340
423
|
it "checks policy for permitted attributes" do
|
341
|
-
params = ActionController::Parameters.new(
|
424
|
+
params = ActionController::Parameters.new(action: "update", post: {
|
425
|
+
title: "Hello",
|
426
|
+
votes: 5,
|
427
|
+
admin: true
|
428
|
+
})
|
429
|
+
|
430
|
+
expect(Controller.new(user, params).permitted_attributes(post)).to eq("title" => "Hello", "votes" => 5)
|
431
|
+
expect(Controller.new(double, params).permitted_attributes(post)).to eq("votes" => 5)
|
432
|
+
end
|
433
|
+
|
434
|
+
it "checks policy for permitted attributes for record of a ActiveModel type" do
|
435
|
+
params = ActionController::Parameters.new(action: "update", customer_post: {
|
436
|
+
title: "Hello",
|
437
|
+
votes: 5,
|
438
|
+
admin: true
|
439
|
+
})
|
440
|
+
|
441
|
+
expect(Controller.new(user, params).permitted_attributes(customer_post)).to eq("title" => "Hello", "votes" => 5)
|
442
|
+
expect(Controller.new(double, params).permitted_attributes(customer_post)).to eq("votes" => 5)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
describe "#permitted_attributes_for_action" do
|
447
|
+
it "is checked if it is defined in the policy" do
|
448
|
+
params = ActionController::Parameters.new(action: "revise", post: {
|
449
|
+
title: "Hello",
|
450
|
+
body: "blah",
|
451
|
+
votes: 5,
|
452
|
+
admin: true
|
453
|
+
})
|
454
|
+
|
455
|
+
expect(Controller.new(user, params).permitted_attributes(post)).to eq("body" => "blah")
|
456
|
+
end
|
457
|
+
|
458
|
+
it "can be explicitly set" do
|
459
|
+
params = ActionController::Parameters.new(action: "update", post: {
|
460
|
+
title: "Hello",
|
461
|
+
body: "blah",
|
462
|
+
votes: 5,
|
463
|
+
admin: true
|
464
|
+
})
|
342
465
|
|
343
|
-
expect(Controller.new(user, params).permitted_attributes(post)).to eq(
|
344
|
-
expect(Controller.new(double, params).permitted_attributes(post)).to eq({ 'votes' => 5 })
|
466
|
+
expect(Controller.new(user, params).permitted_attributes(post, :revise)).to eq("body" => "blah")
|
345
467
|
end
|
346
468
|
end
|
347
469
|
|
data/spec/spec_helper.rb
CHANGED
@@ -26,15 +26,24 @@ RSpec.configure do |config|
|
|
26
26
|
end
|
27
27
|
|
28
28
|
class PostPolicy < Struct.new(:user, :post)
|
29
|
+
class Scope < Struct.new(:user, :scope)
|
30
|
+
def resolve
|
31
|
+
scope.published
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
29
35
|
def update?
|
30
36
|
post.user == user
|
31
37
|
end
|
38
|
+
|
32
39
|
def destroy?
|
33
40
|
false
|
34
41
|
end
|
42
|
+
|
35
43
|
def show?
|
36
44
|
true
|
37
45
|
end
|
46
|
+
|
38
47
|
def permitted_attributes
|
39
48
|
if post.user == user
|
40
49
|
[:title, :votes]
|
@@ -42,61 +51,98 @@ class PostPolicy < Struct.new(:user, :post)
|
|
42
51
|
[:votes]
|
43
52
|
end
|
44
53
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
scope.published
|
54
|
+
|
55
|
+
def permitted_attributes_for_revise
|
56
|
+
[:body]
|
49
57
|
end
|
50
58
|
end
|
59
|
+
|
51
60
|
class Post < Struct.new(:user)
|
52
61
|
def self.published
|
53
62
|
:published
|
54
63
|
end
|
55
|
-
|
56
|
-
def
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"Post"
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
"#<Post>"
|
71
|
+
end
|
57
72
|
end
|
58
73
|
|
59
|
-
|
60
|
-
class
|
61
|
-
|
62
|
-
|
74
|
+
module Customer
|
75
|
+
class Post < Post
|
76
|
+
def model_name
|
77
|
+
OpenStruct.new(param_key: "customer_post")
|
78
|
+
end
|
79
|
+
|
80
|
+
def policy_class
|
81
|
+
PostPolicy
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class CommentPolicy < Struct.new(:user, :comment)
|
87
|
+
class Scope < Struct.new(:user, :scope)
|
88
|
+
def resolve
|
89
|
+
scope
|
90
|
+
end
|
63
91
|
end
|
64
92
|
end
|
65
|
-
class Comment; extend ActiveModel::Naming; end
|
66
93
|
|
67
|
-
|
94
|
+
class Comment
|
95
|
+
extend ActiveModel::Naming
|
96
|
+
end
|
97
|
+
|
68
98
|
class CommentsRelation
|
69
|
-
def initialize(empty=false)
|
70
|
-
|
71
|
-
|
99
|
+
def initialize(empty = false)
|
100
|
+
@empty = empty
|
101
|
+
end
|
102
|
+
|
103
|
+
def blank?
|
104
|
+
@empty
|
105
|
+
end
|
106
|
+
|
107
|
+
def model_name
|
108
|
+
Comment.model_name
|
109
|
+
end
|
72
110
|
end
|
73
111
|
|
74
112
|
class Article; end
|
75
113
|
|
76
114
|
class BlogPolicy < Struct.new(:user, :blog); end
|
115
|
+
|
77
116
|
class Blog; end
|
117
|
+
|
78
118
|
class ArtificialBlog < Blog
|
79
119
|
def self.policy_class
|
80
120
|
BlogPolicy
|
81
121
|
end
|
82
122
|
end
|
123
|
+
|
124
|
+
class ArticleTagOtherNamePolicy < Struct.new(:user, :tag)
|
125
|
+
def show?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def destroy?
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
83
134
|
class ArticleTag
|
84
135
|
def self.policy_class
|
85
|
-
|
86
|
-
def show?
|
87
|
-
true
|
88
|
-
end
|
89
|
-
def destroy?
|
90
|
-
false
|
91
|
-
end
|
92
|
-
end
|
136
|
+
ArticleTagOtherNamePolicy
|
93
137
|
end
|
94
138
|
end
|
95
139
|
|
96
140
|
class CriteriaPolicy < Struct.new(:user, :criteria); end
|
97
141
|
|
98
142
|
module Project
|
143
|
+
class CommentPolicy < Struct.new(:user, :post); end
|
99
144
|
class CriteriaPolicy < Struct.new(:user, :criteria); end
|
145
|
+
class PostPolicy < Struct.new(:user, :post); end
|
100
146
|
end
|
101
147
|
|
102
148
|
class DenierPolicy < Struct.new(:user, :record)
|
@@ -127,3 +173,23 @@ class NilClassPolicy
|
|
127
173
|
raise "I'm only here to be annoying!"
|
128
174
|
end
|
129
175
|
end
|
176
|
+
|
177
|
+
class PostFourFiveSix < Struct.new(:user); end
|
178
|
+
|
179
|
+
class CommentFourFiveSix; extend ActiveModel::Naming; end
|
180
|
+
|
181
|
+
module ProjectOneTwoThree
|
182
|
+
class CommentFourFiveSixPolicy < Struct.new(:user, :post); end
|
183
|
+
|
184
|
+
class CriteriaFourFiveSixPolicy < Struct.new(:user, :criteria); end
|
185
|
+
|
186
|
+
class PostFourFiveSixPolicy < Struct.new(:user, :post); end
|
187
|
+
|
188
|
+
class TagFourFiveSix < Struct.new(:user); end
|
189
|
+
|
190
|
+
class TagFourFiveSixPolicy < Struct.new(:user, :tag); end
|
191
|
+
|
192
|
+
class AvatarFourFiveSix; extend ActiveModel::Naming; end
|
193
|
+
|
194
|
+
class AvatarFourFiveSixPolicy < Struct.new(:user, :avatar); end
|
195
|
+
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: 1.0
|
4
|
+
version: 1.1.0
|
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:
|
12
|
+
date: 2016-01-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -123,6 +123,20 @@ dependencies:
|
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rubocop
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
126
140
|
description: Object oriented authorization for Rails applications
|
127
141
|
email:
|
128
142
|
- jonas.nicklas@gmail.com
|
@@ -132,7 +146,9 @@ extensions: []
|
|
132
146
|
extra_rdoc_files: []
|
133
147
|
files:
|
134
148
|
- ".gitignore"
|
149
|
+
- ".rubocop.yml"
|
135
150
|
- ".travis.yml"
|
151
|
+
- ".yardopts"
|
136
152
|
- CHANGELOG.md
|
137
153
|
- CODE_OF_CONDUCT.md
|
138
154
|
- CONTRIBUTING.md
|
@@ -178,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
194
|
version: '0'
|
179
195
|
requirements: []
|
180
196
|
rubyforge_project:
|
181
|
-
rubygems_version: 2.4.
|
197
|
+
rubygems_version: 2.4.8
|
182
198
|
signing_key:
|
183
199
|
specification_version: 4
|
184
200
|
summary: OO authorization for Rails
|