pundit_roles 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9d41f133e02bb2d62751987d703e9f1df6626ee
4
- data.tar.gz: 1ca3ac5b8b5dbba131c29417e45e6fc62f154d55
3
+ metadata.gz: d24862dda5081913834ff5fc0063cc46c98e81c0
4
+ data.tar.gz: 8a426b027d8cf4889d4b11c466e11e6492dfa5fc
5
5
  SHA512:
6
- metadata.gz: 456ee73a9e3145e0d2ca12daa6c65507a35a4eceff7f1e5aaba438a0a7784b0847edf33c1dc0c7ae01ed129879b1005ae5e34f146db482d5a792da0e25348ee6
7
- data.tar.gz: 401da8afe1459ddb0b6dc36a9cfbea9b44ff2fdb165c9ff1d7b7fd459fb29837e4b81a2268ec3039732359adcb0ed181f9c6cf1a721629ed384f1c260121a2bb
6
+ metadata.gz: 41103de643378f38f4f55306f39010301a15ed09d4bc89c7effe183a29c011f0293fbbc478e4e0e925fab4f4e9d51647622d4ae4c66fd502798902b8b1817bea
7
+ data.tar.gz: 46fa3a352eb53d5d7a098d990255c96a4572793c0a20df4f3f6cb7b7712a9c9eb945670908e0fd6aadea2e6d9655e01c724f0f9eb1b5684e6d5e5d8b7c975e9a
data/CHANGELOG.md CHANGED
@@ -1,10 +1,16 @@
1
1
  # PunditRoles
2
2
 
3
- ## 0.2.0 (2017-10-30)
3
+ ## 0.2.1 (2017-10-30)
4
4
 
5
5
  - Roles and permitted options are no longer separately declared with `role` and
6
6
  `permitted_for` methods. Declaration of options has been consolidated into the
7
7
  `role` method.
8
8
  - Roles can no longer be inherited from the superclass.
9
9
  - Test conditions for the roles are now guessed from the name of the role,
10
- instead of being declared explicitly with the `authorize_with` option.
10
+ instead of being declared explicitly with the `authorize_with` option.
11
+ - Policy instance variable @record renamed @resource
12
+
13
+ ## 0.5.0 (2017-11-08)
14
+
15
+ - `authorize` method has been renamed to `authorize!`
16
+ - added support for limiting scopes, can be called with `policy_scope!`
data/README.md CHANGED
@@ -7,15 +7,15 @@
7
7
 
