regulator 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![Regulator](https://upload.wikimedia.org/wikipedia/commons/a/ac/Nat_Powers_%26_Warren_G.jpg)
|
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
|