allowance 0.1.1 → 0.2.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/.travis.yml +2 -5
- data/CHANGES.md +10 -0
- data/LICENSE +22 -0
- data/README.md +140 -151
- data/lib/allowance.rb +6 -5
- data/lib/allowance/activerecord_ext.rb +15 -0
- data/lib/allowance/subject.rb +70 -0
- data/lib/allowance/version.rb +1 -1
- data/spec/activerecord_ext_spec.rb +7 -0
- data/spec/subject_spec.rb +96 -0
- metadata +32 -14
- data/lib/allowance/permissions.rb +0 -69
- data/spec/allowance_spec.rb +0 -17
- data/spec/permissions_spec.rb +0 -112
data/.travis.yml
CHANGED
data/CHANGES.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
## 0.2.0 (in development)
|
2
|
+
|
3
|
+
* A complete refactoring to make things slimmer, leaner and meaner.
|
4
|
+
* Moved all functionality into the `Allowance::Subject` mixin.
|
5
|
+
* ActiveRecord integration.
|
6
|
+
* Better specs!
|
7
|
+
|
8
|
+
## 0.1.1 (2012-05-29)
|
9
|
+
|
10
|
+
* Initial release, an extraction from [happy](https://github.com/hmans/happy)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Hendrik Mans <hendrik@mans.de>
|
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.
|
data/README.md
CHANGED
@@ -2,227 +2,216 @@
|
|
2
2
|
|
3
3
|
**Allowance is a general-use permission management library for Ruby.
|
4
4
|
It's decidedly simple, highly flexible, and has out-of-the-box support
|
5
|
-
for
|
5
|
+
for ActiveRecord models.**
|
6
6
|
|
7
|
-
|
8
|
-
of its kind, is not bound to a specific framework.
|
7
|
+
Key features are:
|
9
8
|
|
10
|
-
|
9
|
+
* Works great in any Ruby project, not just Rails.
|
10
|
+
* It loves ActiveRecord scopes.
|
11
|
+
* Very little code (under 100 lines).
|
11
12
|
|
12
|
-
|
13
|
-
p = Allowance.define do |can|
|
14
|
-
# Allow logging in
|
15
|
-
can.login!
|
13
|
+
Allowance was inspired by Ryan Bates' fantastic Rails authorization plugin [CanCan](https://github.com/ryanb/cancan).
|
16
14
|
|
17
|
-
|
18
|
-
can.create! Article
|
15
|
+
### A simple example:
|
19
16
|
|
20
|
-
|
21
|
-
|
17
|
+
~~~ ruby
|
18
|
+
class User < ActiveRecord::Base
|
19
|
+
include Allowance::Subject
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
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:
|
21
|
+
def define_permissions
|
22
|
+
# every user can see all other users
|
23
|
+
allow :read, User
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
p.create? Article # true
|
35
|
-
p.read? @article # true or false, depending on state of @article
|
36
|
-
```
|
25
|
+
# every user can see all published posts
|
26
|
+
allow :read, Post, Post.published
|
37
27
|
|
38
|
-
|
28
|
+
# users can edit/delete their own posts
|
29
|
+
allow :manage, Post, user_id: id
|
39
30
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
31
|
+
if admin?
|
32
|
+
# admins can edit/delete all posts
|
33
|
+
allow :manage, Post
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
~~~
|
44
38
|
|
45
39
|
|
46
40
|
|
47
41
|
## Installation
|
48
42
|
|
49
|
-
|
43
|
+
Simply add `allowance` to your project's `Gemfile` or install it through `gem install allowance`.
|
50
44
|
|
51
|
-
|
45
|
+
It should work fine with Ruby 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
46
|
|
53
47
|
[](http://travis-ci.org/hmans/allowance)
|
54
48
|
|
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
49
|
|
79
50
|
## Usage
|
80
51
|
|
81
52
|
### Defining permissions
|
82
53
|
|
83
|
-
|
84
|
-
methods to add permissions:
|
54
|
+
Allowance generally thinks in terms of "subject", "verb" and, optionally, "object". For example, a _user_ (the subject) may be allowed to _edit_ (the verb) a _post_ (the object). The object may be optional; for example, a _user_ may be able to _login_, and so on.
|
85
55
|
|
86
|
-
|
87
|
-
p = Allowance.define
|
88
|
-
p.allow! :sing
|
89
|
-
```
|
56
|
+
So, first of all, your application will need a subject. A subject can be any class that's using the `Allowance::Subject` mixin. In most applications, this will be your `User` class:
|
90
57
|
|
91
|
-
|
58
|
+
~~~ ruby
|
59
|
+
class User
|
60
|
+
include Allowance::Subject
|
61
|
+
end
|
62
|
+
~~~
|
92
63
|
|
93
|
-
|
94
|
-
p.sing!
|
95
|
-
```
|
64
|
+
Instances of this class can now be configured with permissions using the `#allow` method:
|
96
65
|
|
97
|
-
|
66
|
+
~~~ ruby
|
67
|
+
@user.allow :read, Post
|
68
|
+
@user.allow :manage, Post, user_id: @user.id
|
69
|
+
~~~
|
98
70
|
|
99
|
-
|
100
|
-
p = Allowance.define do |allow|
|
101
|
-
allow.sing!
|
102
|
-
end
|
103
|
-
```
|
71
|
+
These permissions can be queried using the `#allowed?` method:
|
104
72
|
|
105
|
-
|
73
|
+
~~~ ruby
|
74
|
+
if @user.allowed?(:destroy, @post)
|
75
|
+
@post.destroy
|
76
|
+
end
|
77
|
+
~~~
|
106
78
|
|
107
|
-
|
108
|
-
query permissions directly by name. The following two lines are equivalent:
|
79
|
+
Since you'll usually want permissions to be defined automatically when a subject instance is created, you can simply add a `#define_permissions` method that will get executed automatically:
|
109
80
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
p.sing?
|
114
|
-
```
|
81
|
+
~~~ ruby
|
82
|
+
class User
|
83
|
+
include Allowance::Subject
|
115
84
|
|
116
|
-
|
85
|
+
def define_permissions
|
86
|
+
allow :read, Post
|
87
|
+
allow :manage, Post, user_id: id
|
88
|
+
end
|
89
|
+
end
|
90
|
+
~~~
|
117
91
|
|
118
|
-
Allowance lets you define simple, one-dimenstional permissions like this:
|
119
92
|
|
120
|
-
|
121
|
-
p = Allowance.define do |allow|
|
122
|
-
allow.sing!
|
123
|
-
allow.play!
|
124
|
-
allow.dance! if current_user.can_dance?
|
125
|
-
end
|
126
|
-
```
|
93
|
+
### Using Allowance with Rails
|
127
94
|
|
128
|
-
|
95
|
+
Most of you will be using Allowance within a Rails application. Allowance gives you a lot of flexibility as to how to plug it into your app, but the most straight forward way goes as follows.
|
129
96
|
|
130
|
-
|
131
|
-
_verb_ and an _object_, with the object typically being some kind of model class.
|
132
|
-
For example:
|
97
|
+
First of all, add the `Allowance::Subject` mixin to your `User` class and create a `define_permissions` method:
|
133
98
|
|
134
|
-
|
135
|
-
|
136
|
-
|
99
|
+
~~~ ruby
|
100
|
+
class User < ActiveRecord::Base
|
101
|
+
include Allowance::Subject
|
137
102
|
|
138
|
-
|
139
|
-
allow
|
103
|
+
def define_permissions
|
104
|
+
allow :read, Post
|
105
|
+
allow :manage, Post, user_id: id
|
140
106
|
end
|
141
107
|
end
|
142
|
-
|
108
|
+
~~~
|
109
|
+
|
110
|
+
Now, assuming your application has a `current_user` controller and helper method, you can use `current_user.allowed?` to query the currently logged in user's permissions. The code for this could look like this:
|
111
|
+
|
112
|
+
~~~ ruby
|
113
|
+
class ApplicationController < ActionController::Base
|
114
|
+
# Set up the current user. In this example, if a currently logged in
|
115
|
+
# user could not be found, we're creating (but not saving) a new
|
116
|
+
# instance representing a "guest" user.
|
117
|
+
#
|
118
|
+
def current_user
|
119
|
+
@current_user ||= load_current_user || User.new
|
120
|
+
end
|
143
121
|
|
144
|
-
|
122
|
+
# Make it available to your views, too.
|
123
|
+
#
|
124
|
+
helper_method :current_user
|
145
125
|
|
146
|
-
|
147
|
-
|
148
|
-
|
126
|
+
# Load the currently logged in user. This is just one of the many
|
127
|
+
# ways of doing it, so this may look different in your app.
|
128
|
+
#
|
129
|
+
def load_current_user
|
130
|
+
User.find(session[:current_user_id]) if session[:current_user_id]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
~~~
|
149
134
|
|
150
|
-
|
151
|
-
the permission defined for its class, so the following will work, too:
|
135
|
+
All this is just a suggestion -- there are many ways of setting this up. If you're used to how CanCan does it, you could set up a separate, abstract permissions class using the `Allowance::Subject` mixin, create an instance in a `before_filter` and use that instead.
|
152
136
|
|
153
|
-
``` ruby
|
154
|
-
p.edit? @article
|
155
|
-
```
|
156
137
|
|
157
|
-
|
138
|
+
### Working with ActiveRecord scopes
|
158
139
|
|
140
|
+
When defining permissions on ActiveRecord models, you can provide an optional scope as a third parameter, either as a hash of conditions, a lambda, or an actual ActiveRecord scope. Here's a couple of examples:
|
159
141
|
|
160
|
-
|
142
|
+
~~~ ruby
|
143
|
+
# Can read all posts
|
144
|
+
allow :read, Post
|
161
145
|
|
162
|
-
|
146
|
+
# Can only read posts I've created
|
147
|
+
allow :read, Post, user_id: self.id
|
163
148
|
|
164
|
-
|
165
|
-
|
166
|
-
allow.view! Article, :published => true
|
149
|
+
# Can only read published posts
|
150
|
+
allow :read, Post, Post.published
|
167
151
|
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
end
|
172
|
-
```
|
152
|
+
# You can also provide strings...
|
153
|
+
allow :read, Post, "published_at IS NOT NULL"
|
173
154
|
|
174
|
-
|
155
|
+
# ...or lambdas:
|
156
|
+
allow :read, Post, ->(p) { p.where("published_at < ?", 2.weeks.ago) }
|
157
|
+
~~~
|
175
158
|
|
176
|
-
|
159
|
+
When checking permissions against a model instance, Allowance will check if it's part of the allowed scope:
|
177
160
|
|
178
|
-
|
179
|
-
|
180
|
-
|
161
|
+
~~~ ruby
|
162
|
+
# This will look up whatever scope is permitted for :read actions
|
163
|
+
# on Post and will check whether the provided instance is inside
|
164
|
+
# that scope (by running `scope.exists?(instance)`).
|
181
165
|
|
182
|
-
|
166
|
+
allowed?(:read, @post)
|
167
|
+
~~~
|
183
168
|
|
184
|
-
|
185
|
-
p.view! Article, lambda { |r| r.viewable_by(current_user) }
|
186
|
-
```
|
169
|
+
In addition to that, the subject provides a method called `#allowed_scope` that returns the scope that is allowed for a certain verb and class:
|
187
170
|
|
188
|
-
|
171
|
+
~~~ ruby
|
172
|
+
@posts = current_user.allowed_scope(Post, :read)
|
173
|
+
~~~
|
189
174
|
|
190
|
-
|
191
|
-
@articles = p.scoped_model(:view, Article).order('created_at DESC').all
|
192
|
-
```
|
175
|
+
Allowance also add a similar class method to your ActiveRecord classes, so the following will work, too:
|
193
176
|
|
194
|
-
|
177
|
+
~~~ ruby
|
178
|
+
@posts = Post.allowed(current_user, :read)
|
179
|
+
~~~
|
195
180
|
|
196
|
-
|
197
|
-
constructs when defining permissions. For example, in a web application
|
198
|
-
providing a `current_user` method, you can do the following:
|
181
|
+
This becomes handy in Rails controllers:
|
199
182
|
|
200
|
-
|
201
|
-
|
202
|
-
|
183
|
+
~~~ ruby
|
184
|
+
class PostsController < ApplicationController
|
185
|
+
def index
|
186
|
+
@posts = Post.allowed(current_user, :index).all
|
187
|
+
respond_with @posts
|
188
|
+
end
|
203
189
|
|
204
|
-
|
205
|
-
|
190
|
+
def show
|
191
|
+
@post = Post.allowed(current_user, :show).find(params[:id])
|
192
|
+
respond_with @post
|
206
193
|
end
|
194
|
+
|
195
|
+
# ...and so on.
|
207
196
|
end
|
208
|
-
|
197
|
+
~~~
|
209
198
|
|
210
|
-
You can then query this permission like you'd expect:
|
211
199
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
200
|
+
### Verb expansion
|
201
|
+
|
202
|
+
Just like its big role model CanCan, Allowance automatically expands the `create`, `read`, `update` and `manage` verbs to include the common RESTful Rails controller action names:
|
203
|
+
|
204
|
+
* `create` is expanded into `create` and `new`
|
205
|
+
* `read` is expanded into `read`, `index` and `show`
|
206
|
+
* `update` is expanded into `update` and `edit`
|
207
|
+
* `manage` is expanded into `manage`, `index`, `show`, `new`, `create`, `edit`, `update` and `destroy`
|
208
|
+
|
220
209
|
|
221
210
|
## Contributing
|
222
211
|
|
223
212
|
I'm looking forward to seeing your Pull Requests. However, please be aware that,
|
224
213
|
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
|
214
|
+
as sharp and small as possible, so I'm going to be somewhat picky about which pull
|
226
215
|
requests to accept. If you're adding new features, I recommed you check back
|
227
216
|
with me first in order to avoid disappointment.
|
228
217
|
|
data/lib/allowance.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
module Allowance
|
2
|
+
end
|
3
|
+
|
1
4
|
require 'allowance/version'
|
2
|
-
require 'allowance/
|
5
|
+
require 'allowance/subject'
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
Permissions.new(&blk)
|
7
|
-
end
|
7
|
+
if defined?(ActiveRecord)
|
8
|
+
require 'allowance/activerecord_ext'
|
8
9
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Allowance
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def allowed(subject, verb = nil)
|
9
|
+
subject.allowed_scope(self, verb)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::Base.send(:include, Allowance::ActiveRecordExtensions)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Allowance
|
2
|
+
module Subject
|
3
|
+
def permissions
|
4
|
+
unless @permissions_defined
|
5
|
+
define_permissions
|
6
|
+
@permissions_defined = true
|
7
|
+
end
|
8
|
+
|
9
|
+
@permissions || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def define_permissions
|
13
|
+
# TODO: log a warning that the subject's define_permissions needs
|
14
|
+
# to be overloaded.
|
15
|
+
end
|
16
|
+
|
17
|
+
def allowed?(verb, object = nil)
|
18
|
+
# Allow access if there is a direct match in permissions.
|
19
|
+
return true if permissions[[verb, object]]
|
20
|
+
|
21
|
+
# If object is a resource instance, try its class.
|
22
|
+
if object.class.respond_to?(:model_name)
|
23
|
+
if allowed?(verb, object.class)
|
24
|
+
# See if the object is part of the defined scope.
|
25
|
+
return allowed_scope(object.class, verb).exists?(object)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Once we get here, access can't be granted.
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def allow(verbs, objects = nil, scope = true)
|
34
|
+
expand_permissions(verbs).each do |verb|
|
35
|
+
[objects].flatten.each do |object|
|
36
|
+
@permissions ||= {}
|
37
|
+
@permissions[[verb, object]] = scope
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def allowed_scope(model, verb = nil)
|
43
|
+
verb ||= :read
|
44
|
+
|
45
|
+
if p = permissions[[verb, model]]
|
46
|
+
case p
|
47
|
+
when Hash, String, Array then model.where(p)
|
48
|
+
when Proc then p.call(model)
|
49
|
+
else model.scoped
|
50
|
+
end
|
51
|
+
else
|
52
|
+
model.where('1=0') # TODO: replace this with .none once available
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def expand_permissions(*perms)
|
59
|
+
perms.flatten.map do |p|
|
60
|
+
case p
|
61
|
+
when :manage then [:manage, :index, :show, :new, :create, :edit, :update, :destroy]
|
62
|
+
when :create then [:create, :new]
|
63
|
+
when :read then [:read, :index, :show]
|
64
|
+
when :update then [:update, :edit]
|
65
|
+
else p
|
66
|
+
end
|
67
|
+
end.flatten
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/allowance/version.rb
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "a class with the Allowance::Subject mixin" do
|
4
|
+
let(:some_class) { Class.new }
|
5
|
+
let(:some_other_class) { Class.new }
|
6
|
+
|
7
|
+
subject do
|
8
|
+
Class.new do
|
9
|
+
include Allowance::Subject
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has an #allow method that allows setting of permissions" do
|
14
|
+
subject.allow :read
|
15
|
+
insist subject.allowed? :read
|
16
|
+
refuse subject.allowed? :manage
|
17
|
+
end
|
18
|
+
|
19
|
+
it "automatically runs the #define_permissions method" do
|
20
|
+
subject.class.class_eval do
|
21
|
+
def define_permissions
|
22
|
+
allow :manage
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
insist subject.allowed? :manage
|
27
|
+
end
|
28
|
+
|
29
|
+
it "supports verbs and objects" do
|
30
|
+
subject.allow :update, some_class
|
31
|
+
|
32
|
+
insist subject.allowed? :update, some_class
|
33
|
+
refuse subject.allowed? :destroy, some_class
|
34
|
+
refuse subject.allowed? :update, some_other_class
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'verb expansion' do
|
38
|
+
it "expands :read to include :index and :show" do
|
39
|
+
subject.allow :read, some_class
|
40
|
+
|
41
|
+
insist subject.allowed? :read, some_class
|
42
|
+
insist subject.allowed? :index, some_class
|
43
|
+
insist subject.allowed? :show, some_class
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should verify permissions against model instances" do
|
48
|
+
model_class = Class.new
|
49
|
+
model_class.stub(model_name: 'Model')
|
50
|
+
model_class.should_receive(:some_scope).and_return(model_class)
|
51
|
+
|
52
|
+
model_instance = model_class.new
|
53
|
+
model_instance.stub!(:id => 123)
|
54
|
+
|
55
|
+
model_class.should_receive(:exists?).with(model_instance).and_return(true)
|
56
|
+
|
57
|
+
subject.allow :read, model_class, lambda { |r| r.some_scope }
|
58
|
+
|
59
|
+
insist subject.allowed? :read, model_instance
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#allowed_scope" do
|
63
|
+
let(:model) { double }
|
64
|
+
let(:allowed_scope) { double }
|
65
|
+
|
66
|
+
it "allows scopes to be defined through lambdas" do
|
67
|
+
subject.allow :read, model, ->(m) { m.some_scope }
|
68
|
+
model.should_receive(:some_scope).and_return(allowed_scope)
|
69
|
+
subject.allowed_scope(model, :read).should == allowed_scope
|
70
|
+
end
|
71
|
+
|
72
|
+
it "allows scopes to be defined through a conditions hash" do
|
73
|
+
subject.allow :read, model, :awesome => true
|
74
|
+
model.should_receive(:where).with(:awesome => true).and_return(allowed_scope)
|
75
|
+
subject.allowed_scope(model, :read).should == allowed_scope
|
76
|
+
end
|
77
|
+
|
78
|
+
it "allows scopes to be defined through a conditions string" do
|
79
|
+
subject.allow :read, model, "awesome = true"
|
80
|
+
model.should_receive(:where).with("awesome = true").and_return(allowed_scope)
|
81
|
+
subject.allowed_scope(model, :read).should == allowed_scope
|
82
|
+
end
|
83
|
+
|
84
|
+
it "allow scopes to be defined through a conditions array" do
|
85
|
+
subject.allow :read, model, ["awesome = ?", true]
|
86
|
+
model.should_receive(:where).with(["awesome = ?", true]).and_return(allowed_scope)
|
87
|
+
subject.allowed_scope(model, :read).should == allowed_scope
|
88
|
+
end
|
89
|
+
|
90
|
+
it "prevents access to models that have on permissions defined" do
|
91
|
+
model.should_receive(:where).with("1=0").and_return(allowed_scope)
|
92
|
+
subject.allowed_scope(model, :read).should == allowed_scope
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
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.
|
4
|
+
version: 0.2.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-
|
12
|
+
date: 2012-12-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rspec
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: watchr
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description: A general-use permission management library with support for ActiveModel.
|
48
63
|
email:
|
49
64
|
- hendrik@mans.de
|
@@ -56,16 +71,19 @@ files:
|
|
56
71
|
- .rspec
|
57
72
|
- .travis.yml
|
58
73
|
- .watchr
|
74
|
+
- CHANGES.md
|
59
75
|
- Gemfile
|
76
|
+
- LICENSE
|
60
77
|
- README.md
|
61
78
|
- Rakefile
|
62
79
|
- allowance.gemspec
|
63
80
|
- lib/allowance.rb
|
64
|
-
- lib/allowance/
|
81
|
+
- lib/allowance/activerecord_ext.rb
|
82
|
+
- lib/allowance/subject.rb
|
65
83
|
- lib/allowance/version.rb
|
66
|
-
- spec/
|
67
|
-
- spec/permissions_spec.rb
|
84
|
+
- spec/activerecord_ext_spec.rb
|
68
85
|
- spec/spec_helper.rb
|
86
|
+
- spec/subject_spec.rb
|
69
87
|
homepage: https://github.com/hmans/allowance
|
70
88
|
licenses: []
|
71
89
|
post_install_message:
|
@@ -86,12 +104,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
104
|
version: '0'
|
87
105
|
requirements: []
|
88
106
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.8.
|
107
|
+
rubygems_version: 1.8.23
|
90
108
|
signing_key:
|
91
109
|
specification_version: 3
|
92
110
|
summary: A general-use permission management library with support for ActiveModel.
|
93
111
|
test_files:
|
94
|
-
- spec/
|
95
|
-
- spec/permissions_spec.rb
|
112
|
+
- spec/activerecord_ext_spec.rb
|
96
113
|
- spec/spec_helper.rb
|
114
|
+
- spec/subject_spec.rb
|
97
115
|
has_rdoc:
|
@@ -1,69 +0,0 @@
|
|
1
|
-
module Allowance
|
2
|
-
class Permissions
|
3
|
-
def initialize
|
4
|
-
@permissions = {}
|
5
|
-
yield(self) if block_given?
|
6
|
-
end
|
7
|
-
|
8
|
-
def allowed?(verb, object = nil)
|
9
|
-
return true if @permissions[[verb, object]]
|
10
|
-
|
11
|
-
# If object is a resource instance, try its class
|
12
|
-
if object.class.respond_to?(:find)
|
13
|
-
if allowed?(verb, object.class)
|
14
|
-
# See if the object is part of the defined scope
|
15
|
-
return !scoped_model(verb, object.class).
|
16
|
-
find(:first, :conditions => { :id => object.id }).nil?
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
false
|
21
|
-
end
|
22
|
-
|
23
|
-
alias_method :can?, :allowed?
|
24
|
-
|
25
|
-
def allow!(verbs, objects = nil, scope = true, &blk)
|
26
|
-
expand_permissions(verbs).each do |verb|
|
27
|
-
[objects].flatten.each do |object|
|
28
|
-
@permissions[[verb, object]] = scope # TODO: add blk, too
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
alias_method :can!, :allow!
|
34
|
-
|
35
|
-
def method_missing(name, *args, &blk)
|
36
|
-
if name.to_s =~ /(.+)!$/
|
37
|
-
allow!($1.to_sym, *args, &blk)
|
38
|
-
elsif name.to_s =~ /(.+)\?$/
|
39
|
-
allowed?($1.to_sym, *args, &blk)
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def scoped_model(verb, model)
|
46
|
-
if p = @permissions[[verb, model]]
|
47
|
-
case p
|
48
|
-
when Hash, String, Array then model.where(p)
|
49
|
-
when Proc then p.call(model)
|
50
|
-
else model
|
51
|
-
end
|
52
|
-
else
|
53
|
-
model.where(false)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def expand_permissions(*permissions)
|
60
|
-
permissions.flatten.map do |p|
|
61
|
-
case p
|
62
|
-
when :manage then [:manage, :index, :show, :new, :create, :edit, :update, :destroy]
|
63
|
-
when :view then [:view, :index, :show]
|
64
|
-
else p
|
65
|
-
end
|
66
|
-
end.flatten
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/spec/allowance_spec.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Allowance
|
4
|
-
describe "#define" do
|
5
|
-
it "should return an instance of Allowance::Permissions" do
|
6
|
-
Allowance.define.should be_kind_of(Allowance::Permissions)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should make sure the passed block is executed" do
|
10
|
-
p = Allowance.define do |allow|
|
11
|
-
allow.sing!
|
12
|
-
end
|
13
|
-
|
14
|
-
insist p.sing?
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/spec/permissions_spec.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Allowance
|
4
|
-
describe Permissions do
|
5
|
-
SomeClass = Class.new
|
6
|
-
SomeOtherClass = Class.new
|
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
|
-
|
16
|
-
it "should allow simple permissions to be specified" do
|
17
|
-
subject.moo!
|
18
|
-
|
19
|
-
insist subject.moo?
|
20
|
-
refuse subject.quack?
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should support a block-style initialization" do
|
24
|
-
p = Permissions.new do |can|
|
25
|
-
can.sing!
|
26
|
-
end
|
27
|
-
|
28
|
-
insist p.allowed? :sing
|
29
|
-
insist p.sing?
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should not modify the block's scope" do
|
33
|
-
yup = true
|
34
|
-
nope = false
|
35
|
-
|
36
|
-
p = Permissions.new do |can|
|
37
|
-
can.sing! if yup
|
38
|
-
can.dance! if nope
|
39
|
-
end
|
40
|
-
|
41
|
-
insist p.sing?
|
42
|
-
refuse p.dance?
|
43
|
-
end
|
44
|
-
|
45
|
-
it "should allow verbs and objects" do
|
46
|
-
subject.update! SomeClass
|
47
|
-
|
48
|
-
insist subject.update? SomeClass
|
49
|
-
refuse subject.destroy? SomeClass
|
50
|
-
refuse subject.update? SomeOtherClass
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should expand :view to include :index and :show" do
|
54
|
-
subject.view! SomeClass
|
55
|
-
|
56
|
-
insist subject.view? SomeClass
|
57
|
-
insist subject.index? SomeClass
|
58
|
-
insist subject.show? SomeClass
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should verify permissions against model instances" do
|
62
|
-
model_class = Class.new
|
63
|
-
model_class.should_receive(:some_scope).and_return(model_class)
|
64
|
-
|
65
|
-
model_instance = model_class.new
|
66
|
-
model_instance.stub!(:id => 123)
|
67
|
-
|
68
|
-
model_class.should_receive(:find).and_return(model_instance)
|
69
|
-
|
70
|
-
subject.view! model_class, lambda { |r| r.some_scope }
|
71
|
-
|
72
|
-
insist subject.view? model_instance
|
73
|
-
end
|
74
|
-
|
75
|
-
describe "#scoped_model" do
|
76
|
-
it "should allow scopes to be defined through lambdas" do
|
77
|
-
model = mock
|
78
|
-
model.should_receive(:some_scope).and_return(scoped_model = mock)
|
79
|
-
|
80
|
-
subject.view! model, lambda { |r| r.some_scope }
|
81
|
-
subject.scoped_model(:view, model).should == scoped_model
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should allow scopes to be defined through a conditions hash" do
|
85
|
-
model = mock
|
86
|
-
model.should_receive(:where).with(:awesome => true).and_return(scoped_model = mock)
|
87
|
-
|
88
|
-
subject.view! model, :awesome => true
|
89
|
-
|
90
|
-
subject.scoped_model(:view, model).should == scoped_model
|
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
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|