consul 1.3.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +30 -25
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -1
- data/Gemfile.6-1 +1 -1
- data/Gemfile.6-1.lock +13 -13
- data/{Gemfile.5-2 → Gemfile.7-1} +3 -4
- data/Gemfile.7-1.lock +171 -0
- data/Gemfile.7-2 +17 -0
- data/Gemfile.7-2.lock +171 -0
- data/{Gemfile.7-0 → Gemfile.8-0} +1 -1
- data/Gemfile.8-0.lock +174 -0
- data/Gemfile.lock +1 -1
- data/README.md +69 -100
- data/consul.gemspec +3 -3
- data/lib/consul/active_record.rb +3 -1
- data/lib/consul/util.rb +14 -41
- data/lib/consul/version.rb +1 -1
- data/media/logo.dark.shapes.svg +125 -0
- data/media/logo.dark.text.svg +107 -0
- data/media/logo.light.shapes.svg +89 -0
- data/media/logo.light.text.svg +108 -0
- data/media/makandra-with-bottom-margin.dark.svg +180 -0
- data/media/makandra-with-bottom-margin.light.svg +180 -0
- metadata +21 -16
- data/Gemfile.5-2.lock +0 -128
- data/Gemfile.7-0.lock +0 -133
data/Gemfile.8-0.lock
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
consul (2.0.0)
|
5
|
+
activerecord (>= 6.0)
|
6
|
+
activesupport (>= 6.0)
|
7
|
+
edge_rider (>= 0.3.0)
|
8
|
+
memoized (>= 1.0.2)
|
9
|
+
railties (>= 6.0)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
actionpack (8.0.1)
|
15
|
+
actionview (= 8.0.1)
|
16
|
+
activesupport (= 8.0.1)
|
17
|
+
nokogiri (>= 1.8.5)
|
18
|
+
rack (>= 2.2.4)
|
19
|
+
rack-session (>= 1.0.1)
|
20
|
+
rack-test (>= 0.6.3)
|
21
|
+
rails-dom-testing (~> 2.2)
|
22
|
+
rails-html-sanitizer (~> 1.6)
|
23
|
+
useragent (~> 0.16)
|
24
|
+
actionview (8.0.1)
|
25
|
+
activesupport (= 8.0.1)
|
26
|
+
builder (~> 3.1)
|
27
|
+
erubi (~> 1.11)
|
28
|
+
rails-dom-testing (~> 2.2)
|
29
|
+
rails-html-sanitizer (~> 1.6)
|
30
|
+
activemodel (8.0.1)
|
31
|
+
activesupport (= 8.0.1)
|
32
|
+
activerecord (8.0.1)
|
33
|
+
activemodel (= 8.0.1)
|
34
|
+
activesupport (= 8.0.1)
|
35
|
+
timeout (>= 0.4.0)
|
36
|
+
activesupport (8.0.1)
|
37
|
+
base64
|
38
|
+
benchmark (>= 0.3)
|
39
|
+
bigdecimal
|
40
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
41
|
+
connection_pool (>= 2.2.5)
|
42
|
+
drb
|
43
|
+
i18n (>= 1.6, < 2)
|
44
|
+
logger (>= 1.4.2)
|
45
|
+
minitest (>= 5.1)
|
46
|
+
securerandom (>= 0.3)
|
47
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
48
|
+
uri (>= 0.13.1)
|
49
|
+
assignable_values (1.0.0)
|
50
|
+
activerecord (>= 2.3)
|
51
|
+
base64 (0.2.0)
|
52
|
+
benchmark (0.4.0)
|
53
|
+
bigdecimal (3.1.8)
|
54
|
+
builder (3.3.0)
|
55
|
+
concurrent-ruby (1.3.3)
|
56
|
+
connection_pool (2.4.1)
|
57
|
+
crass (1.0.6)
|
58
|
+
database_cleaner (2.0.2)
|
59
|
+
database_cleaner-active_record (>= 2, < 3)
|
60
|
+
database_cleaner-active_record (2.2.0)
|
61
|
+
activerecord (>= 5.a)
|
62
|
+
database_cleaner-core (~> 2.0.0)
|
63
|
+
database_cleaner-core (2.0.1)
|
64
|
+
date (3.4.1)
|
65
|
+
diff-lcs (1.5.1)
|
66
|
+
drb (2.2.1)
|
67
|
+
edge_rider (2.3.0)
|
68
|
+
activerecord (>= 3.2)
|
69
|
+
erubi (1.13.0)
|
70
|
+
gemika (0.8.3)
|
71
|
+
i18n (1.14.5)
|
72
|
+
concurrent-ruby (~> 1.0)
|
73
|
+
io-console (0.8.0)
|
74
|
+
irb (1.14.3)
|
75
|
+
rdoc (>= 4.0.0)
|
76
|
+
reline (>= 0.4.2)
|
77
|
+
logger (1.6.1)
|
78
|
+
loofah (2.22.0)
|
79
|
+
crass (~> 1.0.2)
|
80
|
+
nokogiri (>= 1.12.0)
|
81
|
+
memoized (1.1.1)
|
82
|
+
mini_portile2 (2.8.8)
|
83
|
+
minitest (5.24.1)
|
84
|
+
nokogiri (1.16.6)
|
85
|
+
mini_portile2 (~> 2.8.2)
|
86
|
+
racc (~> 1.4)
|
87
|
+
psych (5.2.2)
|
88
|
+
date
|
89
|
+
stringio
|
90
|
+
racc (1.8.0)
|
91
|
+
rack (3.1.7)
|
92
|
+
rack-session (2.0.0)
|
93
|
+
rack (>= 3.0.0)
|
94
|
+
rack-test (2.1.0)
|
95
|
+
rack (>= 1.3)
|
96
|
+
rackup (2.2.1)
|
97
|
+
rack (>= 3)
|
98
|
+
rails-dom-testing (2.2.0)
|
99
|
+
activesupport (>= 5.0.0)
|
100
|
+
minitest
|
101
|
+
nokogiri (>= 1.6)
|
102
|
+
rails-html-sanitizer (1.6.0)
|
103
|
+
loofah (~> 2.21)
|
104
|
+
nokogiri (~> 1.14)
|
105
|
+
railties (8.0.1)
|
106
|
+
actionpack (= 8.0.1)
|
107
|
+
activesupport (= 8.0.1)
|
108
|
+
irb (~> 1.13)
|
109
|
+
rackup (>= 1.0.0)
|
110
|
+
rake (>= 12.2)
|
111
|
+
thor (~> 1.0, >= 1.2.2)
|
112
|
+
zeitwerk (~> 2.6)
|
113
|
+
rake (13.2.1)
|
114
|
+
rdoc (6.10.0)
|
115
|
+
psych (>= 4.0.0)
|
116
|
+
reline (0.6.0)
|
117
|
+
io-console (~> 0.5)
|
118
|
+
rspec (3.13.0)
|
119
|
+
rspec-core (~> 3.13.0)
|
120
|
+
rspec-expectations (~> 3.13.0)
|
121
|
+
rspec-mocks (~> 3.13.0)
|
122
|
+
rspec-core (3.13.0)
|
123
|
+
rspec-support (~> 3.13.0)
|
124
|
+
rspec-expectations (3.13.1)
|
125
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
126
|
+
rspec-support (~> 3.13.0)
|
127
|
+
rspec-mocks (3.13.1)
|
128
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
129
|
+
rspec-support (~> 3.13.0)
|
130
|
+
rspec-rails (6.1.3)
|
131
|
+
actionpack (>= 6.1)
|
132
|
+
activesupport (>= 6.1)
|
133
|
+
railties (>= 6.1)
|
134
|
+
rspec-core (~> 3.13)
|
135
|
+
rspec-expectations (~> 3.13)
|
136
|
+
rspec-mocks (~> 3.13)
|
137
|
+
rspec-support (~> 3.13)
|
138
|
+
rspec-support (3.13.1)
|
139
|
+
rspec_candy (0.5.1)
|
140
|
+
rspec
|
141
|
+
sneaky-save
|
142
|
+
securerandom (0.3.2)
|
143
|
+
shoulda-matchers (6.2.0)
|
144
|
+
activesupport (>= 5.2.0)
|
145
|
+
sneaky-save (0.1.3)
|
146
|
+
activerecord (>= 3.2.0)
|
147
|
+
sqlite3 (2.4.1)
|
148
|
+
mini_portile2 (~> 2.8.0)
|
149
|
+
stringio (3.1.2)
|
150
|
+
thor (1.3.2)
|
151
|
+
timeout (0.4.1)
|
152
|
+
tzinfo (2.0.6)
|
153
|
+
concurrent-ruby (~> 1.0)
|
154
|
+
uri (1.0.2)
|
155
|
+
useragent (0.16.10)
|
156
|
+
zeitwerk (2.7.1)
|
157
|
+
|
158
|
+
PLATFORMS
|
159
|
+
ruby
|
160
|
+
|
161
|
+
DEPENDENCIES
|
162
|
+
assignable_values
|
163
|
+
consul!
|
164
|
+
database_cleaner
|
165
|
+
gemika (>= 0.8.1)
|
166
|
+
railties (~> 8.0)
|
167
|
+
rspec
|
168
|
+
rspec-rails
|
169
|
+
rspec_candy
|
170
|
+
shoulda-matchers
|
171
|
+
sqlite3
|
172
|
+
|
173
|
+
BUNDLED WITH
|
174
|
+
2.5.15
|
data/Gemfile.lock
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Gemfile.
|
1
|
+
Gemfile.8-0.lock
|
data/README.md
CHANGED
@@ -1,19 +1,29 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
<p>
|
2
|
+
<a href="https://makandra.de/">
|
3
|
+
<picture>
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="media/makandra-with-bottom-margin.light.svg">
|
5
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/makandra-with-bottom-margin.dark.svg">
|
6
|
+
<img align="right" width="25%" alt="makandra" src="media/makandra-with-bottom-margin.light.svg">
|
7
|
+
</picture>
|
8
|
+
</a>
|
9
|
+
|
10
|
+
<picture>
|
11
|
+
<source media="(prefers-color-scheme: light)" srcset="media/logo.light.shapes.svg">
|
12
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/logo.dark.shapes.svg">
|
13
|
+
<img width="155" alt="consul" role="heading" aria-level="1" src="media/logo.light.shapes.svg">
|
14
|
+
</picture>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
[![Tests](https://github.com/makandra/consul/workflows/Tests/badge.svg)](https://github.com/makandra/consul/actions)
|
18
|
+
|
19
|
+
Consul is an authorization solution for Ruby on Rails where you describe _sets of accessible things_ to control what a user can see or edit.
|
8
20
|
|
9
21
|
We have used Consul in combination with [assignable_values](https://github.com/makandra/assignable_values) to solve a variety of authorization requirements ranging from boring to bizarre.
|
10
22
|
Also see our crash course video: [Solving bizare authorization requirements with Rails](http://bizarre-authorization.talks.makandra.com/).
|
11
23
|
|
12
|
-
Consul is tested with Rails
|
24
|
+
Consul is tested with Rails 6.1, 7.1, 7.2 and 8.0 on Ruby 2.5, 2.7, 3.2, 3.3 (only if supported, for each Ruby/Rails combination). If you need support for Rails 3.2, please use [v0.13.2](https://github.com/makandra/consul/tree/v0.13.2).
|
13
25
|
|
14
|
-
|
15
|
-
Describing access to your application
|
16
|
-
-------------------------------------
|
26
|
+
## Describing access to your application
|
17
27
|
|
18
28
|
You describe access to your application by putting a `Power` model into `app/models/power.rb`.
|
19
29
|
Inside your `Power` you can talk about what is accessible for the current user, e.g.
|
@@ -51,10 +61,8 @@ There are no restrictions on the name or constructor arguments of this class.
|
|
51
61
|
|
52
62
|
You can deposit all kinds of objects in your power. See the sections below for details.
|
53
63
|
|
54
|
-
|
55
64
|
### Scope powers (relations)
|
56
65
|
|
57
|
-
|
58
66
|
A typical use case in a Rails application is to restrict access to your ActiveRecord models. For example:
|
59
67
|
|
60
68
|
- Anonymous visitors may only see public posts
|
@@ -111,13 +119,10 @@ power.note!(Note.last) # => raises Consul::Powerless unless the given Note is in
|
|
111
119
|
|
112
120
|
See our crash course video [Solving bizare authorization requirements with Rails](http://bizarre-authorization.talks.makandra.com/) for many different use cases you can cover with this pattern.
|
113
121
|
|
114
|
-
|
115
|
-
|
116
122
|
### Defining different powers for different actions
|
117
123
|
|
118
124
|
If you have different access rights for e.g. viewing or updating posts, simply use different powers:
|
119
125
|
|
120
|
-
|
121
126
|
```rb
|
122
127
|
class Power
|
123
128
|
...
|
@@ -139,8 +144,6 @@ end
|
|
139
144
|
|
140
145
|
There is also a [shortcut to map different powers to RESTful controller actions](#protect-entry-into-controller-actions).
|
141
146
|
|
142
|
-
|
143
|
-
|
144
147
|
### Boolean powers
|
145
148
|
|
146
149
|
Boolean powers are useful to control access to stuff that doesn't live in the database:
|
@@ -164,7 +167,6 @@ power.dashboard? # => true
|
|
164
167
|
power.dashboard! # => raises Consul::Powerless unless Power#dashboard? returns true
|
165
168
|
```
|
166
169
|
|
167
|
-
|
168
170
|
### Powers that give no access at all
|
169
171
|
|
170
172
|
Note that there is a difference between having access to an empty list of records, and having no access at all.
|
@@ -194,8 +196,6 @@ power.user?(User.last) # => returns false
|
|
194
196
|
power.user!(User.last) # => raises Consul::Powerless
|
195
197
|
```
|
196
198
|
|
197
|
-
|
198
|
-
|
199
199
|
### Powers that only check a given object
|
200
200
|
|
201
201
|
Sometimes it is not convenient to define powers as a collection or scope (relation).
|
@@ -203,7 +203,6 @@ Sometimes you only want to store a method that checks whether a given object is
|
|
203
203
|
|
204
204
|
To do so, simply define a power that ends in a question mark:
|
205
205
|
|
206
|
-
|
207
206
|
```rb
|
208
207
|
class Power
|
209
208
|
...
|
@@ -223,7 +222,6 @@ power.updatable_post?(Post.last) # return true if the author of the post is @use
|
|
223
222
|
power.updatable_post!(Post.last) # raises Consul::Powerless unless the author of the post is @user
|
224
223
|
```
|
225
224
|
|
226
|
-
|
227
225
|
### Other types of powers
|
228
226
|
|
229
227
|
A power can return any type of object. For instance, you often want to return an array:
|
@@ -254,7 +252,6 @@ power.assignable_note_state?('published') # => returns false
|
|
254
252
|
power.assignable_note_state!('published') # => raises Consul::Powerless
|
255
253
|
```
|
256
254
|
|
257
|
-
|
258
255
|
### Defining multiple powers at once
|
259
256
|
|
260
257
|
You can define multiple powers at once by giving multiple power names:
|
@@ -270,7 +267,6 @@ class Power
|
|
270
267
|
end
|
271
268
|
```
|
272
269
|
|
273
|
-
|
274
270
|
### Powers that require context (arguments)
|
275
271
|
|
276
272
|
Sometimes it can be useful to define powers that require context. To do so, just take an argument in your `power` block:
|
@@ -294,7 +290,6 @@ note = ...
|
|
294
290
|
Power.current.client_note?(client, note)
|
295
291
|
```
|
296
292
|
|
297
|
-
|
298
293
|
### Optimizing record checks for scope powers
|
299
294
|
|
300
295
|
You can query a scope power for a given record, e.g.
|
@@ -337,9 +332,7 @@ end
|
|
337
332
|
|
338
333
|
This way you do not need to touch the database at all.
|
339
334
|
|
340
|
-
|
341
|
-
Role-based permissions
|
342
|
-
----------------------
|
335
|
+
## Role-based permissions
|
343
336
|
|
344
337
|
Consul has no built-in support for role-based permissions, but you can easily implement it yourself. Let's say your `User` model has a string column `role` which can be `"author"` or `"admin"`:
|
345
338
|
|
@@ -367,9 +360,7 @@ class Power
|
|
367
360
|
end
|
368
361
|
```
|
369
362
|
|
370
|
-
|
371
|
-
Controller integration
|
372
|
-
----------------------
|
363
|
+
## Controller integration
|
373
364
|
|
374
365
|
It is convenient to expose the power for the current request to the rest of the application. Consul will help you with that if you tell it how to instantiate a power for the current request:
|
375
366
|
|
@@ -398,7 +389,6 @@ class NotesController < ApplicationController
|
|
398
389
|
end
|
399
390
|
```
|
400
391
|
|
401
|
-
|
402
392
|
### Protect entry into controller actions
|
403
393
|
|
404
394
|
To make sure a power is given before every action in a controller:
|
@@ -409,7 +399,7 @@ class NotesController < ApplicationController
|
|
409
399
|
end
|
410
400
|
```
|
411
401
|
|
412
|
-
You can use `:except` and `:only` options like in
|
402
|
+
You can use `:except` and `:only` options like in before_actions.
|
413
403
|
|
414
404
|
You can also map different powers to different actions:
|
415
405
|
|
@@ -441,7 +431,6 @@ class NotesController < ApplicationController
|
|
441
431
|
end
|
442
432
|
```
|
443
433
|
|
444
|
-
|
445
434
|
And if your power [requires context](#powers-that-require-context-arguments) (is parametrized), you can give it using the `:context` method:
|
446
435
|
|
447
436
|
```rb
|
@@ -458,8 +447,6 @@ class ClientNotesController < ApplicationController
|
|
458
447
|
end
|
459
448
|
```
|
460
449
|
|
461
|
-
|
462
|
-
|
463
450
|
### Auto-mapping a power scope to a controller method
|
464
451
|
|
465
452
|
It is often convenient to map a power scope to a private controller method:
|
@@ -486,7 +473,7 @@ class NotesController < ApplicationController
|
|
486
473
|
power :notes, :as => :note_scope
|
487
474
|
|
488
475
|
# ...
|
489
|
-
|
476
|
+
|
490
477
|
def note_scope
|
491
478
|
super.where(trashed: false)
|
492
479
|
end
|
@@ -494,7 +481,6 @@ class NotesController < ApplicationController
|
|
494
481
|
end
|
495
482
|
```
|
496
483
|
|
497
|
-
|
498
484
|
### Multiple power-mappings for nested resources
|
499
485
|
|
500
486
|
When using [nested resources](http://guides.rubyonrails.org/routing.html#nested-resources) you probably want two power
|
@@ -564,7 +550,7 @@ class ApplicationController < ActionController::Base
|
|
564
550
|
end
|
565
551
|
```
|
566
552
|
|
567
|
-
Note that this check is satisfied by
|
553
|
+
Note that this check is satisfied by _any_ `.power` directive in the controller class or its ancestors, even if that `.power` directive has `:only` or `:except` options that do not apply to the current action.
|
568
554
|
|
569
555
|
Should you want to forego the power check (e.g. to remove authorization checks from an entirely public controller):
|
570
556
|
|
@@ -574,9 +560,7 @@ class ApiController < ApplicationController
|
|
574
560
|
end
|
575
561
|
```
|
576
562
|
|
577
|
-
|
578
|
-
Validating assignable values
|
579
|
-
----------------------------
|
563
|
+
## Validating assignable values
|
580
564
|
|
581
565
|
Sometimes a scope is not enough to express what a user can edit. You will often want to give a user write access to a record, but restrict the values she can assign to a given field.
|
582
566
|
|
@@ -647,8 +631,7 @@ The `authorize_values_for` macro comes with many useful options and details best
|
|
647
631
|
assignable_values_for :field, :through => lambda { Power.current }
|
648
632
|
```
|
649
633
|
|
650
|
-
Memoization
|
651
|
-
-----------
|
634
|
+
## Memoization
|
652
635
|
|
653
636
|
All power methods are [memoized](https://www.justinweiss.com/articles/4-simple-memoization-patterns-in-ruby-and-one-gem/) for performance reasons. Multiple calls to the same method will only call your block the first time, and return a cached result afterwards:
|
654
637
|
|
@@ -665,9 +648,7 @@ If you want to discard all cached results, call `#unmemoize_all`:
|
|
665
648
|
power.unmemoize_all
|
666
649
|
```
|
667
650
|
|
668
|
-
|
669
|
-
Dynamic power access
|
670
|
-
--------------------
|
651
|
+
## Dynamic power access
|
671
652
|
|
672
653
|
Consul gives you a way to dynamically access and query powers for a given name, model class or record.
|
673
654
|
A common use case for this are generic helper methods, e.g. a method to display an "edit" link for any given record
|
@@ -687,30 +668,27 @@ end
|
|
687
668
|
|
688
669
|
You can find a full list of available dynamic calls below:
|
689
670
|
|
690
|
-
| Dynamic call
|
691
|
-
|
692
|
-
| `Power.current.send(:notes)`
|
693
|
-
| `Power.current.include_power?(:notes)`
|
694
|
-
| `Power.current.include_power!(:notes)`
|
695
|
-
| `Power.current.include_object?(:notes, Note.last)`
|
696
|
-
| `Power.current.include_object!(:notes, Note.last)`
|
697
|
-
| `Power.current.for_model(Note)`
|
698
|
-
| `Power.current.for_model(:updatable, Note)`
|
699
|
-
| `Power.current.include_model?(Note)`
|
700
|
-
| `Power.current.include_model?(:updatable, Note)`
|
701
|
-
| `Power.current.include_model!(Note)`
|
702
|
-
| `Power.current.include_model!(:updatable, Note)`
|
703
|
-
| `Power.current.include_record?(Note.last)`
|
704
|
-
| `Power.current.include_record?(:updatable, Note.last)`
|
705
|
-
| `Power.current.include_record!(Note.last)`
|
706
|
-
| `Power.current.include_record!(:updatable, Note.last)`
|
707
|
-
| `Power.current.name_for_model(Note)`
|
708
|
-
| `Power.current.name_for_model(:updatable, Note)`
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
Querying a power that might be nil
|
713
|
-
----------------------------------
|
671
|
+
| Dynamic call | Equivalent |
|
672
|
+
| ------------------------------------------------------ | ------------------------------------------ |
|
673
|
+
| `Power.current.send(:notes)` | `Power.current.notes` |
|
674
|
+
| `Power.current.include_power?(:notes)` | `Power.current.notes?` |
|
675
|
+
| `Power.current.include_power!(:notes)` | `Power.current.notes!` |
|
676
|
+
| `Power.current.include_object?(:notes, Note.last)` | `Power.current.note?(Note.last)` |
|
677
|
+
| `Power.current.include_object!(:notes, Note.last)` | `Power.current.note!(Note.last)` |
|
678
|
+
| `Power.current.for_model(Note)` | `Power.current.notes` |
|
679
|
+
| `Power.current.for_model(:updatable, Note)` | `Power.current.updatable_notes` |
|
680
|
+
| `Power.current.include_model?(Note)` | `Power.current.notes?` |
|
681
|
+
| `Power.current.include_model?(:updatable, Note)` | `Power.current.updatable_notes?` |
|
682
|
+
| `Power.current.include_model!(Note)` | `Power.current.notes!` |
|
683
|
+
| `Power.current.include_model!(:updatable, Note)` | `Power.current.updatable_notes!` |
|
684
|
+
| `Power.current.include_record?(Note.last)` | `Power.current.note?(Note.last)` |
|
685
|
+
| `Power.current.include_record?(:updatable, Note.last)` | `Power.current.updatable_note?(Note.last)` |
|
686
|
+
| `Power.current.include_record!(Note.last)` | `Power.current.note!(Note.last)` |
|
687
|
+
| `Power.current.include_record!(:updatable, Note.last)` | `Power.current.updatable_note!(Note.last)` |
|
688
|
+
| `Power.current.name_for_model(Note)` | `:notes` |
|
689
|
+
| `Power.current.name_for_model(:updatable, Note)` | `:updatable_notes` |
|
690
|
+
|
691
|
+
## Querying a power that might be nil
|
714
692
|
|
715
693
|
You will often want to access `Power.current` from another model, to e.g. iterate through the list of accessible users:
|
716
694
|
|
@@ -760,23 +738,20 @@ end
|
|
760
738
|
|
761
739
|
There is a long selection of class methods that behave neutrally in case `Power.current` is `nil`:
|
762
740
|
|
763
|
-
| Call
|
764
|
-
|
765
|
-
| `Power.for_model(Note)`
|
766
|
-
| `Power.for_model(:updatable, Note)`
|
767
|
-
| `Power.include_model?(Note)`
|
768
|
-
| `Power.include_model?(:updatable, Note)`
|
769
|
-
| `Power.include_model!(Note)`
|
770
|
-
| `Power.include_model!(:updatable, Note)`
|
771
|
-
| `Power.include_record?(Note.last)`
|
772
|
-
| `Power.include_record?(:updatable, Note.last)`
|
773
|
-
| `Power.include_record!(Note.last)`
|
774
|
-
| `Power.include_record!(:updatable, Note.last)`
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
Testing
|
779
|
-
-------
|
741
|
+
| Call | Equivalent |
|
742
|
+
| ---------------------------------------------- | ------------------------------------------------------------------- |
|
743
|
+
| `Power.for_model(Note)` | `Power.current.present? ? Power.current.notes : Note` |
|
744
|
+
| `Power.for_model(:updatable, Note)` | `Power.current.present? ? Power.current.updatable_notes : Note` |
|
745
|
+
| `Power.include_model?(Note)` | `Power.current.present? ? Power.notes? : true` |
|
746
|
+
| `Power.include_model?(:updatable, Note)` | `Power.current.present? ? Power.updatable_notes? : true` |
|
747
|
+
| `Power.include_model!(Note)` | `Power.notes! if Power.current.present?` |
|
748
|
+
| `Power.include_model!(:updatable, Note)` | `Power.updatable_notes! if Power.current.present?` |
|
749
|
+
| `Power.include_record?(Note.last)` | `Power.current.present? ? Power.note?(Note.last) : true` |
|
750
|
+
| `Power.include_record?(:updatable, Note.last)` | `Power.current.present? ? Power.updatable_note?(Note.last?) : true` |
|
751
|
+
| `Power.include_record!(Note.last)` | `Power.note!(Note.last) if Power.current.present?` |
|
752
|
+
| `Power.include_record!(:updatable, Note.last)` | `Power.updatable_note!(Note.last) if Power.current.present?` |
|
753
|
+
|
754
|
+
## Testing
|
780
755
|
|
781
756
|
This section Some hints for testing authorization with Consul.
|
782
757
|
|
@@ -847,9 +822,7 @@ Power.without_power do
|
|
847
822
|
end
|
848
823
|
```
|
849
824
|
|
850
|
-
|
851
|
-
Installation
|
852
|
-
------------
|
825
|
+
## Installation
|
853
826
|
|
854
827
|
Add the following to your `Gemfile`:
|
855
828
|
|
@@ -859,15 +832,13 @@ gem 'consul'
|
|
859
832
|
|
860
833
|
Now run `bundle install` to lock the gem into your project.
|
861
834
|
|
835
|
+
## Development
|
862
836
|
|
863
|
-
|
864
|
-
-----------
|
865
|
-
|
866
|
-
We currently develop using Ruby 2.5.3 (see `.ruby-version`) since that version works for current versions of ActiveRecord that we support. GitHub Actions will test additional Ruby versions (2.3.8, 2.4.5, and 3.0.1).
|
837
|
+
We currently develop using Ruby 3.4.1 (see `.ruby-version`) since that version works for current versions of ActiveRecord that we support. GitHub Actions will test additional Ruby versions (2.7.3, 3.2.0).
|
867
838
|
|
868
839
|
There are tests in `spec`. We only accept PRs with tests. To run tests:
|
869
840
|
|
870
|
-
- Install Ruby
|
841
|
+
- Install Ruby 3.4.1
|
871
842
|
- run `bundle install`
|
872
843
|
- Put your database credentials into `spec/support/database.yml`. There's a `database.sample.yml` you can use as a template.
|
873
844
|
- There are gem bundles in the project root for each rails version that we support.
|
@@ -884,8 +855,6 @@ Note that we have configured GitHub Actions to automatically run tests in all su
|
|
884
855
|
|
885
856
|
I'm very eager to keep this gem leightweight and on topic. If you're unsure whether a change would make it into the gem, [talk to me beforehand](mailto:henning.koch@makandra.de).
|
886
857
|
|
887
|
-
|
888
|
-
Credits
|
889
|
-
-------
|
858
|
+
## Credits
|
890
859
|
|
891
860
|
Henning Koch from [makandra](http://makandra.com/)
|
data/consul.gemspec
CHANGED
@@ -26,8 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.require_paths = ["lib"]
|
27
27
|
|
28
28
|
s.add_dependency('memoized', '>=1.0.2')
|
29
|
-
s.add_dependency('activerecord', '>=
|
30
|
-
s.add_dependency('activesupport', '>=
|
31
|
-
s.add_dependency('railties', '>=
|
29
|
+
s.add_dependency('activerecord', '>= 6.0')
|
30
|
+
s.add_dependency('activesupport', '>= 6.0')
|
31
|
+
s.add_dependency('railties', '>= 6.0')
|
32
32
|
s.add_dependency('edge_rider', '>= 0.3.0')
|
33
33
|
end
|
data/lib/consul/active_record.rb
CHANGED
data/lib/consul/util.rb
CHANGED
@@ -3,11 +3,7 @@ module Consul
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def scope_selects_all_records?(scope)
|
6
|
-
|
7
|
-
scope = scope.scoped({})
|
8
|
-
else
|
9
|
-
scope = scope.scoped
|
10
|
-
end
|
6
|
+
scope = scope.scoped
|
11
7
|
scope_sql = scope.to_sql
|
12
8
|
quoted_table_name = Regexp.quote(scope.connection.quote_table_name(scope.table_name))
|
13
9
|
all_sql_pattern = /\ASELECT (#{quoted_table_name}\.)?\* FROM #{quoted_table_name}\z/
|
@@ -23,26 +19,17 @@ module Consul
|
|
23
19
|
end
|
24
20
|
|
25
21
|
def define_scope(klass, name, lambda)
|
26
|
-
|
27
|
-
|
28
|
-
klass.
|
29
|
-
|
30
|
-
klass.send :scope, name, lambda { |*args|
|
31
|
-
options = lambda.call(*args)
|
32
|
-
klass.scoped(options.slice *EdgeRider::Scoped::VALID_FIND_OPTIONS)
|
33
|
-
}
|
34
|
-
end
|
22
|
+
klass.send :scope, name, lambda { |*args|
|
23
|
+
options = lambda.call(*args)
|
24
|
+
klass.scoped(options.slice *EdgeRider::Scoped::VALID_FIND_OPTIONS)
|
25
|
+
}
|
35
26
|
end
|
36
27
|
|
37
28
|
# This method does not support dynamic default scopes via lambdas
|
38
29
|
# (as does #define_scope), because it is currently not required.
|
39
30
|
def define_default_scope(klass, conditions)
|
40
|
-
|
41
|
-
klass.
|
42
|
-
else
|
43
|
-
klass.send :default_scope do
|
44
|
-
klass.scoped(conditions)
|
45
|
-
end
|
31
|
+
klass.send :default_scope do
|
32
|
+
klass.scoped(conditions)
|
46
33
|
end
|
47
34
|
end
|
48
35
|
|
@@ -58,33 +45,19 @@ module Consul
|
|
58
45
|
end
|
59
46
|
|
60
47
|
def skip_before_action(controller_class, name, options)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
# Every `power` in a controller will skip the power check filter. After the 1st time, Rails 5+ will raise
|
67
|
-
# an error because there is no `unchecked_power` action to skip any more.
|
68
|
-
# To avoid this, we add the following extra option. Note that it must not be added in Rails 4 to avoid errors.
|
69
|
-
# See http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-skip_callback
|
70
|
-
controller_class.skip_before_action name, { :raise => false }.merge!(options)
|
71
|
-
end
|
48
|
+
# Every `power` in a controller will skip the power check filter. After the 1st time, Rails will raise
|
49
|
+
# an error because there is no `unchecked_power` action to skip any more.
|
50
|
+
# To avoid this, we add the following extra option.
|
51
|
+
# See http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-skip_callback
|
52
|
+
controller_class.skip_before_action name, { :raise => false }.merge!(options)
|
72
53
|
end
|
73
54
|
|
74
55
|
def before_action(controller_class, *args, &block)
|
75
|
-
|
76
|
-
controller_class.before_filter *args, &block
|
77
|
-
else
|
78
|
-
controller_class.before_action *args, &block
|
79
|
-
end
|
56
|
+
controller_class.before_action *args, &block
|
80
57
|
end
|
81
58
|
|
82
59
|
def around_action(controller_class, *args, &block)
|
83
|
-
|
84
|
-
controller_class.around_filter *args, &block
|
85
|
-
else
|
86
|
-
controller_class.around_action *args, &block
|
87
|
-
end
|
60
|
+
controller_class.around_action *args, &block
|
88
61
|
end
|
89
62
|
|
90
63
|
end
|
data/lib/consul/version.rb
CHANGED