8
8
  PunditRoles is a helper gem which works on top of [Pundit](https://github.com/elabs/pundit)
9
9
  (if you are not familiar with Pundit, it is recommended you read it's documentation before continuing).
10
- It allows you to extend Pundit's authorization system to include attributes and associations.
10
+ It allows you to extend Pundit's authorization system to include attributes and associations, and provides a couple of
11
+ helpers for convenience.
11
12
 
12
13
  If you are already using Pundit, this should not conflict with any of Pundit's existing functionality.
13
- You may use Pundit's features as well as the features from this gem interchangeably.
14
+ You may use Pundit's features as well as the features from this gem interchangeably. There are
15
+ some caveats however, see the [Porting over from Pundit](#Porting over from Pundit).
14
16
 
15
17
  Please note that this gem is not affiliated with Pundit or it's creators, but it very much
16
- appreciates the work that they did with their great authorization system.
17
-
18
- * **Important**: This gem is **not** yet considered production ready.
18
+ appreciates the work that they did with their great authorization system.
19
19
 
20
20
  ## Installation
21
21
 
@@ -44,8 +44,7 @@ end
44
44
  PunditRoles operates around the notion of _**roles**_. Each role needs to be defined at the Policy level
45
45
  and provided with a conditional method that determines whether the `@user`(the `current_user` in the context of a Policy)
46
46
  falls into this role. Additionally, each role can have a set of permitted
47
- _**attributes**_ and _**associations**_(from here on collectively referred to as _**options**_)
48
- defined for it. A basic example for a UserPolicy would be:
47
+ _**attributes**_ and _**associations**_ defined for it. A basic example for a UserPolicy would be:
49
48
  ```ruby
50
49
  class UserPolicy < ApplicationPolicy
51
50
  role :regular_user,
@@ -71,7 +70,8 @@ end
71
70
  This assumes that there are two methods defined in the UserPolicy called `regular_user?` and
72
71
  `correct_user?`.
73
72
 
74
- * Please note, that there was a breaking change since `0.1.2`. View the [changelog](https://github.com/StairwayB/pundit_roles/blob/master/CHANGELOG.md) for additional details.
73
+ * Please note, that there were a couple of breaking change since `0.2.1`. View the
74
+ [changelog](https://github.com/StairwayB/pundit_roles/blob/master/CHANGELOG.md) for additional details.
75
75
 
76
76
  And then in you query method, you simply say:
77
77
  ```ruby
@@ -90,19 +90,19 @@ def show?
90
90
  end
91
91
  ```
92
92
 
93
- Finally, in your controller you call Pundit's `authorize` method and pass it's return value
93
+ Finally, in your controller you call `authorize!` method and pass it's return value
94
94
  to a variable:
95
95
  ```ruby
96
96
  class UserController < ApplicationController
97
97
  def show
98
98
  @user = User.find(params[:id])
99
- permitted = authorize @user
99
+ permitted = authorize! @user
100
100
  # [...]
101
101
  end
102
102
  end
103
103
  ```
104
104
 
105
- The `authorize` method will return a hash of permitted attributes and associations for the corresponding action that the
105
+ The `authorize!` method will return a hash of permitted attributes and associations for the corresponding action that the
106
106
  user has access to. What you do with that is your business. Accessors for each segment look like this:
107
107
  ```ruby
108
108
  permitted[:attributes][:show] # ex. returns => [:username, :name, :avatar, :is_confirmed, :created_at]
@@ -112,7 +112,7 @@ permitted[:associations][:show]
112
112
  permitted[:associations][:update]
113
113
  ```
114
114
 
115
- It hash also contains the roles that the user has fulfilled:
115
+ The hash also contains the roles that the user has fulfilled:
116
116
  ```ruby
117
117
  permitted[:roles] # ex. returns => [:regular_user, :correct_user]
118
118
  ```
@@ -122,37 +122,42 @@ If the user does not fall into any roles permitted by a query, the `authorize` m
122
122
  ### Defining roles
123
123
 
124
124
  Roles are defined with the `role` method. It receives the name of the role as it's first argument and the
125
- options for the role as it's second(multiple `roles` can be defined at once, if you wish to do that,
126
- but all of these will have identical options, so what's the point, really). Valid options for the role are:
127
- `:attributes, :associations, :scope, :uses_db, :extend`.
125
+ options for the role as it's second. Additionally, you need to define a method which checks if
126
+ the user falls into that role. This method's name must be the name of the role with a question
127
+ mark at the end. For example, a `:correct_user` role's conditional method must be declared as
128
+ `correct_user?`.
128
129
 
129
- Currently only attributes and associations are supported, scopes and db permissions will be coming Soon&trade;.
130
+ Valid options for roles are:
131
+ `:attributes, :associations, :scope`
130
132
 
131
133
  ```ruby
132
- role :regular_user,
134
+ role :correct_user,
133
135
  attributes: {show: [:name]},
134
136
  associations: {show: [:posts]}
135
137
 
136
138
  private
137
139
 
138
- def regular_user?
139
- @user.present?
140
+ def correct_user?
141
+ @user.id == @resource.id
140
142
  end
141
143
  ```
142
144
 
143
145
  One thing to watch out for is that roles are not inherited, because each is unique to the model in question.
144
146
  But since the name of the role is just the conditional method for the role,
145
147
  without the '?' question mark, it is encouraged to inherit from an `ApplicationPolicy`,
146
- and define common `role` conditional methods there.
148
+ and define common `role` conditionals there.
149
+
150
+ * see [Declaring attributes and associations](#Declaring attributes and associations) for how to declare
151
+ attributes and associations.
147
152
 
148
153
  ### Users with multiple roles
149
154
 
150
- You may have noticed that in the first example `correct_user` has fewer permitted options
155
+ You may have noticed that in the first example `correct_user` has fewer permitted attributes and associations
151
156
  defined than `regular_user`. That is because PunditRoles does not treat roles as exclusionary.
152
157
  Users may have a single role or they may have multiple roles, within the context of the model they are trying to access.
153
158
  In the previous example, a `correct_user`, meaning a `regular_user` trying to access it's own model, is naturally
154
- also a `regular_user`, so it will have access to all options a `regular_user` has access to plus the
155
- options that a `correct_user` has access to.
159
+ also a `regular_user`, so it will have access to all attributes and associations a `regular_user` has access to plus the
160
+ ones that a `correct_user` has access to.
156
161
 
157
162
  Take this example, to better illustrate what is happening:
158
163
 
@@ -174,7 +179,7 @@ role :admin_user,
174
179
  ```
175
180
 
176
181
  Here, a user which fulfills the `admin_user` condition trying to access it's own model, would receive the
177
- options of all three roles, without any duplicates, meaning the `permitted[:attributes][:show]` would look like:
182
+ attributes and associations of all three roles, without any duplicates, meaning the `permitted[:attributes][:show]` would look like:
178
183
  ```ruby
179
184
  [:username, :name, :avatar, :email, :phone_number, :is_admin]
180
185
  ```
@@ -217,16 +222,106 @@ end
217
222
  with other roles. It is also the first role that is evaluated, and if the user is a `:guest`, it will return the guest
218
223
  attributes if `:guest` is allowed, or raise `Pundit::NotAuthorizedError` if not.
219
224
 
220
- * Do **not** use a custom role for `nil` or `undefined` `@user`, use `:guest`.
221
- If you do, it will most likely lead to unwanted exceptions.
225
+ * Do **not** use a custom role for `nil` users, use `:guest`.
226
+ If you do, it will most likely lead to unwanted errors.
227
+
228
+ ### Scopes
229
+ PunditRoles supports limiting scopes for actions which return a list of records. If you wish to do
230
+ this, define a scope option for a role as a `lambda`, and then call `policy_scope!` for the list you want to
231
+ limit. It should look something like this:
232
+ ```ruby
233
+ role :guest,
234
+ attributes: {
235
+ show: %i(name avatar),
236
+ },
237
+ associations: {},
238
+ scope: lambda{resource.where(visible_publicly: true)}
239
+ role :regular_user,
240
+ attributes: {
241
+ show: %i(username name avatar)
242
+ },
243
+ associations: {
244
+ show: %i(posts followers following)
245
+ },
246
+ scope: lambda{resource.where.not(id: user.id)}
247
+
248
+ def index?
249
+ allow :guest, :regular_user
250
+ end
251
+ ```
252
+ Then in your controller you pass the list you want to limit based on what role the current user fulfills:
253
+ ```ruby
254
+ def index
255
+ @users = policy_scope!(User.all)
256
+ end
257
+ ```
258
+ The `policy_scope!` method returns the scope for the role, or raises `Pundit::NotAuthorizedError` if the user is not
259
+ allowed to perform the action. Since the syntax for permitting scopes is the same as the syntax for getting the permitted
260
+ attributes and associations, you may use both `authorize!` and `policy_scope!` for the same action. A recommended usage is
261
+ to use both(this example uses the excellent [jsonapi-rails](https://github.com/jsonapi-rb/jsonapi-rails) gem for serialization):
262
+ ```ruby
263
+ def index
264
+ @users = policy_scope!(User.all)
265
+ permitted = authorize! @users
266
+ render jsonapi: @users, fields: {users: permitted[:attributes][:show]}
267
+ end
268
+ ```
269
+
270
+ #### Important: Scope declaration order
271
+
272
+ While attributes and associations for roles are merged, scopes are **not**! This means that whenever you wish to authorize a list of records,
273
+ you must take care in what order you define the roles. PunditRoles will go over the allowed roles in a query method in the
274
+ order in which they were defined, and when it finds a role that the user fulfills, it will return the scope for that role.
275
+
276
+ Take this example, where there are two roles permitted for an `index` action: `regular_user` and `:admin_user`:
277
+ ```ruby
278
+
279
+ role :regular_user, scope: lambda{resource.regular_user}
280
+ role :admin_user, scope: lambda{resource.admin_user}
222
281
 
223
- ### Explicit declaration of options
282
+ def index?
283
+ allow :regular_user, :admin_user
284
+ end
285
+
286
+ private
287
+
288
+ def regular_user?
289
+ @user.present?
290
+ end
291
+
292
+ def admin_user?
293
+ @user.admin?
294
+ end
295
+ ```
296
+
297
+ Whenever an admin tries to access the `index` action, PunditRoles will first check if the admin is a `regular_user`,
298
+ which will be true, since admin is in fact logged in. Therefore, it will return the scope defined for `regular_user`,
299
+ instead of the scope defined for `admin_user`. This is not the desired behaviour. In order to avoid this, the `index?` method
300
+ needs to look like this:
301
+ ```ruby
302
+ def index?
303
+ allow :admin_user, :regular_user
304
+ end
305
+ ```
306
+ In this case, `admin_user` is evaluated before `regular_user`, so admins will correctly get their own scope, instead of the
307
+ `regular_user` scope.
308
+
309
+ * The rule is: whenever a role supersedes another, declare that role first. If two or more roles are exclusionary,
310
+ meaning that there is no way that a user can fulfill more than one of these roles, then the order in which they are declared
311
+ does not matter. The guest role can be declared wherever, since PunditRoles will always evaluate whether the user is a
312
+ `guest` first.
313
+
314
+ ### Declaring attributes and associations
315
+
316
+ * Attributes and associations in this heading are referred to collectively as _options_
317
+
318
+ ##### Explicit declaration of options
224
319
 
225
320
  Options are declared with the `attributes` and `associations` options of the role method.
226
321
 
227
322
  Valid options for both `:attributes` and `:associations` are `:show`,`:create`,`:update` and `:save` or the implicit options.
228
323
 
229
- ### Implicit declaration of options
324
+ ##### Implicit declaration of options
230
325
 
231
326
  PunditRoles provides a set of helpers to be able to implicitly declare the options of a role.
232
327
 
@@ -299,17 +394,21 @@ RESTRICTED_SHOW_ATTRIBUTES = RESTRICTED_SHOW_ATTRIBUTES + [:attr_one, :attr_two]
299
394
  There are 8 `RESTRICTED_#{action}_#{option_type}` constants in total, where `option_type` refers
300
395
  to either `ATTRIBUTES` or `ASSOCIATIONS` and `action` refers to `SHOW`, `CREATE`, `UPDATE` or `SAVE`.
301
396
 
302
- ## Planned updates
303
-
304
- Support for Pundit's scope method should be added in the near future, along with authorizing associations,
305
- generators, and possibly rspec helpers.
397
+ ## Porting over from Pundit
398
+ If you're already using Pundit, this gem should not conflict with any existing functionality. However, there
399
+ are a couple of things to watch out for:
400
+ * PunditRoles uses `@resouce` instead of `@record` in the Policy. This change was made, to reflect the
401
+ fact that the Policy can have scopes as well as records passed to it.
402
+ * PunditRoles uses the bang methods `authorize!` and `policy_scope!`, instead of `authorize` and `policy_scope`.
403
+ * PunditRoles does not use the `Scope` class of Pundit, but it is included in `Policy::Base` so you may use
404
+ that as well, if you so choose.
306
405
 
406
+ ## Planned updates
407
+ Authorizing associations, generators, and possibly rspec helpers will be coming in the near future.
307
408
 
308
409
  ## Contributing
309
-
310
410
  Bug reports are welcome on GitHub at [StairwayB](https://github.com/StairwayB/pundit_roles).
311
411
 
312
412
  ## License
313
-
314
413
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
315
414
 
@@ -8,7 +8,7 @@ module Policy
8
8
  # return a uniquely merged hash of permitted attributes and associations of each role the @user has.
9
9
  #
10
10
  # @attr_reader user [Object] the user that initiated the action
11
- # @attr_reader record [Object] the object we're checking permissions of
11
+ # @attr_reader resource [Object] the object we're checking permissions of
12
12
  class Base
13
13
  extend Role
14
14
  include PolicyDefaults
@@ -25,62 +25,89 @@ module Policy
25
25
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
26
26
  def resolve_query(query)
27
27
  permitted_roles = public_send(query)
28
- if permitted_roles.is_a? TrueClass or permitted_roles.is_a? FalseClass
29
- return permitted_roles
30
- end
28
+ return permitted_roles if permitted_roles.is_a? TrueClass or permitted_roles.is_a? FalseClass
31
29
 
32
- permissions_hash = self.class.permissions_hash
30
+ validate_permission_type(permitted_roles, query)
31
+ permissions = self.class.permissions
33
32
 
34
- # Always checks if user is a guest, and return the appropriate permission if true
35
- # the guest role cannot be merged with other roles
36
33
  if guest?
37
- return handle_guest_user(permitted_roles, permissions_hash)
34
+ return handle_guest_options(permitted_roles, permissions)
38
35
  end
39
- current_roles = determine_current_roles(permitted_roles, permissions_hash)
40
36
 
41
- unless current_roles.present?
42
- return false
43
- end
37
+ current_roles = determine_current_roles(permitted_roles)
38
+ return false unless current_roles.present?
44
39
 
45
- if current_roles.length == 1
46
- return current_roles.values[0].merge({roles: [current_roles.keys[0]]})
40
+ return options_or_merge(current_roles, permissions)
41
+ end
42
+
43
+ # Retrieves the permitted roles for the current query and checks each role, until it finds one that
44
+ # that the user fulfills. It returns the defined scope for that role. Scopes do no merge with other scopes
45
+ #
46
+ # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
47
+ def resolve_scope(query)
48
+ permitted_roles = public_send(query)
49
+ return permitted_roles if permitted_roles.is_a? TrueClass or permitted_roles.is_a? FalseClass
50
+
51
+ validate_permission_type(permitted_roles, query)
52
+ scopes = self.class.scopes
53
+
54
+ if guest?
55
+ return handle_guest_scope(permitted_roles, scopes)
47
56
  end
48
57
 
49
- return unique_merge(current_roles)
58
+ current_roles = determine_current_roles(permitted_roles)
59
+ return false unless current_roles.present?
60
+
61
+ return instance_eval &scopes[current_roles[0]]
50
62
  end
51
63
 
52
64
  private
53
65
 
54
- # Return the default :guest role if guest is present in @permitted_roles. Return false otherwise
66
+ # Return the default :guest role if guest is present in permitted_roles. Return false otherwise
55
67
  #
56
68
  # @param permitted_roles [Hash] roles returned by the query
57
- # @param permissions_hash [Hash] unrefined hash of options defined by all permitted_for methods
58
- def handle_guest_user(permitted_roles, permissions_hash)
69
+ # @param permissions [Hash] unrefined hash of options defined by all permitted_for methods
70
+ def handle_guest_options(permitted_roles, permissions)
71
+ if permitted_roles.include? :guest
72
+ return permissions[:guest].merge({roles: [:guest]})
73
+ end
74
+ return false
75
+ end
76
+
77
+ def handle_guest_scope(permitted_roles, scopes)
59
78
  if permitted_roles.include? :guest
60
- return permissions_hash[:guest].merge({roles: [:guest]})
79
+ return instance_eval &scopes[:guest]
61
80
  end
62
81
  return false
63
82
  end
64
83
 
65
- # Build a hash of the roles that the user fulfills and the roles' attributes and associations,
66
- # based on the test_condition of the role.
84
+ # inspects the current_roles and returns the appropriate option
85
+ #
86
+ # @param current_roles [Hash] roles that the current user fulfills
87
+ # @param permissions [Hash] unrefined hash of options defined by all permitted_for methods
88
+ def options_or_merge(current_roles, permissions)
89
+ return false unless current_roles.present?
90
+
91
+ if current_roles.length == 1
92
+ return permissions[current_roles[0]].merge({roles: [current_roles[0]]})
93
+ end
94
+
95
+ return unique_merge(current_roles, permissions)
96
+ end
97
+
98
+ # Build an Array of the roles that the user fulfills.
67
99
  #
68
100
  # @param permitted_roles [Hash] roles returned by the query
69
- # @param permissions_hash [Hash] unrefined hash of options defined by all permitted_for methods
70
- def determine_current_roles(permitted_roles, permissions_hash)
71
- current_roles = {}
101
+ def determine_current_roles(permitted_roles)
102
+ current_roles = []
72
103
 
73
104
  permitted_roles.each do |permitted_role|
74
- if permitted_role == :guest or permitted_role == :guest_user
105
+ if permitted_role == :guest
75
106
  next
76
107
  end
77
108
 
78
- begin
79
- if send("#{permitted_role}?")
80
- current_roles[permitted_role] = permissions_hash[permitted_role]
81
- end
82
- rescue NoMethodError => e
83
- raise NoMethodError, "Undefined test condition, it must be defined as 'role?', where, role is :#{permitted_role}, => #{e.message}"
109
+ if test_condition?(permitted_role)
110
+ current_roles << permitted_role
84
111
  end
85
112
  end
86
113
 
@@ -89,13 +116,13 @@ module Policy
89
116
 
90
117
  # Uniquely merge the options of all roles that the user fulfills
91
118
  #
92
- # @param roles [Hash] roles and options that the user fulfills
93
- def unique_merge(roles)
94
- merged_hash = {attributes: {}, associations: {}, roles: []}
119
+ # @param roles [Hash] roles that the user fulfills
120
+ # @param permissions [Hash] the options for all roles
121
+ def unique_merge(roles, permissions)
122
+ merged_hash = {attributes: {}, associations: {}, roles: roles}
95
123
 
96
- roles.each do |role, option|
97
- merged_hash[:roles] << role
98
- option.each do |type, actions|
124
+ roles.each do |role|
125
+ permissions[role].each do |type, actions|
99
126
  actions.each do |key, value|
100
127
  unless merged_hash[type][key]
101
128
  merged_hash[type][key] = []
@@ -108,6 +135,22 @@ module Policy
108
135
  return merged_hash
109
136
  end
110
137
 
138
+ # Helper method for testing the conditional of a role
139
+ #
140
+ # @param role [Symbol] the role to be tested
141
+ # @raise [NoMethodError] if the test condition is undefined
142
+ def test_condition?(role)
143
+ begin
144
+ if send("#{role}?")
145
+ return true
146
+ end
147
+ rescue NoMethodError => e
148
+ raise NoMethodError, "Undefined test condition, it must be defined as 'role?', where, role is :#{role}, => #{e.message}"
149
+ end
150
+
151
+ return false
152
+ end
153
+
111
154
  # Helper method to be able to define allow: :guest, :user, etc. in the query methods
112
155
  #
113
156
  # @param *roles [Array] an array of permitted roles for a particular action
@@ -120,8 +163,25 @@ module Policy
120
163
  @user.nil?
121
164
  end
122
165
 
123
- # Scope class from Pundit, to be used for limiting scopes. Unchanged from Pundit,
124
- # possible implementation forthcoming in a future update
166
+ # @api private
167
+ def validate_permission_type(permitted_roles, query)
168
+ valid = false
169
+ _allowed_permission_types.each do |type|
170
+ if permitted_roles.is_a? type
171
+ valid = true
172
+ break
173
+ end
174
+ end
175
+
176
+ raise ArgumentError, "expected #{_allowed_permission_types} in #{query}, got #{permitted_roles.inspect}" unless valid
177
+ end
178
+
179
+ # @api private
180
+ def _allowed_permission_types
181
+ [Array, FalseClass, TrueClass]
182
+ end
183
+
184
+ # Scope class from Pundit, not used in this gem
125
185
  class Scope
126
186
  attr_reader :user, :scope
127
187
 
@@ -3,21 +3,22 @@ require_relative 'role/option_builder'
3
3
  # Extended by Policy::Base. Defines the methods necessary for declaring roles.
4
4
  module Role
5
5
 
6
- attr_accessor :permissions_hash
6
+ attr_accessor :permissions
7
+ attr_accessor :scopes
7
8
 
8
- # Builds a new role by saving it into the #permissions_hash class instance variable
9
+ # Builds a new role by saving it into the #permissions class instance variable
9
10
  # Valid options are :attributes, :associations, :scope, :uses_db, :extend
10
11
  #
11
12
  # @param *opts [Array] the roles, and the options which define the roles
12
13
  # @raise [ArgumentError] if the options are incorrectly defined, or no options are present
13
- # @return [Hash] Returns the permissions hash or the record
14
14
  def role(*opts)
15
15
  user_opts = opts.extract_options!.dup
16
16
  options = user_opts.slice(*_role_default_keys)
17
17
 
18
18
  raise ArgumentError, 'Please provide at least one role' unless opts.present?
19
19
 
20
- @permissions_hash = {} if @permissions_hash.nil?
20
+ @permissions = {} if @permissions.nil?
21
+ @scopes = {} if @scopes.nil?
21
22
 
22
23
  options.each do |key, value|
23
24
  if value.present?
@@ -32,7 +33,8 @@ module Role
32
33
 
33
34
  opts.each do |role|
34
35
  raise ArgumentError, "Expected Symbol for #{role}, got #{role.class}" unless role.is_a? Symbol
35
- @permissions_hash[role] = OptionBuilder.new(self, options[:attributes], options[:associations], options[:scope]).permitted
36
+ @permissions[role] = OptionBuilder.new(self, options[:attributes], options[:associations], options[:scope]).permitted
37
+ @scopes[role] = options[:scope]
36
38
  end
37
39
  end
38
40
 
@@ -43,7 +45,7 @@ module Role
43
45
 
44
46
  # @api private
45
47
  private def _role_option_validations
46
- {attributes: [Hash, Symbol], associations: [Hash, Symbol], scope: [String], uses_db: [Symbol], extend: [Symbol]}
48
+ {attributes: [Hash, Symbol], associations: [Hash, Symbol], scope: [Proc], uses_db: [Symbol], extend: [Symbol]}
47
49
  end
48
50
 
49
51
  end
@@ -1,32 +1,56 @@
1
1
  # Contains the overwritten #authorize method
2
2
  module PunditOverwrite
3
3
 
4
- # Overwrite for Pundit's default authorization, to be able to use PunditRoles. Does not conflict with existing
5
- # Pundit implementations
4
+ # A modified version of Pundit's default authorization. Returns a hash of permitted attributes or raises exception
5
+ # it the user is not authorized
6
6
  #
7
7
  # @param resource [Object] the object we're checking permissions of
8
8
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
9
9
  # If omitted then this defaults to the Rails controller action name.
10
10
  # @raise [NotAuthorizedError] if the given query method returned false
11
- # @return [Object, Hash] Returns the permissions hash or the record
12
- def authorize(resource, query = nil)
11
+ # @return [Object, Hash] Returns the permissions hash or the resource
12
+ def authorize!(resource, query = nil)
13
13
  query ||= params[:action].to_s + '?'
14
14
 
15
15
  @_pundit_policy_authorized = true
16
16
 
17
17
  policy = policy(resource)
18
-
19
18
  permitted_records = policy.resolve_query(query)
20
19
 
21
- unless permitted_records
20
+ return determine_action(resource, query, policy, permitted_records)
21
+ end
22
+
23
+ # Returns the permitted scope or raises exception
24
+ #
25
+ # @param resource [Object] the object we're checking permissions of
26
+ # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
27
+ # If omitted then this defaults to the Rails controller action name.
28
+ # @raise [NotAuthorizedError] if the given query method returned false
29
+ # @return [Object, ActiveRecord::Association] Returns the permissions hash or the resource
30
+ def policy_scope!(resource, query = nil)
31
+ query ||= params[:action].to_s + '?'
32
+
33
+ @_pundit_policy_scoped = true
34
+
35
+ policy = policy(resource)
36
+ permitted_scope = policy.resolve_scope(query)
37
+
38
+ return determine_action(resource, query, policy, permitted_scope)
39
+ end
40
+
41
+ private
42
+
43
+ # @api private
44
+ def determine_action(resource, query, policy, permitted)
45
+ unless permitted
22
46
  raise Pundit::NotAuthorizedError, query: query, record: resource, policy: policy
23
47
  end
24
48
 
25
- if permitted_records.is_a? TrueClass
49
+ if permitted.is_a? TrueClass
26
50
  return resource
27
51
  end
28
52
 
29
- return permitted_records
53
+ return permitted
30
54
  end
31
55
  end
32
56
 
@@ -1,3 +1,3 @@
1
1
  module PunditRoles
2
- VERSION = "0.2.1"
2
+ VERSION = "0.5.0"
3
3
  end
data/pundit_roles.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Daniel Balogh"]
10
10
  spec.email = ["danielferencbalogh@gmail.com"]
11
11
 
12
- spec.summary = %q{Extends Pundit with roles, allowing attribute and association level authorizations}
13
- spec.description = %q{Extends Pundit with roles, allowing attribute and association level authorizations}
12
+ spec.summary = %q{Extends Pundit with roles, allowing attribute and association level authorizations, suits modern APIs well}
13
+ spec.description = %q{Extends Pundit with roles, allowing attribute and association level authorizations, suits modern APIs well}
14
14
  spec.homepage = "https://github.com/StairwayB/pundit_roles"
15
15
  spec.license = "MIT"
16
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pundit_roles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Balogh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-30 00:00:00.000000000 Z
11
+ date: 2017-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,7 +38,8 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.1.0
41
- description: Extends Pundit with roles, allowing attribute and association level authorizations
41
+ description: Extends Pundit with roles, allowing attribute and association level authorizations,
42
+ suits modern APIs well
42
43
  email:
43
44
  - danielferencbalogh@gmail.com
44
45
  executables: []
@@ -86,5 +87,6 @@ rubyforge_project:
86
87
  rubygems_version: 2.6.11
87
88
  signing_key:
88
89
  specification_version: 4
89
- summary: Extends Pundit with roles, allowing attribute and association level authorizations
90
+ summary: Extends Pundit with roles, allowing attribute and association level authorizations,
91
+ suits modern APIs well
90
92
  test_files: []