acts_as_permission 2.0.0 → 2.0.1
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.
- data/README.md +320 -0
- data/VERSION.yml +1 -1
- data/acts_as_permission.gemspec +2 -2
- data/lib/acts_as_permission.rb +1 -4
- data/test/permission_test.rb +19 -9
- metadata +38 -32
- data/README.rdoc +0 -296
data/README.md
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
Acts as permission
|
2
|
+
==================
|
3
|
+
|
4
|
+
Acts as permission is a plugin for Ruby on Rails that allows to assign a list of
|
5
|
+
permissions on an object, according to the ACL concept, where each permission
|
6
|
+
can be extended to a subject.
|
7
|
+
|
8
|
+
More specifically, it can make possible to allow or to deny any action of the
|
9
|
+
controller of a protected resource. These actions are called permittables.
|
10
|
+
|
11
|
+
A permittable action can be directly attached to a resource. Examples of such
|
12
|
+
actions:
|
13
|
+
|
14
|
+
* `show`,
|
15
|
+
* `edit`,
|
16
|
+
* `update`,
|
17
|
+
* `destroy`.
|
18
|
+
|
19
|
+
Or it can be indirectly, through a parent resource. Examples:
|
20
|
+
|
21
|
+
* `index`,
|
22
|
+
* `new`,
|
23
|
+
* `create`.
|
24
|
+
|
25
|
+
Here is an example of query to a direct article's action:
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
@article = Article.find(params[:id])
|
29
|
+
@article.permission?("articles#destroy") # => false
|
30
|
+
```
|
31
|
+
|
32
|
+
Same query, extended to a user:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
@article.permission?("articles#destroy", @bob) # => nil
|
36
|
+
@article.permission?("articles#destroy", @admin) # => true
|
37
|
+
```
|
38
|
+
|
39
|
+
A query example on an indirect articles' action, through a category:
|
40
|
+
|
41
|
+
``` ruby
|
42
|
+
@category = Category.find(params[:category_id])
|
43
|
+
@category.permission?("articles#index") # => true
|
44
|
+
```
|
45
|
+
|
46
|
+
Other examples, on unpermittable actions:
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
@category.permission?("articles#read") # => nil
|
50
|
+
@category.permission?("silk_routes#index") # => nil
|
51
|
+
```
|
52
|
+
|
53
|
+
The value of a permission depends on its context, which includes a route and an
|
54
|
+
optional extension to a permitted resource.
|
55
|
+
|
56
|
+
The `permission?(route, ext = nil)` query may return, depending on the context:
|
57
|
+
|
58
|
+
* `true`, if the permission is allowed;
|
59
|
+
* `false`, if the permission is denied;
|
60
|
+
* `nil`, if the permission is indefinable (resulting of the unknown context).
|
61
|
+
|
62
|
+
Philosophy
|
63
|
+
----------
|
64
|
+
|
65
|
+
General library that does only one thing, without any feature.
|
66
|
+
|
67
|
+
Installation
|
68
|
+
------------
|
69
|
+
|
70
|
+
Include the gem in your `Gemfile`:
|
71
|
+
|
72
|
+
gem 'acts_as_permission'
|
73
|
+
|
74
|
+
And run the `bundle` command. Or as a plugin:
|
75
|
+
|
76
|
+
rails plugin install git://github.com/cyril/acts_as_permission.git
|
77
|
+
|
78
|
+
Then, generate files and apply the migration:
|
79
|
+
|
80
|
+
rails generate permissions
|
81
|
+
rake db:migrate
|
82
|
+
|
83
|
+
Getting started
|
84
|
+
---------------
|
85
|
+
|
86
|
+
### Configuring models
|
87
|
+
|
88
|
+
Permittable models have to be declared with `acts_as_permission`. And they have
|
89
|
+
to be so with a default permission mask. For example:
|
90
|
+
|
91
|
+
``` ruby
|
92
|
+
# app/models/article.rb
|
93
|
+
class Article < ActiveRecord::Base
|
94
|
+
acts_as_permission({
|
95
|
+
'articles#show' => [true, {}],
|
96
|
+
'articles#edit' => [false, {
|
97
|
+
permitted_id: 1,
|
98
|
+
permitted_type: "User",
|
99
|
+
value: true }],
|
100
|
+
'articles#update' => [false, {
|
101
|
+
permitted_id: 1,
|
102
|
+
permitted_type: "User",
|
103
|
+
value: true }],
|
104
|
+
'articles#destroy' => [false, {
|
105
|
+
permitted_id: 1,
|
106
|
+
permitted_type: "User",
|
107
|
+
value: true }],
|
108
|
+
'comments#index' => true,
|
109
|
+
'comments#new' => [true, [{
|
110
|
+
permitted_id: 3,
|
111
|
+
permitted_type: "User",
|
112
|
+
value: false }]],
|
113
|
+
'comments#create' => [true, [{
|
114
|
+
permitted_id: 3,
|
115
|
+
permitted_type: "User",
|
116
|
+
value: false }]]})
|
117
|
+
|
118
|
+
belongs_to :user
|
119
|
+
has_many :comments, dependent: :destroy
|
120
|
+
end
|
121
|
+
|
122
|
+
# app/models/comment.rb
|
123
|
+
class Comment < ActiveRecord::Base
|
124
|
+
acts_as_permission([
|
125
|
+
["comments#show", true],
|
126
|
+
["comments#edit", [false, [
|
127
|
+
{permitted_id: 1, permitted_type: "User", value: true},
|
128
|
+
{permitted_id: 2, permitted_type: "User", value: true} ]]],
|
129
|
+
["comments#update", [false, [
|
130
|
+
{permitted_id: 1, permitted_type: "User", value: true},
|
131
|
+
{permitted_id: 2, permitted_type: "User", value: true} ]]],
|
132
|
+
["comments#destroy", [false, {
|
133
|
+
permitted_id: 1,
|
134
|
+
permitted_type: "User",
|
135
|
+
value: true }]]])
|
136
|
+
|
137
|
+
belongs_to :article
|
138
|
+
belongs_to :user
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Optionally, some models (such as `User`, `Group`, `Role`) can also be declared
|
143
|
+
as permitted with `is_able_to_be_permitted`. Example:
|
144
|
+
|
145
|
+
``` ruby
|
146
|
+
# app/models/user.rb
|
147
|
+
class User < ActiveRecord::Base
|
148
|
+
is_able_to_be_permitted
|
149
|
+
|
150
|
+
with_options(dependent: :destroy) do |opts|
|
151
|
+
opts.has_many :articles
|
152
|
+
opts.has_many :comments
|
153
|
+
end
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
### Configuring controllers
|
158
|
+
|
159
|
+
Example of a fully protected comments controller:
|
160
|
+
|
161
|
+
``` ruby
|
162
|
+
class CommentsController < ApplicationController
|
163
|
+
before_filter :check_permissions
|
164
|
+
|
165
|
+
# GET /comments
|
166
|
+
# GET /comments.xml
|
167
|
+
def index
|
168
|
+
@comments = current_resource.comments
|
169
|
+
|
170
|
+
respond_to do |format|
|
171
|
+
format.html # index.html.erb
|
172
|
+
format.xml { render :xml => @comments }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# GET /comments/1
|
177
|
+
# GET /comments/1.xml
|
178
|
+
def show
|
179
|
+
@comment = current_resource
|
180
|
+
|
181
|
+
respond_to do |format|
|
182
|
+
format.html # show.html.erb
|
183
|
+
format.xml { render :xml => @comment }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# GET /comments/new
|
188
|
+
# GET /comments/new.xml
|
189
|
+
def new
|
190
|
+
@comment = current_resource.comments.build
|
191
|
+
|
192
|
+
respond_to do |format|
|
193
|
+
format.html # new.html.erb
|
194
|
+
format.xml { render :xml => @comment }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# GET /comments/1/edit
|
199
|
+
def edit
|
200
|
+
@comment = current_resource
|
201
|
+
end
|
202
|
+
|
203
|
+
# POST /comments
|
204
|
+
# POST /comments.xml
|
205
|
+
def create
|
206
|
+
@comment = current_resource.comments.build(params[:comment])
|
207
|
+
|
208
|
+
respond_to do |format|
|
209
|
+
if @comment.save
|
210
|
+
format.html { redirect_to(@comment,
|
211
|
+
:notice => 'Comment was successfully created.') }
|
212
|
+
format.xml { render :xml => @comment, :status => :created,
|
213
|
+
:location => @comment }
|
214
|
+
else
|
215
|
+
format.html { render :action => "new" }
|
216
|
+
format.xml { render :xml => @comment.errors,
|
217
|
+
:status => :unprocessable_entity }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# PUT /comments/1
|
223
|
+
# PUT /comments/1.xml
|
224
|
+
def update
|
225
|
+
@comment = current_resource
|
226
|
+
|
227
|
+
respond_to do |format|
|
228
|
+
if @comment.update_attributes(params[:comment])
|
229
|
+
format.html { redirect_to(@comment,
|
230
|
+
:notice => 'Comment was successfully updated.') }
|
231
|
+
format.xml { head :ok }
|
232
|
+
else
|
233
|
+
format.html { render :action => "edit" }
|
234
|
+
format.xml { render :xml => @comment.errors,
|
235
|
+
:status => :unprocessable_entity }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# DELETE /comments/1
|
241
|
+
# DELETE /comments/1.xml
|
242
|
+
def destroy
|
243
|
+
@comment = current_resource
|
244
|
+
@comment.destroy
|
245
|
+
|
246
|
+
respond_to do |format|
|
247
|
+
format.html { redirect_to(comments_url) }
|
248
|
+
format.xml { head :ok }
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
protected
|
253
|
+
|
254
|
+
def check_permissions
|
255
|
+
route = [ params[:controller],
|
256
|
+
params[:action] ].join('#')
|
257
|
+
|
258
|
+
unless (current_user &&
|
259
|
+
current_resource.permission?(route, current_user)) ||
|
260
|
+
current_resource.permission?(route)
|
261
|
+
respond_to do |format|
|
262
|
+
format.html { redirect_to(:back, :warning => '403 Forbidden',
|
263
|
+
:status => :forbidden) }
|
264
|
+
format.xml { render :xml => '403 Forbidden', :status => :forbidden }
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def current_resource
|
270
|
+
@current_resource ||= if params[:id]
|
271
|
+
Comment.find(params[:id])
|
272
|
+
else
|
273
|
+
Article.find(params[:article_id], :readonly => true)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
### Configuring views
|
280
|
+
|
281
|
+
We can now perform some checks on related views from a comment instance, thanks
|
282
|
+
to the protected actions of its controller, in order to only display allowed
|
283
|
+
links:
|
284
|
+
|
285
|
+
``` ruby
|
286
|
+
if current_user && @comment.permission?("comments#edit", current_user) ||
|
287
|
+
@comment.permission?("comments#edit")
|
288
|
+
link_to "Edit comment", edit_article_comment_path(@comment.article, @comment)
|
289
|
+
end
|
290
|
+
```
|
291
|
+
|
292
|
+
And also some indirect checks from the current article instance, like this one:
|
293
|
+
|
294
|
+
``` ruby
|
295
|
+
if current_user && @article.permission?("comments#index", current_user) ||
|
296
|
+
@article.permission?("comments#index")
|
297
|
+
link_to "Comments", article_comments_path(@article)
|
298
|
+
end
|
299
|
+
```
|
300
|
+
|
301
|
+
Or this other one:
|
302
|
+
|
303
|
+
``` ruby
|
304
|
+
if current_user && @article.permission?("comments#new", current_user) ||
|
305
|
+
@article.permission?("comments#new")
|
306
|
+
link_to "New comment", new_article_comment_path(@article)
|
307
|
+
end
|
308
|
+
```
|
309
|
+
|
310
|
+
#### Form helper
|
311
|
+
|
312
|
+
Object's permissions management is as simple as:
|
313
|
+
|
314
|
+
``` ruby
|
315
|
+
form_for @article do |f|
|
316
|
+
permission_fields f
|
317
|
+
end
|
318
|
+
```
|
319
|
+
|
320
|
+
Copyright (c) 2009-2011 Cyril Wack, released under the MIT license
|
data/VERSION.yml
CHANGED
data/acts_as_permission.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "acts_as_permission"
|
3
|
-
s.version =
|
3
|
+
s.version = YAML.load_file("VERSION.yml").values.join('.')
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.authors = ["Cyril Wack"]
|
6
|
-
s.email = ["cyril
|
6
|
+
s.email = ["contact@cyril.io"]
|
7
7
|
s.homepage = "http://github.com/cyril/acts_as_permission"
|
8
8
|
s.summary = %q{Simple permission solution for Rails.}
|
9
9
|
s.description = %q{Simple Rails plugin to assign a list of permissions on a resource.}
|
data/lib/acts_as_permission.rb
CHANGED
@@ -44,10 +44,6 @@ module ActsAsPermission
|
|
44
44
|
permissions.compact
|
45
45
|
end
|
46
46
|
|
47
|
-
def mass_assignment_authorizer
|
48
|
-
super + [:permissions_attributes]
|
49
|
-
end
|
50
|
-
|
51
47
|
def has_permission?(action)
|
52
48
|
ActiveSupport::Deprecation.warn 'has_permission?(action) is deprecated ' +
|
53
49
|
'and may be removed from future releases, use permission?(route, ext ' +
|
@@ -119,6 +115,7 @@ class ActiveRecord::Base
|
|
119
115
|
|
120
116
|
has_many :permissions, :as => :permittable, :dependent => :destroy
|
121
117
|
accepts_nested_attributes_for :permissions, :allow_destroy => true
|
118
|
+
attr_accessible :permissions_attributes
|
122
119
|
validates_associated :permissions
|
123
120
|
|
124
121
|
class << self
|
data/test/permission_test.rb
CHANGED
@@ -195,28 +195,35 @@ class PermissionTest < MiniTest::Unit::TestCase
|
|
195
195
|
refute @blog.permission?("articles#new")
|
196
196
|
assert_nil @blog.permission?("silk_routes#index")
|
197
197
|
assert_equal 5, @blog.permissions.count
|
198
|
-
|
198
|
+
@blog.permissions.delete_all
|
199
|
+
assert_empty @blog.permissions
|
199
200
|
refute_empty @blog.create_default_permissions!
|
200
201
|
assert_equal @blog.permissions.length, @blog.permissions.count
|
201
|
-
|
202
|
+
refute_empty @blog.permissions
|
203
|
+
@blog.permissions.delete_all
|
204
|
+
assert_empty @blog.permissions
|
202
205
|
assert_empty @blog.permissions
|
203
206
|
refute_nil @blog.create_permission!("categories#create", false, @admin)
|
204
207
|
assert_equal 1, @blog.permissions.count
|
205
208
|
refute @blog.permission?(:"categories#create", @admin)
|
206
209
|
assert_equal 1, @blog.permissions.count
|
207
|
-
|
210
|
+
@blog.permissions.delete_all
|
211
|
+
assert_empty @blog.permissions
|
208
212
|
assert @blog.permission("categories#create", @admin).
|
209
213
|
update_attribute(:value, false)
|
210
214
|
assert_equal 1, @blog.permissions.count
|
211
215
|
refute @blog.permission?(:"categories#create", @admin)
|
212
216
|
assert_equal 1, @blog.permissions.count
|
213
|
-
|
217
|
+
@blog.permissions.delete_all
|
218
|
+
assert_empty @blog.permissions
|
214
219
|
refute_nil @blog.create_permission!("categories#create", true, @admin)
|
215
220
|
assert @blog.permission?(:"categories#create", @admin)
|
216
|
-
|
221
|
+
@blog.permissions.delete_all
|
222
|
+
assert_empty @blog.permissions
|
217
223
|
refute_nil @blog.create_permission!("categories#create", true, @admin)
|
218
224
|
assert @blog.permission?(:"categories#create", @admin)
|
219
|
-
|
225
|
+
@blog.permissions.delete_all
|
226
|
+
assert_empty @blog.permissions
|
220
227
|
refute_nil @blog.create_permission!("categories#create", true, @bob)
|
221
228
|
refute Blog.first.permissions.empty?
|
222
229
|
assert_equal @blog.permissions.length, @blog.permissions.count
|
@@ -246,7 +253,8 @@ class PermissionTest < MiniTest::Unit::TestCase
|
|
246
253
|
assert @category.permission?("articles#create", @bob)
|
247
254
|
assert_nil @category.permission?("articles#show", @bob)
|
248
255
|
assert_equal 5, @category.permissions.count
|
249
|
-
|
256
|
+
@category.permissions.delete_all
|
257
|
+
assert_empty @category.permissions
|
250
258
|
assert_nil @category.permission?("articles#create", @bob)
|
251
259
|
refute_nil @category.create_permission!("articles#create", true, @bob)
|
252
260
|
assert @category.permission?("articles#create", @bob)
|
@@ -274,7 +282,8 @@ class PermissionTest < MiniTest::Unit::TestCase
|
|
274
282
|
refute @article.permission?('comments#new', @spammer)
|
275
283
|
refute @article.permission?('comments#create', @spammer)
|
276
284
|
assert_equal 5, @article.permissions.count
|
277
|
-
|
285
|
+
@article.permissions.delete_all
|
286
|
+
assert_empty @article.permissions
|
278
287
|
assert_equal 0, @article.permissions.count
|
279
288
|
assert @article.permission?('comments#new')
|
280
289
|
assert_equal @article.permissions.count, @article.permissions.length
|
@@ -318,7 +327,8 @@ class PermissionTest < MiniTest::Unit::TestCase
|
|
318
327
|
assert_equal 1, @comment1.permissions.count
|
319
328
|
refute @comment1.permission?(:"comments#show")
|
320
329
|
assert_equal 1, @comment1.permissions.count
|
321
|
-
|
330
|
+
@comment1.permissions.delete_all
|
331
|
+
assert_empty @comment1.permissions
|
322
332
|
assert @comment1.permission?(:"comments#show")
|
323
333
|
assert_equal 1, @comment1.permissions.count
|
324
334
|
refute_nil @comment1.permission("comments#show").
|
metadata
CHANGED
@@ -1,39 +1,43 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_permission
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 2.0.1
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Cyril Wack
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2011-09-08 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
15
16
|
name: railties
|
16
|
-
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
19
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
21
23
|
version: 3.0.0
|
22
24
|
type: :runtime
|
23
|
-
|
24
|
-
version_requirements: *2153626060
|
25
|
+
version_requirements: *id001
|
25
26
|
description: Simple Rails plugin to assign a list of permissions on a resource.
|
26
|
-
email:
|
27
|
-
- cyril
|
27
|
+
email:
|
28
|
+
- contact@cyril.io
|
28
29
|
executables: []
|
30
|
+
|
29
31
|
extensions: []
|
32
|
+
|
30
33
|
extra_rdoc_files: []
|
31
|
-
|
34
|
+
|
35
|
+
files:
|
32
36
|
- .gitignore
|
33
37
|
- .rvmrc
|
34
38
|
- Gemfile
|
35
39
|
- MIT-LICENSE
|
36
|
-
- README.
|
40
|
+
- README.md
|
37
41
|
- Rakefile
|
38
42
|
- VERSION.yml
|
39
43
|
- acts_as_permission.gemspec
|
@@ -52,28 +56,30 @@ files:
|
|
52
56
|
- test/test_helper.rb
|
53
57
|
homepage: http://github.com/cyril/acts_as_permission
|
54
58
|
licenses: []
|
59
|
+
|
55
60
|
post_install_message:
|
56
61
|
rdoc_options: []
|
57
|
-
|
62
|
+
|
63
|
+
require_paths:
|
58
64
|
- lib
|
59
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
66
|
none: false
|
61
|
-
requirements:
|
62
|
-
- -
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version:
|
65
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
72
|
none: false
|
67
|
-
requirements:
|
68
|
-
- -
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version:
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
71
77
|
requirements: []
|
78
|
+
|
72
79
|
rubyforge_project: acts_as_permission
|
73
|
-
rubygems_version: 1.
|
80
|
+
rubygems_version: 1.8.9
|
74
81
|
signing_key:
|
75
82
|
specification_version: 3
|
76
83
|
summary: Simple permission solution for Rails.
|
77
|
-
test_files:
|
78
|
-
|
79
|
-
- test/test_helper.rb
|
84
|
+
test_files: []
|
85
|
+
|
data/README.rdoc
DELETED
@@ -1,296 +0,0 @@
|
|
1
|
-
= Acts as permission
|
2
|
-
|
3
|
-
Acts as permission is a plugin for Ruby on Rails that allows to assign a list of
|
4
|
-
permissions on an object, according to the ACL concept, where each permission
|
5
|
-
can be extended to a subject.
|
6
|
-
|
7
|
-
More specifically, it can make possible to allow or to deny any action of the
|
8
|
-
controller of a protected resource. These actions are called permittables.
|
9
|
-
|
10
|
-
A permittable action can be directly attached to a resource. Examples of such
|
11
|
-
actions:
|
12
|
-
|
13
|
-
* <tt>show</tt>,
|
14
|
-
* <tt>edit</tt>,
|
15
|
-
* <tt>update</tt>,
|
16
|
-
* <tt>destroy</tt>.
|
17
|
-
|
18
|
-
Or it can be indirectly, through a parent resource. Examples:
|
19
|
-
|
20
|
-
* <tt>index</tt>,
|
21
|
-
* <tt>new</tt>,
|
22
|
-
* <tt>create</tt>.
|
23
|
-
|
24
|
-
Here is an example of query to a direct article's action:
|
25
|
-
|
26
|
-
@article = Article.find(params[:id])
|
27
|
-
@article.permission?("articles#destroy") # => false
|
28
|
-
|
29
|
-
Same query, extended to a user:
|
30
|
-
|
31
|
-
@article.permission?("articles#destroy", @bob) # => nil
|
32
|
-
@article.permission?("articles#destroy", @admin) # => true
|
33
|
-
|
34
|
-
A query example on an indirect articles' action, through a category:
|
35
|
-
|
36
|
-
@category = Category.find(params[:category_id])
|
37
|
-
@category.permission?("articles#index") # => true
|
38
|
-
|
39
|
-
Other examples, on unpermittable actions:
|
40
|
-
|
41
|
-
@category.permission?("articles#read") # => nil
|
42
|
-
@category.permission?("silk_routes#index") # => nil
|
43
|
-
|
44
|
-
The value of a permission depends on its context, which includes a route and an
|
45
|
-
optional extension to a permitted resource.
|
46
|
-
|
47
|
-
The <tt>permission?(route, ext = nil)</tt> query may return, depending on the
|
48
|
-
context:
|
49
|
-
|
50
|
-
* <tt>true</tt>, if the permission is allowed;
|
51
|
-
* <tt>false</tt>, if the permission is denied;
|
52
|
-
* <tt>nil</tt>, if the permission is indefinable (resulting of the unknowned
|
53
|
-
context).
|
54
|
-
|
55
|
-
== Philosophy
|
56
|
-
|
57
|
-
General library that does only one thing, without any feature.
|
58
|
-
|
59
|
-
== Installation
|
60
|
-
|
61
|
-
Include the gem in your <tt>Gemfile</tt>:
|
62
|
-
|
63
|
-
gem 'acts_as_permission'
|
64
|
-
|
65
|
-
And run the +bundle+ command. Or as a plugin:
|
66
|
-
|
67
|
-
rails plugin install git://github.com/cyril/acts_as_permission.git
|
68
|
-
|
69
|
-
Then, generate files and apply the migration:
|
70
|
-
|
71
|
-
rails generate permissions
|
72
|
-
rake db:migrate
|
73
|
-
|
74
|
-
== Getting started
|
75
|
-
|
76
|
-
=== Configuring models
|
77
|
-
|
78
|
-
Permittable models have to be declared with <tt>acts_as_permission</tt>. And
|
79
|
-
they have to be so with a default permission mask. For example:
|
80
|
-
|
81
|
-
# app/models/article.rb
|
82
|
-
class Article < ActiveRecord::Base
|
83
|
-
acts_as_permission({
|
84
|
-
'articles#show' => [true, {}],
|
85
|
-
'articles#edit' => [false, {
|
86
|
-
:permitted_id => 1,
|
87
|
-
:permitted_type => "User",
|
88
|
-
:value => true }],
|
89
|
-
'articles#update' => [false, {
|
90
|
-
:permitted_id => 1,
|
91
|
-
:permitted_type => "User",
|
92
|
-
:value => true }],
|
93
|
-
'articles#destroy' => [false, {
|
94
|
-
:permitted_id => 1,
|
95
|
-
:permitted_type => "User",
|
96
|
-
:value => true }],
|
97
|
-
'comments#index' => true,
|
98
|
-
'comments#new' => [true, [{
|
99
|
-
:permitted_id => 3,
|
100
|
-
:permitted_type => "User",
|
101
|
-
:value => false }]],
|
102
|
-
'comments#create' => [true, [{
|
103
|
-
:permitted_id => 3,
|
104
|
-
:permitted_type => "User",
|
105
|
-
:value => false }]]})
|
106
|
-
|
107
|
-
belongs_to :user
|
108
|
-
has_many :comments, :dependent => :destroy
|
109
|
-
end
|
110
|
-
|
111
|
-
# app/models/comment.rb
|
112
|
-
class Comment < ActiveRecord::Base
|
113
|
-
acts_as_permission([
|
114
|
-
["comments#show", true],
|
115
|
-
["comments#edit", [false, [
|
116
|
-
{:permitted_id => 1, :permitted_type => "User", :value => true},
|
117
|
-
{:permitted_id => 2, :permitted_type => "User", :value => true} ]]],
|
118
|
-
["comments#update", [false, [
|
119
|
-
{:permitted_id => 1, :permitted_type => "User", :value => true},
|
120
|
-
{:permitted_id => 2, :permitted_type => "User", :value => true} ]]],
|
121
|
-
["comments#destroy", [false, {
|
122
|
-
:permitted_id => 1,
|
123
|
-
:permitted_type => "User",
|
124
|
-
:value => true }]]])
|
125
|
-
|
126
|
-
belongs_to :article
|
127
|
-
belongs_to :user
|
128
|
-
end
|
129
|
-
|
130
|
-
Optionally, some models (such as <tt>User</tt>, <tt>Group</tt>, <tt>Role</tt>)
|
131
|
-
can also be declared as permitted with <tt>is_able_to_be_permitted</tt>.
|
132
|
-
Example:
|
133
|
-
|
134
|
-
# app/models/user.rb
|
135
|
-
class User < ActiveRecord::Base
|
136
|
-
is_able_to_be_permitted
|
137
|
-
|
138
|
-
with_options :dependent => :destroy do |opts|
|
139
|
-
opts.has_many :articles
|
140
|
-
opts.has_many :comments
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
=== Configuring controllers
|
145
|
-
|
146
|
-
Example of a fully protected comments controller:
|
147
|
-
|
148
|
-
class CommentsController < ApplicationController
|
149
|
-
before_filter :check_permissions
|
150
|
-
|
151
|
-
# GET /comments
|
152
|
-
# GET /comments.xml
|
153
|
-
def index
|
154
|
-
@comments = current_resource.comments
|
155
|
-
|
156
|
-
respond_to do |format|
|
157
|
-
format.html # index.html.erb
|
158
|
-
format.xml { render :xml => @comments }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# GET /comments/1
|
163
|
-
# GET /comments/1.xml
|
164
|
-
def show
|
165
|
-
@comment = current_resource
|
166
|
-
|
167
|
-
respond_to do |format|
|
168
|
-
format.html # show.html.erb
|
169
|
-
format.xml { render :xml => @comment }
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# GET /comments/new
|
174
|
-
# GET /comments/new.xml
|
175
|
-
def new
|
176
|
-
@comment = current_resource.comments.build
|
177
|
-
|
178
|
-
respond_to do |format|
|
179
|
-
format.html # new.html.erb
|
180
|
-
format.xml { render :xml => @comment }
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
# GET /comments/1/edit
|
185
|
-
def edit
|
186
|
-
@comment = current_resource
|
187
|
-
end
|
188
|
-
|
189
|
-
# POST /comments
|
190
|
-
# POST /comments.xml
|
191
|
-
def create
|
192
|
-
@comment = current_resource.comments.build(params[:comment])
|
193
|
-
|
194
|
-
respond_to do |format|
|
195
|
-
if @comment.save
|
196
|
-
format.html { redirect_to(@comment,
|
197
|
-
:notice => 'Comment was successfully created.') }
|
198
|
-
format.xml { render :xml => @comment, :status => :created,
|
199
|
-
:location => @comment }
|
200
|
-
else
|
201
|
-
format.html { render :action => "new" }
|
202
|
-
format.xml { render :xml => @comment.errors,
|
203
|
-
:status => :unprocessable_entity }
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
# PUT /comments/1
|
209
|
-
# PUT /comments/1.xml
|
210
|
-
def update
|
211
|
-
@comment = current_resource
|
212
|
-
|
213
|
-
respond_to do |format|
|
214
|
-
if @comment.update_attributes(params[:comment])
|
215
|
-
format.html { redirect_to(@comment,
|
216
|
-
:notice => 'Comment was successfully updated.') }
|
217
|
-
format.xml { head :ok }
|
218
|
-
else
|
219
|
-
format.html { render :action => "edit" }
|
220
|
-
format.xml { render :xml => @comment.errors,
|
221
|
-
:status => :unprocessable_entity }
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
# DELETE /comments/1
|
227
|
-
# DELETE /comments/1.xml
|
228
|
-
def destroy
|
229
|
-
@comment = current_resource
|
230
|
-
@comment.destroy
|
231
|
-
|
232
|
-
respond_to do |format|
|
233
|
-
format.html { redirect_to(comments_url) }
|
234
|
-
format.xml { head :ok }
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
protected
|
239
|
-
|
240
|
-
def check_permissions
|
241
|
-
route = [ params[:controller],
|
242
|
-
params[:action] ].join('#')
|
243
|
-
|
244
|
-
unless (current_user &&
|
245
|
-
current_resource.permission?(route, current_user)) ||
|
246
|
-
current_resource.permission?(route)
|
247
|
-
respond_to do |format|
|
248
|
-
format.html { redirect_to(:back, :warning => '403 Forbidden',
|
249
|
-
:status => :forbidden) }
|
250
|
-
format.xml { render :xml => '403 Forbidden', :status => :forbidden }
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
def current_resource
|
256
|
-
@current_resource ||= if params[:id]
|
257
|
-
Comment.find(params[:id])
|
258
|
-
else
|
259
|
-
Article.find(params[:article_id], :readonly => true)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
=== Configuring views
|
265
|
-
|
266
|
-
We can now perform some checks on related views from a comment instance, thanks
|
267
|
-
to the protected actions of its controller, in order to only display allowed
|
268
|
-
links:
|
269
|
-
|
270
|
-
<% if current_user && @comment.permission?("comments#edit", current_user) ||
|
271
|
-
@comment.permission?("comments#edit") %>
|
272
|
-
<%= link_to "Edit comment",
|
273
|
-
edit_article_comment_path(@comment.article, @comment) %>
|
274
|
-
<% end %>
|
275
|
-
|
276
|
-
And also some indirect checks from the current article instance, like this one:
|
277
|
-
|
278
|
-
<% if current_user && @article.permission?("comments#index", current_user) ||
|
279
|
-
@article.permission?("comments#index") %>
|
280
|
-
<%= link_to "Comments", article_comments_path(@article) %>
|
281
|
-
<% end %>
|
282
|
-
|
283
|
-
Or this other one:
|
284
|
-
|
285
|
-
<% if current_user && @article.permission?("comments#new", current_user) ||
|
286
|
-
@article.permission?("comments#new") %>
|
287
|
-
<%= link_to "New comment", new_article_comment_path(@article) %>
|
288
|
-
<% end %>
|
289
|
-
|
290
|
-
==== Form helper
|
291
|
-
|
292
|
-
Object's permissions management is as simple as:
|
293
|
-
|
294
|
-
<%= permission_fields f %>
|
295
|
-
|
296
|
-
Copyright (c) 2009-2011 Cyril Wack, released under the MIT license
|