pundit 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://secure.travis-ci.org/elabs/pundit.
|
4
|
-
[![Code Climate](https://codeclimate.com/github/elabs/pundit.
|
5
|
-
[![Inline docs](http://inch-ci.org/github/elabs/pundit.
|
3
|
+
[![Build Status](https://secure.travis-ci.org/elabs/pundit.svg?branch=master)](https://travis-ci.org/elabs/pundit)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/elabs/pundit.svg)](https://codeclimate.com/github/elabs/pundit)
|
5
|
+
[![Inline docs](http://inch-ci.org/github/elabs/pundit.svg?branch=master)](http://inch-ci.org/github/elabs/pundit)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/pundit.svg)](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
|