allowance 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
6
- - jruby-18mode # JRuby in 1.9 mode
7
- - rbx-18mode
8
- - jruby-19mode # JRuby in 1.9 mode
9
- - rbx-19mode
5
+ # - jruby-19mode # JRuby in 1.9 mode
6
+ # - rbx-19mode
10
7
  script: bundle exec rake
@@ -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 ActiveModel-compliant classes.**
5
+ for ActiveRecord models.**
6
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.
7
+ Key features are:
9
8
 
10
- A simple Example:
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
- ``` ruby
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
- # Allow creating new Article instances
18
- can.create! Article
15
+ ### A simple example:
19
16
 
20
- # Allow the user to edit Article instances that belong to him
21
- can.edit! Article, :author_id => current_user.id
17
+ ~~~ ruby
18
+ class User < ActiveRecord::Base
19
+ include Allowance::Subject
22
20
 
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]
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:
21
+ def define_permissions
22
+ # every user can see all other users
23
+ allow :read, User
31
24
 
32
- ``` ruby
33
- p.login? # true
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
- You can use the same object to provide you with correctly scoped models, too:
28
+ # users can edit/delete their own posts
29
+ allow :manage, Post, user_id: id
39
30
 
40
- ``` ruby
41
- p.scoped_model(:view, Article).all
42
- # -> Article.where(['published = ? OR author_id = ?', true, current_user.id]).all
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
- ### Requirements
43
+ Simply add `allowance` to your project's `Gemfile` or install it through `gem install allowance`.
50
44
 
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.
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
  [![Build Status](https://secure.travis-ci.org/hmans/allowance.png?branch=master)](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
- Use `Allowance.define` to create a new permissions object, then use its `allow!` or `can!`
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
- ``` ruby
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
- Instead of using `allow!` or `can!`, you can just name the permission directly:
58
+ ~~~ ruby
59
+ class User
60
+ include Allowance::Subject
61
+ end
62
+ ~~~
92
63
 
93
- ``` ruby
94
- p.sing!
95
- ```
64
+ Instances of this class can now be configured with permissions using the `#allow` method:
96
65
 
97
- You can also specify permissions as a block:
66
+ ~~~ ruby
67
+ @user.allow :read, Post
68
+ @user.allow :manage, Post, user_id: @user.id
69
+ ~~~
98
70
 
99
- ``` ruby
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
- ### Querying permissions
73
+ ~~~ ruby
74
+ if @user.allowed?(:destroy, @post)
75
+ @post.destroy
76
+ end
77
+ ~~~
106
78
 
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:
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
- ``` ruby
111
- p.allowed? :sing
112
- p.can? :sing
113
- p.sing?
114
- ```
81
+ ~~~ ruby
82
+ class User
83
+ include Allowance::Subject
115
84
 
116
- ### One-dimensional permissions
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
- ``` ruby
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
- ### Two-dimensional permissions
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
- 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:
97
+ First of all, add the `Allowance::Subject` mixin to your `User` class and create a `define_permissions` method:
133
98
 
134
- ``` ruby
135
- p = Allowance.define do |allow|
136
- allow.view! Article
99
+ ~~~ ruby
100
+ class User < ActiveRecord::Base
101
+ include Allowance::Subject
137
102
 
138
- if current_user.is_admin?
139
- allow.edit! Article
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
- When querying for permissions, just pass an object as an additional parameter:
122
+ # Make it available to your views, too.
123
+ #
124
+ helper_method :current_user
145
125
 
146
- ``` ruby
147
- p.edit? Article
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
- 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:
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
- 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.
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
- ### Defining model scopes
142
+ ~~~ ruby
143
+ # Can read all posts
144
+ allow :read, Post
161
145
 
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:
146
+ # Can only read posts I've created
147
+ allow :read, Post, user_id: self.id
163
148
 
164
- ``` ruby
165
- p = Allowance.define do |allow|
166
- allow.view! Article, :published => true
149
+ # Can only read published posts
150
+ allow :read, Post, Post.published
167
151
 
168
- if current_user.is_admin?
169
- allow.view! Article
170
- end
171
- end
172
- ```
152
+ # You can also provide strings...
153
+ allow :read, Post, "published_at IS NOT NULL"
173
154
 
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.
155
+ # ...or lambdas:
156
+ allow :read, Post, ->(p) { p.where("published_at < ?", 2.weeks.ago) }
157
+ ~~~
175
158
 
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:
159
+ When checking permissions against a model instance, Allowance will check if it's part of the allowed scope:
177
160
 
178
- ``` ruby
179
- p.view! Article, ['published = ? OR user_id = ?', true, current_user.id]
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
- Lastly, you're encouraged to re-use scopes defined on the model class. Just define your scope using a lambda:
166
+ allowed?(:read, @post)
167
+ ~~~
183
168
 
184
- ``` ruby
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
- 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:
171
+ ~~~ ruby
172
+ @posts = current_user.allowed_scope(Post, :read)
173
+ ~~~
189
174
 
190
- ``` ruby
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
- ### Defining contextual permissions
177
+ ~~~ ruby
178
+ @posts = Post.allowed(current_user, :read)
179
+ ~~~
195
180
 
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:
181
+ This becomes handy in Rails controllers:
199
182
 
200
- ``` ruby
201
- permissions = Allowance.define do |allow|
202
- allow.read! Article
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
- if current_user.is_admin?
205
- allow.destroy! Article
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
- ``` 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
- ```
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
 
@@ -1,8 +1,9 @@
1
+ module Allowance
2
+ end
3
+
1
4
  require 'allowance/version'
2
- require 'allowance/permissions'
5
+ require 'allowance/subject'
3
6
 
4
- module Allowance
5
- def self.define(&blk)
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
@@ -1,3 +1,3 @@
1
1
  module Allowance
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ActiveRecord extensions' do
4
+ describe '#allowed' do
5
+ pending
6
+ end
7
+ end
@@ -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.1.1
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-05-29 00:00:00.000000000 Z
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: &70166894971300 !ruby/object:Gem::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: *70166894971300
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: &70166894970560 !ruby/object:Gem::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: *70166894970560
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: &70166894967440 !ruby/object:Gem::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: *70166894967440
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/permissions.rb
81
+ - lib/allowance/activerecord_ext.rb
82
+ - lib/allowance/subject.rb
65
83
  - lib/allowance/version.rb
66
- - spec/allowance_spec.rb
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.11
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/allowance_spec.rb
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
@@ -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
@@ -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