permissive 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{permissive}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Flip Sasser", "Simon Parsons"]
12
+ s.date = %q{2009-11-01}
13
+ s.description = %q{Permissive combines a model-based permissions system with bitmasking to
14
+ create a flexible approach to maintaining permissions on your ActiveRecord
15
+ models. It supports an easy-to-use set of methods for accessing and
16
+ determining permissions, including some fun metaprogramming.}
17
+ s.email = %q{flip@x451.com}
18
+ s.extra_rdoc_files = [
19
+ "README.markdown"
20
+ ]
21
+ s.files = [
22
+ "VERSION"
23
+ ]
24
+ s.homepage = %q{http://github.com/flipsasser/permissive}
25
+ s.rdoc_options = ["--charset=UTF-8"]
26
+ s.require_paths = ["lib"]
27
+ s.rubygems_version = %q{1.3.5}
28
+ s.summary = %q{Permissive gives your ActiveRecord models granular permission support}
29
+ s.test_files = [
30
+ "spec/acts_as_permissive_spec.rb",
31
+ "spec/permissions_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+
35
+ if s.respond_to? :specification_version then
36
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
37
+ s.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
40
+ else
41
+ end
42
+ else
43
+ end
44
+ end
45
+
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ pkg/
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Flip Sasser
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -13,7 +13,7 @@ Installation
13
13
  `gem install permissive`
14
14
 
15
15
  or as a plugin:
16
-
16
+
17
17
  `script/plugin install git://github.com/flipsasser/permissive.git`
18
18
 
19
19
  2. Generate a migration so you can get some sweet table action:
@@ -30,20 +30,20 @@ First, define a few permissions constants. We'll define them in `Rails.root/conf
30
30
  Permission constants need to be int values counting up from zero. We use ints because Permissive uses bit masking to keep permissions data compact and performant.
31
31
 
32
32
  module Permissive::Permissions
33
- MANAGE_GAMES = 0
34
- CONTROL_RIDES = 1
35
- PUNCH = 2
33
+ MANAGE_GAMES = 0
34
+ CONTROL_RIDES = 1
35
+ PUNCH = 2
36
36
  end
37
37
 
38
38
  And that's all it takes to configure permissions! Now that we have them, let's grant them to a model or two:
39
39
 
40
40
  class Employee < ActiveRecord::Base
41
- acts_as_permissive
42
- validates_presence_of :first_name, :last_name
41
+ acts_as_permissive
42
+ validates_presence_of :first_name, :last_name
43
43
  end
44
44
 
45
45
  class Company < ActiveRecord::Base
46
- validates_presence_of :name
46
+ validates_presence_of :name
47
47
  end
48
48
 
49
49
  Easy-peasy, right? Let's try granting a few permissions:
@@ -98,7 +98,7 @@ Scoping
98
98
  Permissive supports scoping at the class-configuration level, which adds relationships to permitted objects:
99
99
 
100
100
  class Employee < ActiveRecord::Base
101
- acts_as_permissive :scope => :company
101
+ acts_as_permissive :scope => :company
102
102
  end
103
103
 
104
104
  @frigo.permissive_companies #=> [Company 1, Company 2]
@@ -116,10 +116,10 @@ Next Steps
116
116
  There's a number of things I want to add to the permissive settings. At the moment, Permissive currently support scoping at the class level, BUT all it really does is add a `has_many` relationship. `@employee.can!(:do_anything)` will still work, as will `@employee.can!(:do_something, :on => @something_that_isnt_a_company)`. That's pretty confusing to me. Adding more granular permissions might be cooler:
117
117
 
118
118
  class Employee < ActiveRecord::Base
119
- has_permissions do
120
- on :companies
121
- on :employees
122
- end
119
+ has_permissions do
120
+ on :companies
121
+ on :employees
122
+ end
123
123
  end
124
124
 
125
125
  which might yield something like
@@ -154,10 +154,10 @@ Next up! I currently use a manual reset to grant permissions through a controlle
154
154
  .. and in the controller:
155
155
 
156
156
  def update
157
- @employee.can!(params[:employees].delete(:permissions), :revert => true)
158
- respond_to do |format|
159
- ...
160
- end
157
+ @employee.can!(params[:employees].delete(:permissions), :revert => true)
158
+ respond_to do |format|
159
+ ...
160
+ end
161
161
  end
162
162
 
163
163
  Finally, I'd like to use the `grant_mask` support that exists on the Permissive::Permission model to control what people can or cannot allow others to do. This would necessitate one of two things - first, a quick way of iterating over a person's granting permissions, e.g.:
@@ -169,11 +169,11 @@ Finally, I'd like to use the `grant_mask` support that exists on the Permissive:
169
169
  and second, write-time checking of grantor permissions. Something like this, maybe:
170
170
 
171
171
  def update
172
- current_user.grant(params[:employees][:permissions], :to => @employee)
172
+ current_user.grant(params[:employees][:permissions], :to => @employee)
173
173
  end
174
174
 
175
175
  which would allow the Permissive::Permission model to make sure whatever `current_user` is granting to @employee, they're **allowed** to grant to @employee.
176
176
 
177
177
  And that's it! Like all of my projects, I extracted it from some live development - which means it, too, is still in development. So please feel free to contribute!
178
178
 
179
- Copyright (c) 2009 Flip Sasser, released under the MIT license
179
+ Copyright (c) 2009 Flip Sasser & Simon Parsons, released under the MIT license
@@ -0,0 +1,191 @@
1
+ <h1>Permissive gives your ActiveRecord models granular permission support</h1>
2
+
3
+ <p>Permissive combines a model-based permissions system with bitmasking to
4
+ create a flexible approach to maintaining permissions on your ActiveRecord
5
+ models. It supports an easy-to-use set of methods for accessing and
6
+ determining permissions, including some fun metaprogramming.</p>
7
+
8
+ <h2>Installation</h2>
9
+
10
+ <ol>
11
+ <li><p>Get yourself some code. You can install as a gem:</p>
12
+
13
+ <p><code>gem install permissive</code></p>
14
+
15
+ <p>or as a plugin:</p>
16
+
17
+ <p><code>script/plugin install git://github.com/flipsasser/permissive.git</code></p></li>
18
+ <li><p>Generate a migration so you can get some sweet table action:</p>
19
+
20
+ <p><code>script/generate permissive_migration</code></p>
21
+
22
+ <p><code>rake db:migrate</code></p></li>
23
+ </ol>
24
+
25
+
26
+ <h2>Usage</h2>
27
+
28
+ <p>First, define a few permissions constants. We'll define them in <code>Rails.root/config/initializers/permissive.rb</code>. The best practice is to name them in a verb format that follows this pattern: "Object can <code>DO_PERMISSION_NAME</code>".</p>
29
+
30
+ <p>Permission constants need to be int values counting up from zero. We use ints because Permissive uses bit masking to keep permissions data compact and performant.</p>
31
+
32
+ <pre><code>module Permissive::Permissions
33
+ MANAGE_GAMES = 0
34
+ CONTROL_RIDES = 1
35
+ PUNCH = 2
36
+ end
37
+ </code></pre>
38
+
39
+ <p>And that's all it takes to configure permissions! Now that we have them, let's grant them to a model or two:</p>
40
+
41
+ <pre><code>class Employee &lt; ActiveRecord::Base
42
+ acts_as_permissive
43
+ validates_presence_of :first_name, :last_name
44
+ end
45
+
46
+ class Company &lt; ActiveRecord::Base
47
+ validates_presence_of :name
48
+ end
49
+ </code></pre>
50
+
51
+ <p>Easy-peasy, right? Let's try granting a few permissions:</p>
52
+
53
+ <pre><code>@james = Employee.create(:first_name =&gt; 'James', :last_name =&gt; 'Brennan')
54
+ @frigo = Employee.create(:first_name =&gt; 'Tommy', :last_name =&gt; 'Frigo')
55
+ @adventureland = Company.create(:name =&gt; 'Adventureland')
56
+
57
+ # Okay, let's do some granting. We'll start by scoping to a specific company.
58
+ @james.can!(:manage_games, :on =&gt; @adventureland)
59
+
60
+ # Now let's do some permission checking.
61
+ @james.can?(:manage_games, :on =&gt; @adventureland) #=&gt; true
62
+
63
+ # We can also use the metaprogramming syntax:
64
+ @james.can_manage_games_on?(@adventureland) #=&gt; true
65
+ @james.can_control_rides_on?(@adventureland) #=&gt; false
66
+
67
+ # We can check for multiple permissions, too:
68
+ @james.can?(:manage_games, :control_rides) #=&gt; false
69
+ # OR:
70
+ @james.can_manage_games_and_control_rides?
71
+
72
+ # Scoping can be done through any object
73
+ @frigo.can!(:punch, :on =&gt; @james)
74
+ @frigo.can_punch_on?(@james) #=&gt; true
75
+
76
+ # And the permissions aren't reciprocal
77
+ @james.can_punch_on?(@frigo) #=&gt; false
78
+
79
+ # Of course, we can grant global (non-scoped) permissions, too:
80
+ @frigo.can!(:control_rides)
81
+ @frigo.can_control_rides? #=&gt; true
82
+
83
+ # BUT! Global permissions don't override scoped permissions.
84
+ @frigo.can_control_rides_on?(@adventureland) #=&gt; false
85
+
86
+ # Likewise, scoped permissions don't bubble up globally:
87
+ @james.can_manage_games? #=&gt; false
88
+
89
+ # And, last but not least, let's take all of those great permissions away:
90
+ @james.revoke(:manage_games, :on =&gt; @adventureland)
91
+
92
+ # We can revoke all permissions, in any scope, too:
93
+ @frigo.revoke(:all)
94
+ </code></pre>
95
+
96
+ <p>And that's it!</p>
97
+
98
+ <h2>Scoping</h2>
99
+
100
+ <p>Permissive supports scoping at the class-configuration level, which adds relationships to permitted objects:</p>
101
+
102
+ <pre><code>class Employee &lt; ActiveRecord::Base
103
+ acts_as_permissive :scope =&gt; :company
104
+ end
105
+
106
+ @frigo.permissive_companies #=&gt; [Company 1, Company 2]
107
+ </code></pre>
108
+
109
+ <h2>Replacing Permissions</h2>
110
+
111
+ <p>Sometimes you want to overwrite all previous permissions in a can! method. That's pretty easy: just add :reset => true to the options.</p>
112
+
113
+ <pre><code>@frigo.can!(:control_rides, :on =&gt; @adventureland, :reset =&gt; true)
114
+ </code></pre>
115
+
116
+ <h2>Next Steps</h2>
117
+
118
+ <p>There's a number of things I want to add to the permissive settings. At the moment, Permissive currently support scoping at the class level, BUT all it really does is add a <code>has_many</code> relationship. <code>@employee.can!(:do_anything)</code> will still work, as will <code>@employee.can!(:do_something, :on =&gt; @something_that_isnt_a_company)</code>. That's pretty confusing to me. Adding more granular permissions might be cooler:</p>
119
+
120
+ <pre><code>class Employee &lt; ActiveRecord::Base
121
+ has_permissions do
122
+ on :companies
123
+ on :employees
124
+ end
125
+ end
126
+ </code></pre>
127
+
128
+ <p>which might yield something like</p>
129
+
130
+ <pre><code>@employee.permissive_companies
131
+ # and
132
+ @employee.can_control_rides_in_company @adventureland
133
+ </code></pre>
134
+
135
+ <p>I'd also like to support a more intelligent grammar:</p>
136
+
137
+ <pre><code>@james.can_punch? @frigo
138
+ @frigo.can!(:control_rides, :in =&gt; @adventureland)
139
+ </code></pre>
140
+
141
+ <p>Meta-programmed methods for granting and revoking would be cool, too:</p>
142
+
143
+ <pre><code>@james.can_punch! @frigo
144
+ @frigo.cannot_control_rides_in! @adventureland
145
+ </code></pre>
146
+
147
+ <p>And while we're on the subject of metaprogramming, let's add some OR-ing to the whole thing:</p>
148
+
149
+ <pre><code>@james.can_control_rides_or_manage_games_in? @adventureland
150
+ </code></pre>
151
+
152
+ <p>I'd also like to enable Permissive::Templates (pre-set permission groups, like roles):</p>
153
+
154
+ <pre><code>administrator = Permissive::Template.named('Administrator')
155
+ @james.acts_like administrator
156
+ </code></pre>
157
+
158
+ <p>Next up! I currently use a manual reset to grant permissions through a controller. It would by great to DRY this stuff up and provide some decent path for moving permissions into HTML forms. Right now, it looks something like this:</p>
159
+
160
+ <pre><code>&lt;%= check_box_tag("employee[permissions][]", Permissive::Permissions::CONTROL_RIDES, @employee.can_control_rides?) %&gt; Control rides
161
+ </code></pre>
162
+
163
+ <p>.. and in the controller:</p>
164
+
165
+ <pre><code>def update
166
+ @employee.can!(params[:employees].delete(:permissions), :revert =&gt; true)
167
+ respond_to do |format|
168
+ ...
169
+ end
170
+ end
171
+ </code></pre>
172
+
173
+ <p>Finally, I'd like to use the <code>grant_mask</code> support that exists on the Permissive::Permission model to control what people can or cannot allow others to do. This would necessitate one of two things - first, a quick way of iterating over a person's granting permissions, e.g.:</p>
174
+
175
+ <pre><code>&lt;% current_user.grant_permissions.each do |permission| %&gt;
176
+ &lt;!-- Do something! --&gt;
177
+ &lt;% end %&gt;
178
+ </code></pre>
179
+
180
+ <p>and second, write-time checking of grantor permissions. Something like this, maybe:</p>
181
+
182
+ <pre><code>def update
183
+ current_user.grant(params[:employees][:permissions], :to =&gt; @employee)
184
+ end
185
+ </code></pre>
186
+
187
+ <p>which would allow the Permissive::Permission model to make sure whatever <code>current_user</code> is granting to @employee, they're <strong>allowed</strong> to grant to @employee.</p>
188
+
189
+ <p>And that's it! Like all of my projects, I extracted it from some live development - which means it, too, is still in development. So please feel free to contribute!</p>
190
+
191
+ <p>Copyright (c) 2009 Flip Sasser, released under the MIT license</p>
@@ -0,0 +1,36 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc 'Run the specs'
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ namespace :spec do
14
+ desc 'Run the specs with Rcov output'
15
+ Spec::Rake::SpecTask.new(:rcov) do |t|
16
+ t.spec_files = FileList['spec/**/*.rb']
17
+ t.rcov = true
18
+ t.rcov_opts = ['--exclude', 'examples']
19
+ end
20
+ end
21
+
22
+ begin
23
+ require 'jeweler'
24
+ Jeweler::Tasks.new do |gemspec|
25
+ gemspec.name = "permissive"
26
+ gemspec.summary = "Permissive gives your ActiveRecord models granular permission support"
27
+ gemspec.description = %{Permissive combines a model-based permissions system with bitmasking to
28
+ create a flexible approach to maintaining permissions on your ActiveRecord
29
+ models. It supports an easy-to-use set of methods for accessing and
30
+ determining permissions, including some fun metaprogramming.}
31
+ gemspec.email = "flip@x451.com"
32
+ gemspec.homepage = "http://github.com/flipsasser/permissive"
33
+ gemspec.authors = ["Flip Sasser", "Simon Parsons"]
34
+ end
35
+ rescue LoadError
36
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.0.1
@@ -0,0 +1,7 @@
1
+ Usage:
2
+
3
+ script/generate permissive_migration
4
+
5
+ This will create a migration that will add the Permissive tables to your
6
+ application. You can modify this migration to your heart's content. When
7
+ you're done, run `rake db:migrate` and Permissive will be ready to go!
@@ -0,0 +1,7 @@
1
+ class PermissiveMigrationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'permissive_migration.rb', File.join('db', 'migrate'), :migration_file_name => 'install_permissive'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ class InstallPermissive < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :permissive_permissions do |t|
4
+ t.integer :permitted_object_id
5
+ t.string :permitted_object_type, :limit => 32
6
+ t.integer :scoped_object_id
7
+ t.string :scoped_object_type, :limit => 32
8
+ t.integer :mask, :default => 0
9
+ t.integer :grant_mask, :default => 0
10
+ end
11
+ add_index :permissive_permissions, [:permitted_object_id, :permitted_object_type], :name => 'permissive_permitted'
12
+ add_index :permissive_permissions, [:scoped_object_id, :scoped_object_type], :name => 'permissive_scoped'
13
+ add_index :permissive_permissions, :mask, :name => 'permissive_masks'
14
+ add_index :permissive_permissions, :grant_mask, :name => 'permissive_grant_masks'
15
+ end
16
+
17
+ def self.down
18
+ drop_table :permissive_permissions
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require 'permissive/permission'
2
+ require 'permissive/acts_as_permissive'
3
+ require 'permissive/permissions'
4
+
5
+ module Permissive
6
+ @@strong = false
7
+
8
+ def self.strong
9
+ @@strong
10
+ end
11
+
12
+ def self.strong=(new_strong)
13
+ @@strong = !!new_strong
14
+ end
15
+ end
@@ -0,0 +1,134 @@
1
+ module Permissive
2
+ module ActsAsPermissive
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # This is the core of the Permissive module. It allows you to define a
6
+ # permissive model structure complete with :scope. This will dynamically
7
+ # generate scoped, polymorphic relationships across one or more models.
8
+ def self.acts_as_permissive(options = {})
9
+ options.assert_valid_keys(:scope)
10
+ has_many :permissions, :class_name => 'Permissive::Permission', :as => :permitted_object do
11
+ def can!(*args)
12
+ options = args.last.is_a?(Hash) ? args.pop : {}
13
+ options.assert_valid_keys(:on, :reset)
14
+ if options[:on]
15
+ permission = proxy_owner.permissions.find_or_initialize_by_scoped_object_id_and_scoped_object_type(options[:on].id, options[:on].class.to_s)
16
+ else
17
+ permission = Permissive::Permission.find_or_initialize_by_permitted_object_id_and_permitted_object_type(proxy_owner.id, proxy_owner.class.to_s)
18
+ end
19
+ if options[:reset]
20
+ permission.mask = 0
21
+ permission.grant_mask = 0
22
+ end
23
+ args.flatten.each do |name|
24
+ bit = bit_for(name)
25
+ unless permission.mask & bit != 0
26
+ permission.mask = permission.mask | bit
27
+ end
28
+ end
29
+ permission.save!
30
+ end
31
+
32
+ def can?(*args)
33
+ options = args.last.is_a?(Hash) ? args.pop : {}
34
+ bits = args.map{|name| bit_for(name) }
35
+ # scope = nil
36
+ # if options[:on]
37
+ # scope = scoped(:conditions => ['scoped_object_id = ? AND scoped_object_type = ?', options[:on].id, options[:on].class.to_s])
38
+ # else
39
+ # scope = scoped(:conditions => ['scoped_object_id IS NULL AND scoped_object_type IS NULL'])
40
+ # end
41
+ # Skip the trip to the database if the proxy has been loaded up already...
42
+ # TODO: Fix this per-scope ... somehow ... probably beyond the scope of this project.
43
+ # if @loaded
44
+ # bits.all?{|bit| self.select{|permission| permission.mask & bit != 0} }
45
+ # else
46
+ on(options[:on]).count(:conditions => [bits.map { 'permissive_permissions.mask & ?' }.join(' AND '), *bits]) > 0
47
+ # end
48
+ end
49
+
50
+ def revoke(*args)
51
+ options = args.last.is_a?(Hash) ? args.pop : {}
52
+ if args.length == 1 && args.first == :all
53
+ on(options[:on]).destroy_all
54
+ else
55
+ bits = args.map{|name| bit_for(name) }
56
+ on(options[:on]).each do |permission|
57
+ bits.each do |bit|
58
+ if permission.mask & bit
59
+ permission.mask = permission.mask ^ bit
60
+ end
61
+ end
62
+ permission.save!
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ if options[:scope]
69
+ scope_name = "permissive_#{options[:scope].to_s}"
70
+ unless reflection = reflect_on_association(scope_name)
71
+ # TODO: There's just no way this should be working. It's WAY too
72
+ # fragile. We need support for something more intelligent here,
73
+ # like an options hash that includes :scope_type.
74
+ namespace = self.to_s.split('::')
75
+ if namespace.length > 1
76
+ namespace.pop
77
+ class_name = namespace.join('::')
78
+ else
79
+ class_name = ''
80
+ end
81
+ class_name << "::#{options[:scope].to_s.classify}"
82
+ has_many scope_name, :through => :permissions, :source => :scoped_object, :source_type => class_name
83
+ end
84
+ end
85
+
86
+ class_eval do
87
+ # Pass calls to the instance down to its permissions collection
88
+ # e.g. current_user.can(:view_comments) will bubble to
89
+ # current_user.permissions.can(:view_comments)
90
+ def can!(*args)
91
+ permissions.can!(*args)
92
+ end
93
+
94
+ # Pass calls to the instance down to its permissions collection
95
+ # e.g. current_user.can(:view_comments) will bubble to
96
+ # current_user.permissions.can(:view_comments)
97
+ def can?(*args)
98
+ permissions.can?(*args)
99
+ end
100
+
101
+ def revoke(*args)
102
+ permissions.revoke(*args)
103
+ end
104
+
105
+ def method_missing(method, *args)
106
+ if method.to_s =~ /^can_([^\?]+)\?$/
107
+ permissions = $1
108
+ options = {}
109
+ if permissions =~ /_on$/
110
+ permissions.chomp!('_on')
111
+ options[:on] = args.shift
112
+ end
113
+ permissions = permissions.split('_and_')
114
+ if permissions.all? {|permission| Permissive::Permissions.hash.has_key?(permission.downcase.to_sym) }
115
+ class_eval <<-end_eval
116
+ def #{method}#{"(scope)" if options[:on]}
117
+ can?(#{[permissions, args].flatten.join(', ').inspect}#{", :on => scope" if options[:on]})
118
+ end
119
+ end_eval
120
+ return can?(*[permissions, options].flatten)
121
+ end
122
+ end
123
+ super
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ if defined?(ActiveRecord::Base)
133
+ ActiveRecord::Base.send :include, Permissive::ActsAsPermissive
134
+ end
@@ -0,0 +1,43 @@
1
+ # This is the core permission class that Permissive uses.
2
+ module Permissive
3
+ class Permission < ActiveRecord::Base
4
+ attr_writer :grant_template, :template
5
+ belongs_to :permitted_object, :polymorphic => true
6
+ belongs_to :scoped_object, :polymorphic => true
7
+ named_scope :on, lambda {|scoped_object|
8
+ if scoped_object.nil?
9
+ {:conditions => ['scoped_object_id IS NULL AND scoped_object_type IS NULL']}
10
+ else
11
+ {:conditions => ['scoped_object_id = ? AND scoped_object_type = ?', scoped_object.id, scoped_object.class.to_s]}
12
+ end
13
+ }
14
+ set_table_name :permissive_permissions
15
+ validates_presence_of :grant_mask, :mask, :permitted_object
16
+
17
+ class << self
18
+ # Use this anywhere!
19
+ def bit_for(permission)
20
+ Permissive::Permissions.hash[permission.to_s.downcase.to_sym] || 0
21
+ end
22
+ end
23
+
24
+ protected
25
+ def before_save
26
+ # Save permission templates or "Roles"
27
+ if @grant_template
28
+ grant_mask = @grant_template
29
+ end
30
+ if @template
31
+ mask = @template
32
+ end
33
+
34
+ # If Permissive is set to be seriously intense about who can grant what to
35
+ # whom, it makes sure no bits on the grant_mask exceed those of the
36
+ # permission mask
37
+ # TODO: You know ... this.
38
+ # if grant_mask && Permissive.strong
39
+ # grant_mask = grant_mask & mask
40
+ # end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ # TODO: Abstract this module more later
2
+ module Permissive
3
+ class PermissionError < StandardError; end;
4
+
5
+ module Permissions
6
+ class << self
7
+ def const_set(*args)
8
+ @@hash = nil
9
+ super
10
+ end
11
+
12
+ def hash
13
+ @@hash ||= begin
14
+ bitwise_hash = constants.inject({}) do |hash, constant_name|
15
+ hash[constant_name.downcase] = 2 ** Permissive::Permissions.const_get(constant_name.to_sym)
16
+ hash
17
+ end
18
+ inverted_hash = bitwise_hash.invert
19
+ bitwise_hash.values.sort.inject(ActiveSupport::OrderedHash.new) do |hash, value|
20
+ hash[inverted_hash[value].to_sym] = value
21
+ hash
22
+ end
23
+ rescue ArgumentError
24
+ raise Permissive::PermissionError.new("Permissions must be integers or longs. Strings, symbols, and floats are not currently supported.")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,2 @@
1
+ # Init.rb is here to make this gem'able down the road
2
+ require 'permissive'
@@ -0,0 +1,2 @@
1
+ --exclude "spec/*,gems/*"
2
+ --rails
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: permissive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flip Sasser
@@ -26,9 +26,28 @@ extensions: []
26
26
 
27
27
  extra_rdoc_files:
28
28
  - README.markdown
29
+ - README.markdown.html
29
30
  files:
30
- - VERSION
31
+ - .gemspec
32
+ - .gitignore
33
+ - MIT-LICENSE
31
34
  - README.markdown
35
+ - Rakefile
36
+ - VERSION
37
+ - generators/permissive_migration/USAGE
38
+ - generators/permissive_migration/permissive_migration_generator.rb
39
+ - generators/permissive_migration/templates/permissive_migration.rb
40
+ - lib/permissive.rb
41
+ - lib/permissive/acts_as_permissive.rb
42
+ - lib/permissive/permission.rb
43
+ - lib/permissive/permissions.rb
44
+ - rails/init.rb
45
+ - spec/acts_as_permissive_spec.rb
46
+ - spec/permissions_spec.rb
47
+ - spec/rcov.opts
48
+ - spec/spec.opts
49
+ - spec/spec_helper.rb
50
+ - README.markdown.html
32
51
  has_rdoc: true
33
52
  homepage: http://github.com/flipsasser/permissive
34
53
  licenses: []