regulator 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +194 -19
- data/lib/regulator/active_admin_adapter.rb +34 -0
- data/lib/regulator/policy_finder.rb +22 -2
- data/lib/regulator/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10e47763e8d217437980f1f09a6a7c4ab32ba1a2
|
4
|
+
data.tar.gz: f7326022562fbef9b9d0e386592fce817715316c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62e40309efbf5f2a5f31abd664e1f7c05ace0cfbfb9ed690e9eb419538c8d3ab92095499654b5d09aa40adae82c8a2f500ed5ef565e6eb850a88c379a7fd5744
|
7
|
+
data.tar.gz: 9a0ee8202586ced8b054792cd66e11f94fa315dc981ef9708b833c94073d97ad3b00c97a2eec56819d60a3666cd10d5a66d043e4e488d54702cffaa5553f645c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Regulator
|
2
2
|
|
3
|
+
## 0.1.3 (2015-07-29)
|
4
|
+
- made Regulator::PolicyFinder.new a bit more nimble
|
5
|
+
- included a simple activeadmin adapter that can be 'required' instead of generated
|
6
|
+
|
3
7
|
## 0.1.2 (2015-07-23)
|
4
8
|
- Add generators for install, policy, and activeadmin adapter
|
5
9
|
|
data/README.md
CHANGED
@@ -13,18 +13,6 @@ I built this because I believe authorization should be controller-based, not mod
|
|
13
13
|
|
14
14
|
Why not contribute to pundit? [It's](https://github.com/elabs/pundit/issues/12) [been](https://github.com/elabs/pundit/issues/178) an [on going](https://github.com/elabs/pundit/search?q=namespace&type=Issues&utf8=%E2%9C%93) 'issue' in pundit and it doesn't look [like it'll be reality.](https://github.com/elabs/pundit/pull/190#issuecomment-53052356)
|
15
15
|
|
16
|
-
## TODOs
|
17
|
-
* [ ] documentation
|
18
|
-
* [ ] Usage section below, mock pundit's
|
19
|
-
* [ ] yard doc
|
20
|
-
* [ ] Lotus examples
|
21
|
-
* [ ] Grape examples
|
22
|
-
* [ ] ROM examples
|
23
|
-
* [ ] Custom permissions examples
|
24
|
-
* [ ] RoleModel gem examples
|
25
|
-
* [ ] rolify gem examples
|
26
|
-
* [ ] contributing wiki
|
27
|
-
|
28
16
|
## Installation
|
29
17
|
|
30
18
|
Add this line to your application's Gemfile:
|
@@ -32,7 +20,6 @@ Add this line to your application's Gemfile:
|
|
32
20
|
```ruby
|
33
21
|
gem 'regulator'
|
34
22
|
```
|
35
|
-
|
36
23
|
And then execute:
|
37
24
|
|
38
25
|
$ bundle
|
@@ -41,24 +28,29 @@ Or install it yourself as:
|
|
41
28
|
|
42
29
|
$ gem install regulator
|
43
30
|
|
44
|
-
|
31
|
+
Include Regulator in your application controller:
|
45
32
|
|
46
|
-
|
33
|
+
``` ruby
|
34
|
+
class ApplicationController < ActionController::Base
|
35
|
+
include Regulator
|
36
|
+
protect_from_forgery
|
37
|
+
end
|
38
|
+
```
|
47
39
|
|
48
|
-
|
40
|
+
## Generators
|
49
41
|
|
50
42
|
Install regulator
|
51
|
-
```
|
43
|
+
``` sh
|
52
44
|
rails g regulator:install
|
53
45
|
```
|
54
46
|
|
55
47
|
Create a new policy and policy test/spec
|
56
|
-
```
|
48
|
+
``` sh
|
57
49
|
rails g regulator:policy User
|
58
50
|
```
|
59
51
|
|
60
52
|
Regulator comes with a generator for creating an ActiveAdmin adapter
|
61
|
-
```
|
53
|
+
``` sh
|
62
54
|
rails g regulator:activeadmin
|
63
55
|
```
|
64
56
|
|
@@ -84,6 +76,177 @@ config.regulator_policy_namespace = nil
|
|
84
76
|
config.regulator_default_policy = nil
|
85
77
|
```
|
86
78
|
|
79
|
+
## Policies
|
80
|
+
|
81
|
+
Regulator is focused around the notion of policy classes. We suggest that you put
|
82
|
+
these classes in `app/policies`. This is a simple example that allows updating
|
83
|
+
a post if the user is an admin, or if the post is unpublished:
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
class PostPolicy
|
87
|
+
attr_reader :user, :post
|
88
|
+
|
89
|
+
def initialize(user, post)
|
90
|
+
@user = user
|
91
|
+
@post = post
|
92
|
+
end
|
93
|
+
|
94
|
+
def update?
|
95
|
+
user.admin? or not post.published?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
Regulator makes the following assumptions about this class:
|
101
|
+
|
102
|
+
- The class has the name `Scope` and is nested under the policy class.
|
103
|
+
- The first argument is a user. In your controller, Regulator will call the
|
104
|
+
`current_user` method to retrieve what to send into this argument.
|
105
|
+
- The second argument is a scope of some kind on which to perform some kind of
|
106
|
+
query. It will usually be an ActiveRecord class or a
|
107
|
+
`ActiveRecord::Relation`, but it could be something else entirely.
|
108
|
+
- Instances of this class respond to the method `resolve`, which should return
|
109
|
+
some kind of result which can be iterated over. For ActiveRecord classes,
|
110
|
+
this would usually be an `ActiveRecord::Relation`.
|
111
|
+
|
112
|
+
You'll probably want to inherit from the application policy scope generated by the
|
113
|
+
generator, or create your own base class to inherit from:
|
114
|
+
|
115
|
+
``` ruby
|
116
|
+
class PostPolicy < ApplicationPolicy
|
117
|
+
class Scope < Scope
|
118
|
+
def resolve
|
119
|
+
if user.admin?
|
120
|
+
scope.all
|
121
|
+
else
|
122
|
+
scope.where(:published => true)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def update?
|
128
|
+
user.admin? or not post.published?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
You can now use this class from your controller via the `policy_scope` method:
|
134
|
+
|
135
|
+
``` ruby
|
136
|
+
def index
|
137
|
+
@posts = policy_scope(Post)
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
Just as with your policy, this will automatically infer that you want to use
|
142
|
+
the `PostPolicy::Scope` class, it will instantiate this class and call
|
143
|
+
`resolve` on the instance. In this case it is a shortcut for doing:
|
144
|
+
|
145
|
+
``` ruby
|
146
|
+
def index
|
147
|
+
@posts = PostPolicy::Scope.new(current_user, Post).resolve
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
You can, and are encouraged to, use this method in views:
|
152
|
+
|
153
|
+
``` erb
|
154
|
+
<% policy_scope(@user.posts).each do |post| %>
|
155
|
+
<p><%= link_to post.title, post_path(post) %></p>
|
156
|
+
<% end %>
|
157
|
+
```
|
158
|
+
|
159
|
+
## Manually specifying policy classes
|
160
|
+
|
161
|
+
Sometimes you might want to explicitly declare which policy to use for a given
|
162
|
+
class, instead of letting Regulator infer it. This can be done like so:
|
163
|
+
|
164
|
+
Regulator supports the Pundit-style model "policy_class", but also implements it
|
165
|
+
at the controller level. You can also set a controller's policy_namespace if you want to use an alternate namespace to the one the controller is in.
|
166
|
+
|
167
|
+
|
168
|
+
``` ruby
|
169
|
+
# Model level
|
170
|
+
class Post
|
171
|
+
def self.policy_class
|
172
|
+
PostablePolicy
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
``` ruby
|
178
|
+
# Controller level
|
179
|
+
class Api::Post
|
180
|
+
# By default, Regulator will look for Api::PostPolicy
|
181
|
+
def self.policy_class
|
182
|
+
PostPolicy
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
``` ruby
|
188
|
+
# Here the admin namespace could be told to use the same policy as the API namespace
|
189
|
+
class Admin::Post
|
190
|
+
# By default, Regulator will look for Admin::PostPolicy
|
191
|
+
def self.policy_class
|
192
|
+
PostPolicy
|
193
|
+
end
|
194
|
+
|
195
|
+
# You can also set it at the instance level
|
196
|
+
def policy_class
|
197
|
+
if current_user.is_a_high_paying_member?
|
198
|
+
HighClassPostPolicy
|
199
|
+
else
|
200
|
+
LowClassPostPolicy
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
``` ruby
|
207
|
+
class Admin::Comment
|
208
|
+
def self.policy_namespace
|
209
|
+
# Will make regulator look for ActiveAdmin::CommentPolicy instead of
|
210
|
+
# Admin::CommentPolicy
|
211
|
+
ActiveAdmin
|
212
|
+
end
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
Of course ```policy_namespace``` and ```policy_class``` can be used together.
|
217
|
+
|
218
|
+
## Policy Namespaces
|
219
|
+
|
220
|
+
This table explains what policies Regulator will look for in different scenarios:
|
221
|
+
|
222
|
+
| Controller Name | Model Name | Expected Policy |
|
223
|
+
| -------------------------------------------------------|------------------| -------------------------------|
|
224
|
+
| AlbumController | Album | AlbumPolicy |
|
225
|
+
| Api::AlbumController | Album | Api::AlbumPolicy |
|
226
|
+
| Admin::AlbumController | Album | Admin::AlbumPolicy |
|
227
|
+
| Admin::AlbumController.policy_namespace = 'SuperUser' | Album | SuperUser::AlbumPolicy |
|
228
|
+
| Admin::AlbumController.policy_namespace = nil | Album | AlbumPolicy |
|
229
|
+
| Admin::AlbumContoller | MySongGem::Album | Admin::MySongGem::AlbumPolicy |
|
230
|
+
| SongController#policy_class = TrackPolicy | Song | TrackPolicy |
|
231
|
+
| SongController.policy_class = Legacy::TrackPolicy | Song | Legacy::TrackPolicy |
|
232
|
+
|
233
|
+
```policy_class``` at the controller-level is king. Setting it will override all logic for determining the policy to use.
|
234
|
+
|
235
|
+
## ActiveAdmin Auth Adapter
|
236
|
+
|
237
|
+
There is a generator and an included adapter. Using the generator will place a more complex customizable adapter in your ```lib``` directory.
|
238
|
+
|
239
|
+
A simple adapter is also provided, to use add the following to your active_admin initializer:
|
240
|
+
``` ruby
|
241
|
+
ActiveAdmin::Dependency.regulator!
|
242
|
+
|
243
|
+
require 'regulator'
|
244
|
+
require 'regulator/active_admin_adapter'
|
245
|
+
ActiveAdmin.setup do |config|
|
246
|
+
config.authorization_adapter = "Regulator::ActiveAdminAdapter"
|
247
|
+
...
|
248
|
+
```
|
249
|
+
|
87
250
|
## Development
|
88
251
|
|
89
252
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -96,7 +259,19 @@ The gem is available as open source under the terms of the [MIT License](http://
|
|
96
259
|
|
97
260
|
## Contributors
|
98
261
|
* [Cory O'Daniel](http://linkedin.com/in/coryodaniel)
|
262
|
+
* All the hard work done on [Pundit](https://github.com/elabs/pundit)
|
99
263
|
|
100
264
|
Thanks to Warren G for the inspiration, bro.
|
101
265
|
|
102
266
|

|
267
|
+
|
268
|
+
## TODOs
|
269
|
+
* [ ] documentation
|
270
|
+
* [ ] yard doc
|
271
|
+
* [ ] Lotus examples
|
272
|
+
* [ ] Grape examples
|
273
|
+
* [ ] ROM examples
|
274
|
+
* [ ] Custom permissions examples
|
275
|
+
* [ ] RoleModel gem examples
|
276
|
+
* [ ] rolify gem examples
|
277
|
+
* [ ] contributing wiki
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Regulator
|
2
|
+
class ActiveAdminAdapter < ActiveAdmin::AuthorizationAdapter
|
3
|
+
def authorized?(action, subject = nil)
|
4
|
+
policy = retrieve_policy(subject)
|
5
|
+
action = format_action(action, subject)
|
6
|
+
policy.respond_to?(action) && policy.public_send(action)
|
7
|
+
end
|
8
|
+
|
9
|
+
def scope_collection(collection, action = Auth::READ)
|
10
|
+
# scoping is appliable only to read/index action
|
11
|
+
# which means there is no way how to scope other actions
|
12
|
+
Regulator.policy_scope!(user, collection, self.resource.controller)
|
13
|
+
end
|
14
|
+
|
15
|
+
def retrieve_policy(subject)
|
16
|
+
case subject
|
17
|
+
when nil then Regulator.policy!(user, resource, self.resource.controller)
|
18
|
+
when Class then Regulator.policy!(user, subject.new, self.resource.controller)
|
19
|
+
else Regulator.policy!(user, subject, self.resource.controller)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_action(action, subject)
|
24
|
+
# https://github.com/elabs/regulator/blob/master/lib/generators/regulator/install/templates/application_policy.rb
|
25
|
+
case action
|
26
|
+
when ActiveAdmin::Auth::CREATE then :create?
|
27
|
+
when ActiveAdmin::Auth::UPDATE then :update?
|
28
|
+
when ActiveAdmin::Auth::READ then subject.is_a?(Class) ? :index? : :show?
|
29
|
+
when ActiveAdmin::Auth::DESTROY then subject.is_a?(Class) ? :destroy_all? : :destroy?
|
30
|
+
else "#{action}?"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -7,11 +7,31 @@ module Regulator
|
|
7
7
|
def initialize(object, controller_or_namespace = nil)
|
8
8
|
@object = object
|
9
9
|
|
10
|
-
if controller_or_namespace.
|
10
|
+
if controller_or_namespace.instance_of? Module
|
11
|
+
# Its a Module
|
11
12
|
@namespace = controller_or_namespace
|
13
|
+
elsif controller_or_namespace.instance_of? Class
|
14
|
+
# Controller Class
|
15
|
+
@controller = controller_or_namespace
|
16
|
+
|
17
|
+
# ActiveAdmin uses the Regulator API directly, it doesnt mix it into the controllers
|
18
|
+
@namespace = if @controller.respond_to?(:policy_namespace)
|
19
|
+
# if the controller explicitly sets the policy_namespace to we want to keep it nil
|
20
|
+
@controller.try(:policy_namespace)
|
21
|
+
else
|
22
|
+
@controller.parent
|
23
|
+
end
|
12
24
|
elsif controller_or_namespace
|
25
|
+
# Controller Instance
|
13
26
|
@controller = controller_or_namespace
|
14
|
-
|
27
|
+
|
28
|
+
# ActiveAdmin uses the Regulator API directly, it doesnt mix it into the controllers
|
29
|
+
@namespace = if @controller.respond_to?(:policy_namespace)
|
30
|
+
# if the controller explicitly sets the policy_namespace to we want to keep it nil
|
31
|
+
@controller.try(:policy_namespace)
|
32
|
+
else
|
33
|
+
@controller.class.parent
|
34
|
+
end
|
15
35
|
end
|
16
36
|
end
|
17
37
|
|
data/lib/regulator/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: regulator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cory O'Daniel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- lib/generators/test_unit/policy_generator.rb
|
160
160
|
- lib/generators/test_unit/templates/policy_test.rb
|
161
161
|
- lib/regulator.rb
|
162
|
+
- lib/regulator/active_admin_adapter.rb
|
162
163
|
- lib/regulator/policy_finder.rb
|
163
164
|
- lib/regulator/rspec.rb
|
164
165
|
- lib/regulator/version.rb
|