moat 0.0.8 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 159b847055cd79c58ce1e5b99788d74d78cb7dae
4
+ data.tar.gz: b5393955e97f8b5ccb8f9d8ff1dc29942307ca33
5
+ SHA512:
6
+ metadata.gz: 1f00dbf92a42d220e5ee6ff92a6f3a39994d4d37904a73ac4c328356a01796273cc1f74afa6621707c00936965d21ed2e490bfc640843f1b77dbb56b577bed84
7
+ data.tar.gz: cc8fbf90c9e3200b14c2f09646fe1ba9229b2451c6e15ca1ef016e508038e7638a4605bfd5c2532851aafe8815c2462af7d3beb9d263c355657cdd330bc89169
@@ -0,0 +1,72 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ DisplayStyleGuide: true
4
+
5
+ Style/StringLiterals:
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: false
10
+
11
+ Metrics/AbcSize:
12
+ Enabled: false
13
+
14
+ Metrics/CyclomaticComplexity:
15
+ Enabled: false
16
+
17
+ Metrics/LineLength:
18
+ Enabled: false
19
+
20
+ Metrics/MethodLength:
21
+ Enabled: false
22
+
23
+ Metrics/PerceivedComplexity:
24
+ Enabled: false
25
+
26
+ Style/Documentation:
27
+ Enabled: false
28
+
29
+ Layout/MultilineOperationIndentation:
30
+ Enabled: false
31
+
32
+ Layout/AlignArray:
33
+ Enabled: false
34
+
35
+ Layout/AlignParameters:
36
+ Enabled: false
37
+
38
+ Style/GuardClause:
39
+ Enabled: false
40
+
41
+ Naming/FileName:
42
+ Enabled: false
43
+
44
+ Layout/DotPosition:
45
+ EnforcedStyle: trailing
46
+
47
+ Layout/IndentationWidth:
48
+ Enabled: false
49
+
50
+ Layout/ElseAlignment:
51
+ Enabled: false
52
+
53
+ Style/RegexpLiteral:
54
+ Enabled: false
55
+
56
+ Style/SignalException:
57
+ EnforcedStyle: semantic
58
+
59
+ Metrics/ModuleLength:
60
+ Enabled: false
61
+
62
+ Metrics/BlockLength:
63
+ Enabled: false
64
+
65
+ Style/FrozenStringLiteralComment:
66
+ Enabled: false
67
+
68
+ Lint/ReturnInVoidContext:
69
+ Enabled: false
70
+
71
+ Layout/EndAlignment:
72
+ EnforcedStyleAlignWith: variable
@@ -0,0 +1,15 @@
1
+ # Contributing
2
+
3
+ ## Reporting issues
4
+
5
+ We would love to help if you are having a problem. Feel free to open an issue with as much detail as possible.
6
+
7
+ ## Contributing code
8
+
9
+ Contributions are encouraged through GitHub Pull Requests.
10
+
11
+ Guidelines when adding new code:
12
+
13
+ * Create tests when possible.
14
+ * Ensure the entire test suite still passes by running `rake`.
15
+ * Ensure code conventions are maintained by running `bundle exec rubocop`.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ moat (0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ diff-lcs (1.3)
11
+ jaro_winkler (1.5.1)
12
+ parallel (1.12.1)
13
+ parser (2.5.1.0)
14
+ ast (~> 2.4.0)
15
+ powerpack (0.1.2)
16
+ rainbow (3.0.0)
17
+ rspec (3.7.0)
18
+ rspec-core (~> 3.7.0)
19
+ rspec-expectations (~> 3.7.0)
20
+ rspec-mocks (~> 3.7.0)
21
+ rspec-core (3.7.1)
22
+ rspec-support (~> 3.7.0)
23
+ rspec-expectations (3.7.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.7.0)
26
+ rspec-mocks (3.7.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.7.0)
29
+ rspec-support (3.7.1)
30
+ rubocop (0.57.2)
31
+ jaro_winkler (~> 1.5.1)
32
+ parallel (~> 1.10)
33
+ parser (>= 2.5)
34
+ powerpack (~> 0.1)
35
+ rainbow (>= 2.2.2, < 4.0)
36
+ ruby-progressbar (~> 1.7)
37
+ unicode-display_width (~> 1.0, >= 1.0.1)
38
+ ruby-progressbar (1.9.0)
39
+ unicode-display_width (1.4.0)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ moat!
46
+ rspec (~> 3.5)
47
+ rubocop (~> 0.57.2)
48
+
49
+ BUNDLED WITH
50
+ 1.16.1
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Poll Everywhere
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,508 @@
1
+ # Moat
2
+
3
+ Moat is an small authorization library built for Ruby (primarily Rails) web applications.
4
+ Moat provides a small number of helpers and specific conventions for writing regular Ruby
5
+ classes to handle authorizations.
6
+
7
+
8
+ ## Installation
9
+
10
+ TODO: Document this once this library is extracted into a gem.
11
+
12
+ ## Policy Classes
13
+
14
+ Moat borrows from [Pundit](https://github.com/varvet/pundit) the concept that all authorization is done through instantiated policy classes. Policy classes are simply classes for plain-ole-ruby-objects (POROs) that follow a specific convention for their interface so that Moat's helper methods can easily apply the proper authorization. Those interface conventions are:
15
+
16
+
17
+ - The name of the policy is generally the name of a model class with the suffix "Policy". For example, `ArticlePolicy` is the class used to describe authorization for `Article` models. Moat's helper methods will automatically look up the policy according to this convention, but you can override this lookup by passing a `policy:` argument with the class to use.
18
+ - A policy will contain two classes within its namespace: `Filter` and `Authorization`.
19
+ - The initializer of both `Filter` and `Authorization` takes a user object as the 1st argument.
20
+ - The 2nd argument to `Filter` is a collection or scope. Usually this will be an ActiveRecord scope, but nothing in Moat requires this.
21
+ - The 2nd argument to `Authorization` is a resource that has already been loaded from the database. Often this will be an ActiveRecord object.
22
+ - The other public methods on both `Filter` and `Authorization` are called "action methods" and the helper methods use them to either apply a scope for a database query (`policy_filter`) or to check authorization for a loaded resource and return true if the user is authorized and false if they are not (`authorize`).
23
+ - Action methods that authorize a loaded resource should have a `?` as the suffix to their name while scope methods are just the name of the action. While you can specify the action method to use for either helper, the convention is to look it up by calling the `action_name` method. (For Rails controllers, that will be the name of the controller action.)
24
+
25
+ Below is a small example of a policy class that implements the interfaces for an update action -- both the scope and the resource methods. Note: while this is sometimes necessary, we recommend using just one of these action methods and preferring the scope-based methods wherever possible.
26
+
27
+ ```ruby
28
+ class ArticlePolicy < ApplicationPolicy
29
+ class Filter < Filter
30
+ def update
31
+ if user.account_admin?
32
+ scope.where(account_id: user.account_id)
33
+ else
34
+ scope.where(user_id: user.id)
35
+ end
36
+ end
37
+ alias_method :edit, :update
38
+ end
39
+
40
+ class Authorization < Authorization
41
+ # Best practice would be to use a Filter for this.
42
+ # This is here to show a more direct comparison to the Filter class.
43
+ def update?
44
+ if user.account_admin?
45
+ resource.account_id == user.account_id
46
+ else
47
+ resource.user_id == user.id
48
+ end
49
+ end
50
+ alias_method :edit?, :update?
51
+
52
+ # This is a more realistic example of an action method
53
+ # in an Authorization class. Since there are no existing
54
+ # objects being acted on, we _can't_ use a database query
55
+ # to scope this action to just records we are permitted to
56
+ # act upon.
57
+ def create?
58
+ # Be careful your controller doesn't override resource.account_id
59
+ # after this authorization check has been performed.
60
+ user.account_admin? && resource.account_id == user.account_id
61
+ end
62
+ end
63
+ end
64
+
65
+ # An ApplicationPolicy class is not necessary, but it can help keep
66
+ # your policies DRY
67
+ class ApplicationPolicy
68
+ class Filter
69
+ def initialize(user, scope)
70
+ @user = user
71
+ @scope = scope
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :user, :scope
77
+
78
+ def account
79
+ @account ||= user&.account
80
+ end
81
+ end
82
+
83
+ class Authorization
84
+ def initialize(user, resource)
85
+ @user = user
86
+ @resource = resource
87
+ end
88
+
89
+ private
90
+
91
+ attr_reader :user, :resource
92
+
93
+ def account
94
+ @account ||= user&.account
95
+ end
96
+ end
97
+ end
98
+ ```
99
+
100
+
101
+ Here are two example controllers — one that uses resource methods and one that uses scope methods. Generally `policy_filter` is preferred over `authorize`. Only use `authorize` when you cannot use a `Filter` to prevent loading objects that the user may not be authorized to access.
102
+
103
+ ```ruby
104
+ class ApplicationController < ActionController::Base
105
+ include Moat
106
+ include MoatVerification
107
+ end
108
+
109
+ class ArticleResourceController < ApplicationController
110
+ before_action :load_article, only: [:edit, :update]
111
+ before_action :load_new_article, only: [:new, :create]
112
+
113
+ def edit
114
+ end
115
+
116
+ def update
117
+ @article.update(article_params)
118
+ redirect_to article_path(@article)
119
+ end
120
+
121
+ def create
122
+ @article.save!
123
+ end
124
+
125
+ private
126
+
127
+ def load_article
128
+ # This is not recommended. It is shown for comparison
129
+ # to the scope based approach.
130
+ # See below about avoiding Direct Object References
131
+ @article = Article.find(params[:id])
132
+ authorize(article)
133
+ end
134
+
135
+ def load_new_article
136
+ # This is a good example of using a authorize because there is
137
+ # no collection to authorize against as it is a new record.
138
+ @article = Article.new(account_id: current_user.account_id)
139
+ authorize(@article)
140
+ end
141
+ end
142
+
143
+ class ArticleScopeController < ApplicationController
144
+ before_action :load_article, only: [:edit, :update]
145
+
146
+ def index
147
+ # policy_filter is always the better option for an index action.
148
+ # The controller should handle filters motivated by:
149
+ # - The user's preferences;
150
+ # - UI concerns; and
151
+ # - Performance concerns.
152
+ # The Policy should only handle filters required by authorization rules.
153
+ @articles = policy_filter(Article.search(params[:search])).limit(10)
154
+ end
155
+
156
+ def edit
157
+ end
158
+
159
+ def update
160
+ @article.update(article_params)
161
+ redirect_to article_path(@article)
162
+ end
163
+
164
+ private
165
+
166
+ def load_article
167
+ # This is the preferred method of loading a record from the database.
168
+ @article = policy_filter(Article).find(params[:id])
169
+ end
170
+ end
171
+ ```
172
+
173
+ ## API
174
+ - `policy_filter(scope, action = action_name, user: moat_user, policy: <optional>)`
175
+ - Called from controller actions or `before_action`s
176
+ - Returns a `scope` with limitations according to `policy`
177
+ - Automagically looks up policy if not given
178
+ - `authorize(resource, action = action_name, user: moat_user, policy: <optional>)`
179
+ - Called from controller actions or `before_action`s
180
+ - Raises `Moat::NotAuthorizedError` if `user` is not permitted to take `action` according to `policy`
181
+ - Automagically looks up policy if not given
182
+ - `moat_user`
183
+ - Returns `current_user` unless overridden
184
+ - `verify_policy_applied`
185
+ - For use as `after_action`
186
+ - Raises `Moat::PolicyNotAppliedError` unless `authorize` or `policy_filter` has been called
187
+ - Using this is highly recommended as a fail safe. However, it is not a replacement for good tests. Sometimes a controller action will need to authorize multiple scopes or resources. This verifies that a policy was applied at least once. It does not verify that a policy was applied to every resource referenced in your controller action.
188
+ - `skip_verify_policy_applied`
189
+ - Called from controller actions
190
+ - Prevents `verify_policy_applied` from raising
191
+ - This removes an important fail-safe.
192
+ - Never use this without making it super clear to future developers why it is safe to call this method.
193
+
194
+ ## Conventions
195
+ - A Moat `policy` is a PORO that is initialized with a user and a scope
196
+ - Moat policies live in `app/policies` and are named after a resource suffixed with `Policy`
197
+ - Example: `AccountPolicy` represents the authorization logic for an `Account` and lives in `app/policies/account_policy.rb`
198
+ - A `scope` is an Enumerable object representing a set of resources
199
+ - In a Rails app, this is almost always an `ActiveRecord::Relation`
200
+ - If you are not using an `ActiveRecord::Relation` you should document your policy very clearly. Properly using the interface between your policies and your controllers is essential for maintaining security.
201
+ - Action methods for `Filter` classes should not end with `?`. If the user is not authorized for anything, then an empty collection/scope should be returned. Otherwise they should return a scope limited to the records the user has access to for the corresponding action.
202
+ - Example: `AccountPolicy#update` should return the scope of all accounts the user has permission to update.
203
+ - Action methods for `Authorization` classes should end with `?`. If the return value is `true` (truthy) then the user is authorized to take the specified action on the resource.
204
+ - Moat policy methods that do not end in `?`
205
+ - Example: `AccountPolicy#update?` should return `true` only if a user is an administrator in the account.
206
+
207
+
208
+
209
+ ## Pundit comparison
210
+
211
+ Moat borrows from [Pundit](https://github.com/varvet/pundit) the concept that all authorization is done through instantiated policy classes that are plain-ole-ruby-objects (POROs) that follow a specific convention for their interface.
212
+
213
+ Unlike [Pundit](https://github.com/varvet/pundit), Moat is focused on scope-based authorization yet easily allows for resource-based authorization within the same policy. This means we are primarily concerned with applying authorization by limiting your database queries to only return rows the specified user has access to.
214
+
215
+
216
+ ### Why scope-based authorization?
217
+
218
+ #### Performance
219
+ If you are working with a collection (index actions, bulk actions, nested attributes, etc.), authorizing one object at a time can easily lead to N+1 performance problems.
220
+ [Pundit](https://github.com/varvet/pundit) does have support for scopes, but is only designed to have a single scope per policy, typically intended for `index` actions. However, listing objects is not the only action that involves a collection.
221
+
222
+ #### DRY
223
+ Using ActiveRecord scopes for authorization also works well.
224
+ Even if you are only loading one object, you can use the scope and just add `find` or `find_by` afterwards.
225
+
226
+ ```ruby
227
+ def show
228
+ @thing = policy_filter(Thing).find_by(id: params[:id])
229
+ end
230
+ ```
231
+
232
+ #### Authorize early
233
+ Using scopes allows authorization to be applied before the sensitive data is even loaded out of the database.
234
+
235
+ This is consistent with the Brakeman recommendation to not use an [Unscoped Find](https://brakemanscanner.org/docs/warning_types/unscoped_find/), also known as [Direct Object Reference](https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References).
236
+
237
+ ```ruby
238
+ def show
239
+ @thing = authorize(Thing.find(params[:id]))
240
+ end
241
+
242
+ def show
243
+ @thing = policy_filter(Thing).find(params[:id])
244
+ end
245
+ ```
246
+
247
+ #### 404 vs 403 vs. 401
248
+ Using scopes can make this a little bit more challenging, but only in a simplistic case.
249
+
250
+ There are really two questions:
251
+
252
+ - Are you authorized to know whether or not this resource exists? If not, 404 is the best response code.
253
+ - Are you authorized to perform this action?
254
+
255
+ ```ruby
256
+ # Without scope.
257
+ # Returns 404 if the object does not exist.
258
+ # Returns 403 if the object exists and you are not authorized to destroy it.
259
+ # Implicitly allows everyone to know whether or not the object exists.
260
+ def destroy
261
+ @thing = authorize(Thing.find(params[:id]))
262
+ @thing.destroy!
263
+ end
264
+
265
+ # With scope
266
+ # Returns 404 if the resource doesn't exist OR if you aren't authorized to destroy it.
267
+ # Implies that if you don't have permission to destroy the object then you also
268
+ # don't have permission to know whether or not the object exists.
269
+ def destroy
270
+ @thing = policy_filter(Thing).find(params[:id])
271
+ @thing.destroy!
272
+ end
273
+
274
+ # Complex/combined scenario
275
+ # Returns 404 if you don't have permission to know whether or not the resource exists.
276
+ # Returns 403 if you can know it exists, but don't have permission to destroy.
277
+ def destroy
278
+ @thing = authorize(policy_filter(Thing, :read).find_by(id: params[:id]))
279
+ @thing.destroy!
280
+ end
281
+ ```
282
+
283
+
284
+ ## Rspec matchers
285
+
286
+ ```ruby
287
+ require "moat/rspec"
288
+
289
+ describe ThingPolicy do
290
+ resource { Thing.create(owner: resource_owner) }
291
+ policy_filters :index, :show, :edit, :update
292
+ policy_authorizations :create?, :view_metadata?
293
+
294
+ let(:superuser) { User.create(superuser: true) }
295
+ let(:anonymous_user) { nil }
296
+ let(:resource_owner) { User.create }
297
+ let(:account_sibling) { User.create(account_id: resource_owner.account_id) }
298
+ let(:non_account_sibling) { User.create }
299
+
300
+ roles :superuser, :resource_owner do
301
+ it { is_expected.to permit_through_all_filters }
302
+ it { is_expected.to permit_all_authorizations }
303
+ end
304
+
305
+ role :account_sibling do
306
+ it { is_expected.to only_permit_through_filters(:index, :show) }
307
+ it { is_expected.to only_permit_authorizations(:create?) }
308
+ end
309
+
310
+ role :non_account_sibling do
311
+ it { is_expected.to deny_through_all_filters }
312
+ it { is_expected.to only_permit_authorizations(:create?) }
313
+ end
314
+
315
+ role :anonymous_user do
316
+ it { is_expected.to deny_through_all_filters }
317
+ it { is_expected.to deny_all_authorizations }
318
+ end
319
+ end
320
+ ```
321
+
322
+ ## Ensure all policies have full test coverage
323
+
324
+ ```ruby
325
+ # spec/support/policy.rb
326
+ module PolicyRSpecHelpers
327
+ def self.included(base_class)
328
+ base_class.class_eval do
329
+ # also a convenient place to define roles to be shared across policy specs
330
+ let(:superuser) { User.create(superuser: true) }
331
+ let(:anonymous_user) { nil }
332
+
333
+ describe "spec/support/policy helper tests" do
334
+ it "tests all defined filters" do
335
+ public_methods = described_class::Filter.instance_methods(false)
336
+ filters = begin
337
+ policy_filters
338
+ rescue NotImplementedError
339
+ []
340
+ end
341
+
342
+ expect(filters).to match_array(public_methods)
343
+ end
344
+
345
+ it "tests all defined authorizations" do
346
+ public_methods = described_class::Authorization.instance_methods(false)
347
+ authorizations = begin
348
+ policy_authorizations
349
+ rescue NotImplementedError
350
+ []
351
+ end
352
+
353
+ expect(authorizations).to match_array(public_methods)
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ RSpec.configure do |config|
361
+ config.include(
362
+ PolicyRSpecHelpers,
363
+ type: :policy,
364
+ file_path: %r{spec/policies}
365
+ )
366
+ end
367
+ ```
368
+
369
+ ## Best Practices
370
+
371
+ 1. The controller should handle filters motivated by:
372
+
373
+ - The user's preferences;
374
+ - UI concerns; and
375
+ - Performance concerns.
376
+
377
+ 1. The Policy should only handle filters required by authorization rules.
378
+
379
+ 1. It is OK if the controller and the Policy duplicate a `where` or `includes`. ActiveRecord and most database engines are good de-duplicate this.
380
+
381
+ 1. The Policy `Filter` methods should add all `includes` and `where` clauses it needs itself. It should not make assumptions about how the argument is already scoped.
382
+
383
+ 1. Be careful about your database indices. The actual SQL that is executed will depend on both the controller and policy code. For example, the following code would require a compound index on both subject_id and user_id.
384
+
385
+ ```ruby
386
+ # Controller
387
+ def index
388
+ @articles = policy_filter(Article.where(subject_id: params[:subject_id]))
389
+ end
390
+
391
+ # Policy
392
+ def index
393
+ scope.where(user_id: user.id)
394
+ end
395
+ ```
396
+
397
+ 1. Use scopes (filters) when possible. But don't be afraid of authorizations when they make the code simpler.
398
+
399
+ 1. Avoid making database queries in action methods in an `Authorize` class. The caller should eager load everything the policies needs to evaluate permissions. This helps to avoid N+1 performance problems if you need to check the permissions of multiple records.
400
+
401
+ 1. Be careful with before_action/after_action/around_action. Rails makes it easy to share these with multiple controller actions. By default Moat implies the policy method from the controller action. When you use Moat via `authorize` or `policy_filter` be sure to test the behavior with every controller action that uses that before_action method.
402
+
403
+ Mistake 1: Failing to define a policy action that is implicitly used.
404
+
405
+ ```ruby
406
+ class ThingsController < ApplicationController
407
+ before_action :load_thing
408
+
409
+ def show
410
+ end
411
+
412
+ def update
413
+ @thing.update!(params.permit(:name))
414
+ end
415
+
416
+ private
417
+
418
+ def load_things
419
+ @thing = policy_filter(Thing).find(params[:id])
420
+ end
421
+ end
422
+
423
+ class ThingPolicy < ApplicationPolicy
424
+ class Filter < Filter
425
+ def show
426
+ scope.where(account_id: user.account_id)
427
+ end
428
+
429
+ # Oops. Forgot to add an `update` policy method.
430
+ end
431
+ end
432
+ ```
433
+
434
+ Mistake 2: Sharing the permission in a shared before_action, thus allowing access that should be denied.
435
+
436
+ ```ruby
437
+ class ThingsController < ApplicationController
438
+ before_action :load_thing
439
+
440
+ def show
441
+ end
442
+
443
+ def update
444
+ @thing.update!(params.permit(:name))
445
+ end
446
+
447
+ private
448
+
449
+ def load_things
450
+ # Oops! `show` permissions are being used for `update` action
451
+ @thing = policy_filter(Thing, :show).find(params[:id])
452
+ end
453
+ end
454
+
455
+ class ThingPolicy < ApplicationPolicy
456
+ class Filter < Filter
457
+ def show
458
+ scope.where(account_id: user.account_id)
459
+ end
460
+
461
+ def update
462
+ scope.where(user_id: user.id)
463
+ end
464
+ end
465
+ end
466
+ ```
467
+
468
+
469
+ 1. Use well-factored, clear names.
470
+
471
+ ```ruby
472
+ # OK because it is a simple case
473
+ class ThingPolicy
474
+ class Filter
475
+ def show
476
+ scope.where(user_id: user.id)
477
+ end
478
+
479
+ def update
480
+ scope.where(account_id: user.account_id)
481
+ end
482
+ end
483
+ end
484
+
485
+ # Better because the filtering logic is labeled.
486
+ class ThingPolicy
487
+ class Filter
488
+ def show
489
+ accounts_things
490
+ end
491
+ def update
492
+ (account_admin? && account_things) || users_things
493
+ end
494
+
495
+ private
496
+
497
+ def users_things
498
+ scope.where(user_id: user.id)
499
+ end
500
+
501
+ def accounts_things
502
+ scope.where(account_id: user.account_id)
503
+ end
504
+ end
505
+ end
506
+ ```
507
+
508
+ 1. Do authorization in controllers. If you are using background jobs, service objects, or presenters, authorize all the user input in the controller before passing responsibility to these other classes. This gives you a consistent place to verify whether or not you have implemented proper authorization.