permissive 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemspec +45 -0
- data/.gitignore +2 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +18 -18
- data/README.markdown.html +191 -0
- data/Rakefile +36 -0
- data/VERSION +1 -1
- data/generators/permissive_migration/USAGE +7 -0
- data/generators/permissive_migration/permissive_migration_generator.rb +7 -0
- data/generators/permissive_migration/templates/permissive_migration.rb +20 -0
- data/lib/permissive.rb +15 -0
- data/lib/permissive/acts_as_permissive.rb +134 -0
- data/lib/permissive/permission.rb +43 -0
- data/lib/permissive/permissions.rb +29 -0
- data/rails/init.rb +2 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +3 -0
- metadata +21 -2
data/.gemspec
ADDED
@@ -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
|
+
|
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.markdown
CHANGED
@@ -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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
42
|
-
|
41
|
+
acts_as_permissive
|
42
|
+
validates_presence_of :first_name, :last_name
|
43
43
|
end
|
44
44
|
|
45
45
|
class Company < ActiveRecord::Base
|
46
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
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 < ActiveRecord::Base
|
42
|
+
acts_as_permissive
|
43
|
+
validates_presence_of :first_name, :last_name
|
44
|
+
end
|
45
|
+
|
46
|
+
class Company < 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 => 'James', :last_name => 'Brennan')
|
54
|
+
@frigo = Employee.create(:first_name => 'Tommy', :last_name => 'Frigo')
|
55
|
+
@adventureland = Company.create(:name => 'Adventureland')
|
56
|
+
|
57
|
+
# Okay, let's do some granting. We'll start by scoping to a specific company.
|
58
|
+
@james.can!(:manage_games, :on => @adventureland)
|
59
|
+
|
60
|
+
# Now let's do some permission checking.
|
61
|
+
@james.can?(:manage_games, :on => @adventureland) #=> true
|
62
|
+
|
63
|
+
# We can also use the metaprogramming syntax:
|
64
|
+
@james.can_manage_games_on?(@adventureland) #=> true
|
65
|
+
@james.can_control_rides_on?(@adventureland) #=> false
|
66
|
+
|
67
|
+
# We can check for multiple permissions, too:
|
68
|
+
@james.can?(:manage_games, :control_rides) #=> 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 => @james)
|
74
|
+
@frigo.can_punch_on?(@james) #=> true
|
75
|
+
|
76
|
+
# And the permissions aren't reciprocal
|
77
|
+
@james.can_punch_on?(@frigo) #=> false
|
78
|
+
|
79
|
+
# Of course, we can grant global (non-scoped) permissions, too:
|
80
|
+
@frigo.can!(:control_rides)
|
81
|
+
@frigo.can_control_rides? #=> true
|
82
|
+
|
83
|
+
# BUT! Global permissions don't override scoped permissions.
|
84
|
+
@frigo.can_control_rides_on?(@adventureland) #=> false
|
85
|
+
|
86
|
+
# Likewise, scoped permissions don't bubble up globally:
|
87
|
+
@james.can_manage_games? #=> false
|
88
|
+
|
89
|
+
# And, last but not least, let's take all of those great permissions away:
|
90
|
+
@james.revoke(:manage_games, :on => @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 < ActiveRecord::Base
|
103
|
+
acts_as_permissive :scope => :company
|
104
|
+
end
|
105
|
+
|
106
|
+
@frigo.permissive_companies #=> [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 => @adventureland, :reset => 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 => @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 < 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 => @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><%= check_box_tag("employee[permissions][]", Permissive::Permissions::CONTROL_RIDES, @employee.can_control_rides?) %> 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 => 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><% current_user.grant_permissions.each do |permission| %>
|
176
|
+
<!-- Do something! -->
|
177
|
+
<% end %>
|
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 => @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>
|
data/Rakefile
ADDED
@@ -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.
|
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,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
|
data/lib/permissive.rb
ADDED
@@ -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
|
data/rails/init.rb
ADDED
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
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.
|
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
|
-
-
|
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: []
|