allowance 0.0.4 → 0.1.0
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/.documentup.json +4 -0
- data/README.md +216 -37
- data/lib/allowance/permissions.rb +8 -6
- data/lib/allowance/version.rb +1 -1
- data/spec/permissions_spec.rb +27 -1
- metadata +9 -9
- data/LICENSE +0 -22
data/.documentup.json
ADDED
data/README.md
CHANGED
@@ -1,73 +1,252 @@
|
|
1
1
|
# Allowance
|
2
2
|
|
3
|
-
Allowance is a general-use permission management library
|
4
|
-
|
3
|
+
**Allowance is a general-use permission management library for Ruby.
|
4
|
+
It's decidedly simple, highly flexible, and has out-of-the-box support
|
5
|
+
for ActiveModel-compliant classes.**
|
6
|
+
|
7
|
+
It was inspired by Ryan Bates' fantastic Rails authorization plugin [cancan](https://github.com/ryanb/cancan) and, unlike most other gems
|
8
|
+
of its kind, is not bound to a specific framework.
|
5
9
|
|
6
10
|
A simple Example:
|
7
11
|
|
8
12
|
``` ruby
|
9
13
|
p = Allowance.define do |can|
|
10
|
-
|
11
|
-
can.
|
14
|
+
# Allow logging in
|
15
|
+
can.login!
|
16
|
+
|
17
|
+
# Allow creating new Article instances
|
18
|
+
can.create! Article
|
19
|
+
|
20
|
+
# Allow the user to edit Article instances that belong to him
|
21
|
+
can.edit! Article, :author_id => current_user.id
|
22
|
+
|
23
|
+
# Allow viewing all Article instances that are published or the user's
|
24
|
+
can.read! Article, ['published = ? OR author_id = ?', true, current_user.id]
|
12
25
|
end
|
26
|
+
```
|
27
|
+
|
28
|
+
Allowance parses these permission definitions and stores them in the object the
|
29
|
+
`Allowance.define` call returns. It is now up to you to query that object where
|
30
|
+
necessary. Some examples:
|
13
31
|
|
14
|
-
|
15
|
-
p.
|
16
|
-
p.
|
32
|
+
``` ruby
|
33
|
+
p.login? # true
|
34
|
+
p.create? Article # true
|
35
|
+
p.read? @article # true or false, depending on state of @article
|
17
36
|
```
|
18
37
|
|
19
|
-
You can
|
38
|
+
You can use the same object to provide you with correctly scoped models, too:
|
20
39
|
|
21
40
|
``` ruby
|
22
|
-
p
|
23
|
-
|
24
|
-
|
41
|
+
p.scoped_model(:view, Article).all
|
42
|
+
# -> Article.where(['published = ? OR author_id = ?', true, current_user.id]).all
|
43
|
+
```
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
## Installation
|
48
|
+
|
49
|
+
### Requirements
|
50
|
+
|
51
|
+
Allowance should work fine with Ruby 1.8.7, 1.9.2, 1.9.3 and respective JRuby versions. Please consult Allowance's [Travis status page](http://travis-ci.org/hmans/allowance) for details.
|
52
|
+
|
53
|
+
[](http://travis-ci.org/hmans/allowance)
|
54
|
+
|
55
|
+
### Installing through Bundler
|
56
|
+
|
57
|
+
Well, you've done this before, haven't you? Just add the `allowance` gem to your project's Gemfile:
|
58
|
+
|
59
|
+
``` ruby
|
60
|
+
gem 'allowance'
|
61
|
+
```
|
62
|
+
|
63
|
+
### Installing without Bundler
|
64
|
+
|
65
|
+
Install using RubyGems:
|
66
|
+
|
67
|
+
```
|
68
|
+
gem install allowance
|
69
|
+
```
|
70
|
+
|
71
|
+
Then require it in your code:
|
72
|
+
|
73
|
+
```
|
74
|
+
require 'rubygems'
|
75
|
+
require 'allowance'
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
## Usage
|
80
|
+
|
81
|
+
### Defining permissions
|
82
|
+
|
83
|
+
Use `Allowance.define` to create a new permissions object, then use its `allow!` or `can!`
|
84
|
+
methods to add permissions:
|
85
|
+
|
86
|
+
``` ruby
|
87
|
+
p = Allowance.define
|
88
|
+
p.allow! :sing
|
89
|
+
```
|
25
90
|
|
26
|
-
|
27
|
-
can.delete! Post, :user_id => current_user.id
|
91
|
+
Instead of using `allow!` or `can!`, you can just name the permission directly:
|
28
92
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
93
|
+
``` ruby
|
94
|
+
p.sing!
|
95
|
+
```
|
96
|
+
|
97
|
+
You can also specify permissions as a block:
|
98
|
+
|
99
|
+
``` ruby
|
100
|
+
p = Allowance.define do |allow|
|
101
|
+
allow.sing!
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
### Querying permissions
|
106
|
+
|
107
|
+
Similar to how you define permissions, you can use the `allowed?` or `can?` methods, or
|
108
|
+
query permissions directly by name. The following two lines are equivalent:
|
109
|
+
|
110
|
+
``` ruby
|
111
|
+
p.allowed? :sing
|
112
|
+
p.can? :sing
|
113
|
+
p.sing?
|
114
|
+
```
|
115
|
+
|
116
|
+
### One-dimensional permissions
|
117
|
+
|
118
|
+
Allowance lets you define simple, one-dimenstional permissions like this:
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
p = Allowance.define do |allow|
|
122
|
+
allow.sing!
|
123
|
+
allow.play!
|
124
|
+
allow.dance! if current_user.can_dance?
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
### Two-dimensional permissions
|
129
|
+
|
130
|
+
Most of the time, you will be using two-dimensional permissions, consisting of a
|
131
|
+
_verb_ and an _object_, with the object typically being some kind of model class.
|
132
|
+
For example:
|
133
|
+
|
134
|
+
``` ruby
|
135
|
+
p = Allowance.define do |allow|
|
136
|
+
allow.view! Article
|
137
|
+
|
138
|
+
if current_user.is_admin?
|
139
|
+
allow.edit! Article
|
33
140
|
end
|
34
141
|
end
|
35
142
|
```
|
36
143
|
|
37
|
-
|
38
|
-
classes that are ActiveModel based (eg. ActiveRecord, Mongoid etc.):
|
144
|
+
When querying for permissions, just pass an object as an additional parameter:
|
39
145
|
|
40
146
|
``` ruby
|
41
|
-
p
|
42
|
-
|
43
|
-
|
147
|
+
p.edit? Article
|
148
|
+
```
|
149
|
+
|
150
|
+
When you pass a class instance (instead of a class), Allowance will check for
|
151
|
+
the permission defined for its class, so the following will work, too:
|
152
|
+
|
153
|
+
``` ruby
|
154
|
+
p.edit? @article
|
155
|
+
```
|
156
|
+
|
157
|
+
Allowance will even allow you to define permissions in specific objects -- this is not recommended, though, since permission objects defined through Allowance only exist in memory; if you need to control permissions on individual model objects, you'll be better off with another authorization library, ideally one that stores its permissions in your datastore.
|
158
|
+
|
159
|
+
|
160
|
+
### Defining model scopes
|
161
|
+
|
162
|
+
For classes implementing ActiveModel (eg. ActiveRecord, Mongoid and others), Allowance allows you to restrict certain permissions to specific scopes. For example, if, in a web application, a user should only be able to see articles that have been published, but admin users can see everything, you can define the permissions like this:
|
163
|
+
|
164
|
+
``` ruby
|
165
|
+
p = Allowance.define do |allow|
|
166
|
+
allow.view! Article, :published => true
|
167
|
+
|
168
|
+
if current_user.is_admin?
|
169
|
+
allow.view! Article
|
170
|
+
end
|
44
171
|
end
|
45
172
|
```
|
46
173
|
|
47
|
-
|
174
|
+
You can see here that when a specific permission (for a _verb_ and _object_ combination) is defined more than once, the last definition will overwrite all those that came before it.
|
48
175
|
|
49
|
-
|
176
|
+
In the example above, we defined the scope as a so-called "where conditions hash", ie. a hash passed to the model class' `.where` method. Since the hash notation assumes a logical AND and isn't all that flexible overall, you can also use the same string/array syntax you know from ActiveModel:
|
177
|
+
|
178
|
+
``` ruby
|
179
|
+
p.view! Article, ['published = ? OR user_id = ?', true, current_user.id]
|
180
|
+
```
|
181
|
+
|
182
|
+
Lastly, you're encouraged to re-use scopes defined on the model class. Just define your scope using a lambda:
|
50
183
|
|
51
|
-
|
184
|
+
``` ruby
|
185
|
+
p.view! Article, lambda { |r| r.viewable_by(current_user) }
|
186
|
+
```
|
52
187
|
|
53
|
-
|
188
|
+
Not only can use check permissions against those scopes, but you can also use the `#scoped_model` method to retreive a correctly scoped model according to its previous scope definition. For example:
|
54
189
|
|
55
|
-
|
190
|
+
``` ruby
|
191
|
+
@articles = p.scoped_model(:view, Article).order('created_at DESC').all
|
192
|
+
```
|
56
193
|
|
57
|
-
|
194
|
+
### Defining contextual permissions
|
58
195
|
|
59
|
-
|
196
|
+
Since permissions are just Ruby code, you can use all your favorite language
|
197
|
+
constructs when defining permissions. For example, in a web application
|
198
|
+
providing a `current_user` method, you can do the following:
|
60
199
|
|
61
|
-
|
200
|
+
``` ruby
|
201
|
+
permissions = Allowance.define do |allow|
|
202
|
+
allow.read! Article
|
62
203
|
|
63
|
-
|
204
|
+
if current_user.is_admin?
|
205
|
+
allow.destroy! Article
|
206
|
+
end
|
207
|
+
end
|
208
|
+
```
|
209
|
+
|
210
|
+
You can then query this permission like you'd expect:
|
64
211
|
|
65
|
-
|
212
|
+
``` ruby
|
213
|
+
@article = Article.find(params[:id])
|
214
|
+
if permissions.destroy? @article
|
215
|
+
@article.destroy
|
216
|
+
else
|
217
|
+
raise "You're not allowed to do this"
|
218
|
+
end
|
219
|
+
```
|
66
220
|
|
67
221
|
## Contributing
|
68
222
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
223
|
+
I'm looking forward to seeing your Pull Requests. However, please be aware that,
|
224
|
+
like with pretty much all other projects I'm maintaining, I'm trying to keep it
|
225
|
+
as small as possible, so I'm going to be somewhat picky about which pull
|
226
|
+
requests to accept. If you're adding new features, I recommed you check back
|
227
|
+
with me first in order to avoid disappointment.
|
228
|
+
|
229
|
+
## License
|
230
|
+
|
231
|
+
Copyright (c) 2012 Hendrik Mans <hendrik@mans.de>
|
232
|
+
|
233
|
+
MIT License
|
234
|
+
|
235
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
236
|
+
a copy of this software and associated documentation files (the
|
237
|
+
"Software"), to deal in the Software without restriction, including
|
238
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
239
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
240
|
+
permit persons to whom the Software is furnished to do so, subject to
|
241
|
+
the following conditions:
|
242
|
+
|
243
|
+
The above copyright notice and this permission notice shall be
|
244
|
+
included in all copies or substantial portions of the Software.
|
245
|
+
|
246
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
247
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
248
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
249
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
250
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
251
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
252
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -20,6 +20,8 @@ module Allowance
|
|
20
20
|
false
|
21
21
|
end
|
22
22
|
|
23
|
+
alias_method :can?, :allowed?
|
24
|
+
|
23
25
|
def allow!(verbs, objects = nil, scope = true, &blk)
|
24
26
|
expand_permissions(verbs).each do |verb|
|
25
27
|
[objects].flatten.each do |object|
|
@@ -28,6 +30,8 @@ module Allowance
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
33
|
+
alias_method :can!, :allow!
|
34
|
+
|
31
35
|
def method_missing(name, *args, &blk)
|
32
36
|
if name.to_s =~ /(.+)!$/
|
33
37
|
allow!($1.to_sym, *args, &blk)
|
@@ -40,12 +44,10 @@ module Allowance
|
|
40
44
|
|
41
45
|
def scoped_model(verb, model)
|
42
46
|
if p = @permissions[[verb, model]]
|
43
|
-
|
44
|
-
model.where(p)
|
45
|
-
|
46
|
-
|
47
|
-
else
|
48
|
-
model
|
47
|
+
case p
|
48
|
+
when Hash, String, Array then model.where(p)
|
49
|
+
when Proc then p.call(model)
|
50
|
+
else model
|
49
51
|
end
|
50
52
|
else
|
51
53
|
model.where(false)
|
data/lib/allowance/version.rb
CHANGED
data/spec/permissions_spec.rb
CHANGED
@@ -5,6 +5,14 @@ module Allowance
|
|
5
5
|
SomeClass = Class.new
|
6
6
|
SomeOtherClass = Class.new
|
7
7
|
|
8
|
+
it "should allow permissions to be specified and queried through its instance methods" do
|
9
|
+
subject.can! :sing
|
10
|
+
subject.allow! :dance
|
11
|
+
|
12
|
+
insist subject.can? :dance
|
13
|
+
insist subject.allowed? :sing
|
14
|
+
end
|
15
|
+
|
8
16
|
it "should allow simple permissions to be specified" do
|
9
17
|
subject.moo!
|
10
18
|
|
@@ -73,7 +81,7 @@ module Allowance
|
|
73
81
|
subject.scoped_model(:view, model).should == scoped_model
|
74
82
|
end
|
75
83
|
|
76
|
-
it "should allow scopes to be defined through
|
84
|
+
it "should allow scopes to be defined through a conditions hash" do
|
77
85
|
model = mock
|
78
86
|
model.should_receive(:where).with(:awesome => true).and_return(scoped_model = mock)
|
79
87
|
|
@@ -81,6 +89,24 @@ module Allowance
|
|
81
89
|
|
82
90
|
subject.scoped_model(:view, model).should == scoped_model
|
83
91
|
end
|
92
|
+
|
93
|
+
it "should allow scopes to be defined through a conditions string" do
|
94
|
+
model = mock
|
95
|
+
model.should_receive(:where).with("awesome = true").and_return(scoped_model = mock)
|
96
|
+
|
97
|
+
subject.view! model, "awesome = true"
|
98
|
+
|
99
|
+
subject.scoped_model(:view, model).should == scoped_model
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should allow scopes to be defined through a conditions array" do
|
103
|
+
model = mock
|
104
|
+
model.should_receive(:where).with(["awesome = ?", true]).and_return(scoped_model = mock)
|
105
|
+
|
106
|
+
subject.view! model, ["awesome = ?", true]
|
107
|
+
|
108
|
+
subject.scoped_model(:view, model).should == scoped_model
|
109
|
+
end
|
84
110
|
end
|
85
111
|
end
|
86
112
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: allowance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70231867448560 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70231867448560
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70231867447800 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70231867447800
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: watchr
|
38
|
-
requirement: &
|
38
|
+
requirement: &70231867444600 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70231867444600
|
47
47
|
description: A generic, but decidedly awesome authorization control layer.
|
48
48
|
email:
|
49
49
|
- hendrik@mans.de
|
@@ -51,12 +51,12 @@ executables: []
|
|
51
51
|
extensions: []
|
52
52
|
extra_rdoc_files: []
|
53
53
|
files:
|
54
|
+
- .documentup.json
|
54
55
|
- .gitignore
|
55
56
|
- .rspec
|
56
57
|
- .travis.yml
|
57
58
|
- .watchr
|
58
59
|
- Gemfile
|
59
|
-
- LICENSE
|
60
60
|
- README.md
|
61
61
|
- Rakefile
|
62
62
|
- allowance.gemspec
|
data/LICENSE
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2012 Hendrik Mans
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|