permit 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.yardopts +3 -0
- data/MIT-LICENSE +20 -0
- data/README.mkd +238 -0
- data/Rakefile +69 -0
- data/VERSION.yml +5 -0
- data/generators/permit/USAGE +40 -0
- data/generators/permit/permit_generator.rb +25 -0
- data/generators/permit/templates/authorization.rb +2 -0
- data/generators/permit/templates/initializer.rb +37 -0
- data/generators/permit/templates/migration.rb +28 -0
- data/generators/permit/templates/role.rb +2 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/models/association.rb +89 -0
- data/lib/models/authorizable.rb +31 -0
- data/lib/models/authorization.rb +54 -0
- data/lib/models/person.rb +148 -0
- data/lib/models/role.rb +59 -0
- data/lib/permit/controller.rb +132 -0
- data/lib/permit/permit_rule.rb +198 -0
- data/lib/permit/permit_rules.rb +141 -0
- data/lib/permit/support.rb +67 -0
- data/lib/permit.rb +134 -0
- data/permit.gemspec +91 -0
- data/rails/init.rb +7 -0
- data/spec/models/alternate_models_spec.rb +54 -0
- data/spec/models/authorizable_spec.rb +78 -0
- data/spec/models/authorization_spec.rb +77 -0
- data/spec/models/person_spec.rb +278 -0
- data/spec/models/role_spec.rb +121 -0
- data/spec/permit/controller_spec.rb +308 -0
- data/spec/permit/permit_rule_spec.rb +452 -0
- data/spec/permit/permit_rules_spec.rb +273 -0
- data/spec/permit_spec.rb +58 -0
- data/spec/spec_helper.rb +73 -0
- data/spec/support/helpers.rb +13 -0
- data/spec/support/models.rb +38 -0
- data/spec/support/permits_controller.rb +7 -0
- data/tasks/permit_tasks.rake +4 -0
- data/uninstall.rb +1 -0
- metadata +107 -0
data/.yardopts
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 [name of plugin creator]
|
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.mkd
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# Permit
|
2
|
+
A flexible controller authorization tool for Ruby on Rails.
|
3
|
+
|
4
|
+
**Source:** [http://github.com/dnd/permit](http://github.com/dnd/permit)
|
5
|
+
**Issues:** [http://github.com/dnd/permit/issues](http://github.com/dnd/permit/issues)
|
6
|
+
**Docs:** [http://yardoc.org/docs/dnd-permit](http://yardoc.org/docs/dnd-permit)
|
7
|
+
**Author:** Steve Valaitis
|
8
|
+
**Copyright:** 2010
|
9
|
+
**License:** MIT License
|
10
|
+
|
11
|
+
* [Description](#description)
|
12
|
+
* [Installation & Setup](#setup)
|
13
|
+
* [Usage](#usage)
|
14
|
+
* [Specs & Coverage](#specs)
|
15
|
+
* [Problems](#problems)
|
16
|
+
|
17
|
+
<span id="description"></span>
|
18
|
+
## How does it work?
|
19
|
+
Permit works by allowing you to define a series of allow or deny rules to
|
20
|
+
authorize a person. The rules that apply for the action of the current request
|
21
|
+
are then evaluated against the current person. Rule evaluation stops as soon as
|
22
|
+
a match is found. If a deny rule matches, `#access_denied` will be called, and
|
23
|
+
the person will be prevented from accessing the action. If an allow rule
|
24
|
+
matches, the person will be directed to the action as normal. If no rules
|
25
|
+
match, the person will be denied access. This can be overridden either at the
|
26
|
+
global or controller level by setting the `default_access` option to `:allow`.
|
27
|
+
Keep in mind that _deny rules are always run first_.
|
28
|
+
|
29
|
+
There are three different types of authorizations that you can use with Permit.
|
30
|
+
They are as follows:
|
31
|
+
|
32
|
+
### Static Authorizations
|
33
|
+
These are the most basic forms of authorization and allow you to use one of
|
34
|
+
these roles by itself to authorize someone.
|
35
|
+
|
36
|
+
* `:everyone` - Exactly what it says, an authorization that applies to everyone.
|
37
|
+
* `:guest` - Indicates a guest to the application, and only matches if
|
38
|
+
`current_person#guest?` returns `true`.
|
39
|
+
* `:person` - Indicates an authorized person, and only matches if
|
40
|
+
`current_person#guest?` returns `false`.
|
41
|
+
|
42
|
+
Example:
|
43
|
+
|
44
|
+
allow :guest, :to => :index
|
45
|
+
|
46
|
+
### Dynamic Authorization
|
47
|
+
When using the `:person` role, you can additionally specify the `:who`/`:that` and
|
48
|
+
`:of`/`:on` options. This will cause the current person object to be sent as an
|
49
|
+
argument to the method indicated by `:who`/`:that` on the target resource
|
50
|
+
indicated by `:of`/`:on`. If the method call returns `true` then the rule will be
|
51
|
+
a match.
|
52
|
+
|
53
|
+
If the symbol given to `:who`/`:that` is prefixed with `is_` some special sugar will be
|
54
|
+
applied, causing Permit to try and use various methods on the resource. You can
|
55
|
+
see these methods in the documentation for
|
56
|
+
[PermitRule#initialize](http://yardoc.org/docs/dnd-permit/Permit/PermitRule:initialize)
|
57
|
+
|
58
|
+
A dynamic authorization might look like this:
|
59
|
+
|
60
|
+
allow :person, :who => :is_owner, :of => :project, :to => :write
|
61
|
+
|
62
|
+
### Named Authorizations
|
63
|
+
These are authorizations using custom roles that you define in the database and
|
64
|
+
are mapped to a person in an authorizations table. A person may be granted a
|
65
|
+
role for a given resource, more than one resource, or no resource at
|
66
|
+
all\(depending on what the role definition allows\).
|
67
|
+
|
68
|
+
Some named authorizations might look like:
|
69
|
+
|
70
|
+
allow :admin, :of => :team, :to => :show
|
71
|
+
allow [:project_owner, :project_manager], :of => :project, :to => :all
|
72
|
+
|
73
|
+
<span id="setup"></span>
|
74
|
+
## How do I get it?
|
75
|
+
|
76
|
+
### Installation
|
77
|
+
You can install Permit as a gem(make sure to add "`config.gem 'permit'`" to your
|
78
|
+
`config/environment.rb` file):
|
79
|
+
|
80
|
+
sudo gem install permit
|
81
|
+
|
82
|
+
or as a plugin:
|
83
|
+
|
84
|
+
script/plugin install git://github.com/dnd/permit.git
|
85
|
+
|
86
|
+
as a gem from source:
|
87
|
+
|
88
|
+
git clone git://github.com/dnd/permit.git
|
89
|
+
sudo rake install
|
90
|
+
|
91
|
+
### Setup
|
92
|
+
|
93
|
+
#### Pre-requisites
|
94
|
+
You must have a `Person` model, _or_ some other model that represents an
|
95
|
+
authorized user of the system that responds to `#guest?`. Permit will not create
|
96
|
+
this model for you. You can get as fancy as you want with the `guest?` method,
|
97
|
+
but a simple example would be:
|
98
|
+
|
99
|
+
def guest?
|
100
|
+
new_record?
|
101
|
+
end
|
102
|
+
|
103
|
+
#### Generation
|
104
|
+
If you are not going to use named authorizations, run:
|
105
|
+
|
106
|
+
script/generate permit [Person] --init-only
|
107
|
+
|
108
|
+
If you are going to use named authorizations you can run:
|
109
|
+
|
110
|
+
script/generate permit [Person [Authorization [Role]]]
|
111
|
+
|
112
|
+
You are not required to pass in any arguments to the generator. The arguments
|
113
|
+
above are optional, and reflect the default names that Permit uses. These only
|
114
|
+
need to be specified if you want to use different class name(s). So if you
|
115
|
+
wanted to use an existing `Employee` class for authorization instead of the
|
116
|
+
default `Person`, run:
|
117
|
+
|
118
|
+
script/generate permit Employee
|
119
|
+
|
120
|
+
For full details on the generator take a look at the `--help`
|
121
|
+
|
122
|
+
Run the migration for the roles and authorizations:
|
123
|
+
|
124
|
+
rake db:migrate
|
125
|
+
|
126
|
+
#### Controller
|
127
|
+
Include Permit in your `ApplicationController`:
|
128
|
+
|
129
|
+
include Permit::ControllerExtensions
|
130
|
+
|
131
|
+
Create a method that returns the current authorization subject. This will by
|
132
|
+
default be inferred from the class name given to Permit for initialization, and
|
133
|
+
takes the form of `current_*`. So if the class was `Person`, Permit would look
|
134
|
+
for `current_person`. For `User` it would be `current_user`, etc... If the
|
135
|
+
method you want to use doesn't follow this convention it can be overridden in
|
136
|
+
the initializer.
|
137
|
+
|
138
|
+
Permit::Config.controller_subject_method = :logged_user
|
139
|
+
|
140
|
+
<span id="usage"></span>
|
141
|
+
## How do I use it?
|
142
|
+
|
143
|
+
### Controller
|
144
|
+
After you have "included" Permit into your controller, it is still not active.
|
145
|
+
For that you must define a block of rules by calling
|
146
|
+
[`permit`](http://yardoc.org/docs/dnd-permit/Permit/ControllerExtensions/PermitClassMethods:permit)
|
147
|
+
in your controller. If you want to by default protect all of your controllers,
|
148
|
+
you can just make an empty permit call in your base controller class\(such as
|
149
|
+
`ApplicationController`\).
|
150
|
+
|
151
|
+
Something to keep in mind is that when `permit` is called, a before filter is
|
152
|
+
set to check the authorizations. Any setup that you need to do for setting
|
153
|
+
the current subject, or the resource to be used in `:of`/`:on` criteria needs to
|
154
|
+
be done through before filters set prior to this call.
|
155
|
+
|
156
|
+
_**The rules defined in `permit` blocks are not additive.** When a new `permit` call
|
157
|
+
is made, it wipes out any previously set rules. It also resets the before filter
|
158
|
+
position for checking the authorizations thus allowing you to add any other
|
159
|
+
before filters you may need in your implementing controller._
|
160
|
+
|
161
|
+
You can create "allow" and "deny" rules by passing at minimum, a role, and one
|
162
|
+
or more actions that the rule applies to. The actions will be expanded using the
|
163
|
+
aliases defined in
|
164
|
+
[Permit::Config.action_aliases](http://yardoc.org/docs/dnd-permit/Permit/Config.action_aliases),
|
165
|
+
and are expanded in a non-recursive fashion. You can optionally pass `:all` for
|
166
|
+
the action, which will cause the rule to be tested for all actions. "allow"
|
167
|
+
rules accept the `:to` key for actions, and "deny" rules accept the `:from` key.
|
168
|
+
|
169
|
+
For the full documentation and description of options you can use for creating rules
|
170
|
+
see the documentation for
|
171
|
+
[PermitRule#initialize](http://yardoc.org/docs/dnd-permit/Permit/PermitRule:initialize)
|
172
|
+
|
173
|
+
permit do
|
174
|
+
deny :person, :from => [:write, :destroy], :if => Proc.new {|person, context| person.status == :on_leave}
|
175
|
+
allow :person, :who => :has_commented?, :on => :article, :to => :show
|
176
|
+
allow :person, :who => :is_author, :of => :article, :to => [:read, :write]
|
177
|
+
allow :admin, :to => :all
|
178
|
+
end
|
179
|
+
|
180
|
+
> Deny a person from new, create, edit, update, delete, and destroy if they are on leave.
|
181
|
+
|
182
|
+
> Allow a person who has commented on the article to show. `@article.has_commented?(current_person)`
|
183
|
+
|
184
|
+
> Allow person who is the author of the article to index, show, new, create,
|
185
|
+
> edit, and update. `@article.author == current_person`
|
186
|
+
|
187
|
+
> Allow a person that has the admin role for no resource to access any action.
|
188
|
+
> `current_person.authorized?(:admin, nil)
|
189
|
+
|
190
|
+
#### Helpers
|
191
|
+
The following helpers are included for use in your views, or for one off
|
192
|
+
operations in your controllers.
|
193
|
+
|
194
|
+
* `allowed?` - Returns `true` if the person matches the rule criteria
|
195
|
+
* `denied?` - Returns `true` if the person does not match the rule criteria
|
196
|
+
* `authorized?` - Calls `current_person.authorized?`
|
197
|
+
|
198
|
+
See
|
199
|
+
[Permit::ControllerExtensions::InstanceMethods](http://yardoc.org/docs/dnd-permit/Permit/ControllerExtensions/PermitInstanceMethods)
|
200
|
+
for the full documentation on these methods.
|
201
|
+
|
202
|
+
|
203
|
+
### Models
|
204
|
+
_This aspect of Permit only applies if you are using named authorizations._
|
205
|
+
|
206
|
+
Named authorizations are setup based on the call to
|
207
|
+
`Permit::Config.set_core_models` in your initializer. This call sets up your
|
208
|
+
authorization, person, and role models by calling `permit_authorization`,
|
209
|
+
`permit_person`, and `permit_role` on them respectively.
|
210
|
+
|
211
|
+
The extensions for authorization, and role setup some basic validations to
|
212
|
+
ensure the integrity of the models.
|
213
|
+
|
214
|
+
#### Resources
|
215
|
+
To setup a model to be used as a resource for authorization, call
|
216
|
+
`permit_authorizable` inside of it.
|
217
|
+
|
218
|
+
#### Associations
|
219
|
+
The person, role, and resource models are setup with a `has_many :authorizations`
|
220
|
+
association. This association is extended with a few methods that are documented
|
221
|
+
in [AssociationExtensions](http://yardoc.org/docs/dnd-permit/Permit/Models/AssociationExtensions)
|
222
|
+
|
223
|
+
#### Person
|
224
|
+
The person model is extended with a few methods to simplify authorizing, and
|
225
|
+
revoking roles, as well as checking if the person is authorized on a given set
|
226
|
+
of roles for a resource. These methods are documented in
|
227
|
+
[PersonInstanceMethods](http://yardoc.org/docs/dnd-permit/Permit/Models/PersonExtensions/PersonInstanceMethods).
|
228
|
+
|
229
|
+
<span id="specs"></span>
|
230
|
+
## Specs & Coverage
|
231
|
+
Permit currently has fairly high test coverage\(>95%\). To run the specs for
|
232
|
+
Permit, the plugin will most likely need to be inside of an existing Rails
|
233
|
+
application.
|
234
|
+
|
235
|
+
<span id="problems"></span>
|
236
|
+
## Problems?
|
237
|
+
Please use the [GitHub issue tracker](http://github.com/dnd/permit/issues) for
|
238
|
+
any bugs, problems, or unexpected behavior you run across while using Permit.
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run specs tests.'
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'spec'
|
10
|
+
require 'spec/rake/spectask'
|
11
|
+
|
12
|
+
desc 'Run all specs'
|
13
|
+
Spec::Rake::SpecTask.new 'spec' do |t|
|
14
|
+
t.spec_files = FileList['spec']
|
15
|
+
t.spec_opts = ["--colour"]
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'rcov'
|
20
|
+
|
21
|
+
desc 'Run all specs with rcov'
|
22
|
+
Spec::Rake::SpecTask.new 'rcov' do |t|
|
23
|
+
t.spec_files = FileList['spec']
|
24
|
+
t.rcov = true
|
25
|
+
t.rcov_opts = ['--exclude', 'spec,app/,config/,rubygems/']
|
26
|
+
end
|
27
|
+
rescue LoadError
|
28
|
+
warn "RCov is not available. To run specs with coverage `gem install rcov`."
|
29
|
+
end
|
30
|
+
rescue LoadError
|
31
|
+
warn "RSpec is not available. To run specs `gem install rspec`."
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Generate documentation for the permit plugin.'
|
35
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
36
|
+
rdoc.rdoc_dir = 'rdoc'
|
37
|
+
rdoc.title = 'Permit'
|
38
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
39
|
+
rdoc.rdoc_files.include('README')
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
require 'yard'
|
45
|
+
|
46
|
+
desc 'Generate YARDocs'
|
47
|
+
YARD::Rake::YardocTask.new do |t|
|
48
|
+
t.files = ['lib/**/*.rb']
|
49
|
+
end
|
50
|
+
rescue LoadError
|
51
|
+
warn "YARD not available. To compile YARDocs `gem install yard`."
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'jeweler'
|
56
|
+
Jeweler::Tasks.new do |gem|
|
57
|
+
gem.name = "permit"
|
58
|
+
gem.summary = "A flexible authorization plugin for Ruby on Rails."
|
59
|
+
gem.email = "steve@digitalnothing.com"
|
60
|
+
gem.homepage = "http://github.com/dnd/permit"
|
61
|
+
gem.author = "Steve Valaitis"
|
62
|
+
gem.files.exclude 'autotest'
|
63
|
+
gem.extra_rdoc_files = ['README.mkd']
|
64
|
+
end
|
65
|
+
Jeweler::GemcutterTasks.new
|
66
|
+
rescue LoadError
|
67
|
+
warn "Jeweler not available. To install `gem install jeweler`."
|
68
|
+
end
|
69
|
+
|
data/VERSION.yml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Description:
|
2
|
+
The permit generator creates the authorization and role models, as well as the
|
3
|
+
migration and an initializer.
|
4
|
+
|
5
|
+
You can optionally pass in different names for the authorization, role, and
|
6
|
+
person class that you want to use. Changing the value for Person will not
|
7
|
+
alter any existing models, but will be used to generate the proper foreign-key
|
8
|
+
in the migration.
|
9
|
+
|
10
|
+
script/generate permit [Person [Authorization [Role]]]
|
11
|
+
|
12
|
+
If you don't want to use named authorizations, and only want to generate the
|
13
|
+
initializer pass in the --init-only option. You can optionally pass the name
|
14
|
+
of the class that represents an authenticated user and the generator will set
|
15
|
+
the name of the current_* method to use to retrieve the subject for
|
16
|
+
authorization.
|
17
|
+
|
18
|
+
script/generate permit [Person] --init-only
|
19
|
+
|
20
|
+
Example:
|
21
|
+
script/generate permit
|
22
|
+
|
23
|
+
This will create:
|
24
|
+
Model: app/models/authorization.rb
|
25
|
+
Model: app/models/role.rb
|
26
|
+
Migration: db/migrate/xxx_create_permit_structure.rb
|
27
|
+
Initializer: config/initializers/permit.rb
|
28
|
+
|
29
|
+
script/generate permit Employee Access Job
|
30
|
+
|
31
|
+
This will create:
|
32
|
+
Model: app/models/access.rb
|
33
|
+
Model: app/models/job.rb
|
34
|
+
Migration: db/migrate/xxx_create_permit_structure.rb
|
35
|
+
Initializer: config/initializers/permit.rb
|
36
|
+
|
37
|
+
script/generate permit --init-only
|
38
|
+
|
39
|
+
This will create:
|
40
|
+
Initializer: config/initializers/permit.rb
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class PermitGenerator < Rails::Generator::Base
|
2
|
+
default_options :setup_named_roles => true
|
3
|
+
|
4
|
+
attr_reader :authorization_class, :role_class, :person_class
|
5
|
+
def manifest
|
6
|
+
record do |m|
|
7
|
+
@person_class = (args.shift || 'Person').camelize
|
8
|
+
@authorization_class = (args.shift || 'Authorization').camelize
|
9
|
+
@role_class = (args.shift || 'Role').camelize
|
10
|
+
|
11
|
+
m.template 'initializer.rb', 'config/initializers/permit.rb'
|
12
|
+
|
13
|
+
if options[:setup_named_roles]
|
14
|
+
m.template 'role.rb', "app/models/#{role_class.underscore}.rb"
|
15
|
+
m.template 'authorization.rb', "app/models/#{authorization_class.underscore}.rb"
|
16
|
+
m.migration_template 'migration.rb', "db/migrate", :migration_file_name => "create_permit_structure"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def add_options!(opt)
|
23
|
+
opt.on("--init-only", "Only generate the initializer file.") {|v| options[:setup_named_roles] = false}
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Sets up the core models to be used for authorizations, people, and roles. This
|
2
|
+
# call is only required if you are using named authorizations, and may only be
|
3
|
+
# called once. If you are not using named authorizations you may leave this
|
4
|
+
# commented out.
|
5
|
+
<%if options[:setup_named_roles] -%>
|
6
|
+
Permit::Config.set_core_models(<%=authorization_class%>, <%=person_class%>, <%=role_class%>)
|
7
|
+
<%else -%>
|
8
|
+
# Permit::Config.set_core_models(Authorization, Person, Role)
|
9
|
+
<%end -%>
|
10
|
+
|
11
|
+
# Sets the method to use for retrieving the current authorization subject. Leave
|
12
|
+
# this nil and Permit will infer the method name(current_*) from the
|
13
|
+
# authorization subject model name given to #set_core_models. If named
|
14
|
+
# authorizations aren't being used then this will default to :current_person.
|
15
|
+
<%if !options[:setup_named_roles] && person_class != 'Person' -%>
|
16
|
+
Permit::Config.controller_subject_method = :current_<%=person_class.underscore %>
|
17
|
+
<%else -%>
|
18
|
+
# Permit::Config.controller_subject_method = nil
|
19
|
+
<%end -%>
|
20
|
+
|
21
|
+
# Controls the default response given by PermitRules#permitted? when no rules
|
22
|
+
# match. To automatically allow access if no rules match, set this to :allow.
|
23
|
+
# Default is :deny.
|
24
|
+
# Permit::Config.default_access = :deny
|
25
|
+
|
26
|
+
# You can modify the action_aliases hash to add your own aliases to be used for
|
27
|
+
# expansion by PermitRules. The hash key is a Symbol for the action to be
|
28
|
+
# expanded, and an array of Symbols representing the actions to expand it into.
|
29
|
+
# Alias expansion is non-rescursive. The defaults are:
|
30
|
+
# {
|
31
|
+
# :create => [:new, :create],
|
32
|
+
# :update => [:edit, :update],
|
33
|
+
# :destroy => [:delete, :destroy],
|
34
|
+
# :read => [:index, :show],
|
35
|
+
# :write => [:new, :create, :edit, :update]
|
36
|
+
# }
|
37
|
+
# Permit::Config.action_aliases
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class CreatePermitStructure < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%=role_class.tableize%> do |t|
|
4
|
+
t.string :key, :name, :null => false
|
5
|
+
t.string :description
|
6
|
+
t.boolean :requires_resource, :authorize_resource, :null => false, :default => true
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :<%=role_class.tableize%>, :key, :unique => true
|
10
|
+
|
11
|
+
create_table :<%=authorization_class.tableize%> do |t|
|
12
|
+
t.integer :<%=person_class.underscore%>_id, :<%=role_class.underscore%>_id, :null => false
|
13
|
+
t.string :resource_type
|
14
|
+
t.integer :resource_id
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :<%=authorization_class.tableize%>, :<%=person_class.underscore%>_id
|
19
|
+
add_index :<%=authorization_class.tableize%>, :<%=role_class.underscore%>_id
|
20
|
+
add_index :<%=authorization_class.tableize%>, [:<%=person_class.underscore%>_id, :<%=role_class.underscore%>_id, :resource_type, :resource_id], :unique => true, :name => '<%=authorization_class.tableize%>_idx_uniq'
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def self.down
|
25
|
+
drop_table :<%=authorization_class.tableize%>
|
26
|
+
drop_table :<%=role_class.tableize%>
|
27
|
+
end
|
28
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init.rb"
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Permit
|
2
|
+
module Models
|
3
|
+
# Defines a set of methods to extend the has_many :authorizations
|
4
|
+
# associations to help with querying for common cases. Some of these methods
|
5
|
+
# do not show up in the documentation because they are dynamically created
|
6
|
+
# with class_eval so that they can be explicit to the models you use for the
|
7
|
+
# Person and Role models.
|
8
|
+
module AssociationExtensions
|
9
|
+
include Permit::Support
|
10
|
+
|
11
|
+
# Finds all authorizations for the given resource
|
12
|
+
#
|
13
|
+
# @param [permit_authorizable, nil, :any] resource the resource to find
|
14
|
+
# authorizations for. :any may be given to find matches for any resource.
|
15
|
+
# @return [<permit_authorization>] the authorizations found for the resource.
|
16
|
+
def for(resource)
|
17
|
+
conditions = authorization_conditions(nil, resource)
|
18
|
+
find(:all, :conditions => conditions)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Finds all authorizations for the given role(s).
|
22
|
+
#
|
23
|
+
# @param [permit_role, String, Symbol, <permit_role, String, Symbol>]
|
24
|
+
# roles the roles to find authorizations for.
|
25
|
+
# @return [<permit_authorization>] the authorizations found for the role(s).
|
26
|
+
def as(roles)
|
27
|
+
conditions = authorization_conditions(roles, :any)
|
28
|
+
find(:all, :conditions => conditions)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Finds all authorizations for the given resource and role(s).
|
32
|
+
#
|
33
|
+
# @param [permit_authorizable, nil, :any] resource the resource to find
|
34
|
+
# authorizations for. :any may be given to find matches for any resource.
|
35
|
+
# @param [permit_role, String, Symbol, <permit_role, String, Symbol>]
|
36
|
+
# roles the roles to find authorizations for.
|
37
|
+
# @return [<permit_authorization>] the authorizations found for the resource and role(s)
|
38
|
+
def for_resource_as(resource, roles)
|
39
|
+
conditions = authorization_conditions(roles, resource)
|
40
|
+
find(:all, :conditions => conditions)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Finds all of the resources authorized for the given role(s).
|
44
|
+
#
|
45
|
+
# @param [permit_role, String, Symbol, <permit_role, String, Symbol>]
|
46
|
+
# roles the roles to find authorizations for.
|
47
|
+
# @return [<permit_authorizable>] a unique list of resources authorized
|
48
|
+
# for the role(s).
|
49
|
+
def resources_as(roles)
|
50
|
+
as(roles).collect(&:resource).uniq
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.extended(klass)
|
54
|
+
class_eval <<-END
|
55
|
+
# Finds all of the people that have authorizations for the given resource.
|
56
|
+
#
|
57
|
+
# @param [permit_authorizable, nil, :any] resource the resource to find
|
58
|
+
# authorizations for. :any may be given to find matches for any resource.
|
59
|
+
# @return [<permit_person>] a unique list of the people with
|
60
|
+
# authorizations for the resource.
|
61
|
+
def #{Permit::Config.person_class.plural_class_symbol.to_s}_for(resource)
|
62
|
+
self.for(resource).collect(&:#{Permit::Config.person_class.class_symbol.to_s}).uniq
|
63
|
+
end
|
64
|
+
|
65
|
+
# Finds all of the people that have authorizations for the given role(s).
|
66
|
+
#
|
67
|
+
# @param [permit_role, String, Symbol, <permit_role, String, Symbol>]
|
68
|
+
# roles the roles to find authorizations for.
|
69
|
+
# @return [<permit_person>] a unique list of the people with
|
70
|
+
# authorizations for the role(s).
|
71
|
+
def #{Permit::Config.person_class.plural_class_symbol.to_s}_as(roles)
|
72
|
+
as(roles).collect(&:#{Permit::Config.person_class.class_symbol.to_s}).uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
# Finds all of the roles authorized for the given resource.
|
76
|
+
#
|
77
|
+
# @param [permit_authorizable, nil, :any] resource the resource to find
|
78
|
+
# authorizations for. :any may be given to find matches for any resource.
|
79
|
+
# @return [<permit_role>] a unique list of roles authorized for the
|
80
|
+
# resource.
|
81
|
+
def #{Permit::Config.role_class.plural_class_symbol.to_s}_for(resource)
|
82
|
+
self.for(resource).collect(&:#{Permit::Config.role_class.class_symbol.to_s}).uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
END
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Permit
|
2
|
+
module Models
|
3
|
+
module AuthorizableExtensions
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend AuthorizableClassMethods
|
6
|
+
klass.extend Permit::Support::ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module AuthorizableClassMethods
|
10
|
+
def permit_authorizable
|
11
|
+
return if include? Permit::Models::AuthorizableExtensions::AuthorizableInstanceMethods
|
12
|
+
|
13
|
+
Permit::Config.authorizable_classes << self
|
14
|
+
|
15
|
+
permit_authorized_model :as => :resource
|
16
|
+
|
17
|
+
def resource_type
|
18
|
+
self.base_class.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
include Permit::Support
|
22
|
+
include Permit::Models::AuthorizableExtensions::AuthorizableInstanceMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module AuthorizableInstanceMethods
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Permit
|
2
|
+
module Models
|
3
|
+
module AuthorizationExtensions
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend AuthorizationClassMethods
|
6
|
+
klass.extend Permit::Support::ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module AuthorizationClassMethods
|
10
|
+
# Defines the current model class as handling authorizations for Permit.
|
11
|
+
def permit_authorization
|
12
|
+
return if include? Permit::Models::AuthorizationExtensions::AuthorizationInstanceMethods
|
13
|
+
|
14
|
+
belongs_to :resource, :polymorphic => true
|
15
|
+
belongs_to Permit::Config.person_class.class_symbol, :class_name => Permit::Config.person_class.name
|
16
|
+
belongs_to Permit::Config.role_class.class_symbol, :class_name => Permit::Config.role_class.name
|
17
|
+
|
18
|
+
class_eval <<-END
|
19
|
+
protected
|
20
|
+
def permit_person_proxy
|
21
|
+
#{Permit::Config.person_class.class_symbol.to_s}
|
22
|
+
end
|
23
|
+
|
24
|
+
def permit_role_proxy
|
25
|
+
#{Permit::Config.role_class.class_symbol.to_s}
|
26
|
+
end
|
27
|
+
END
|
28
|
+
|
29
|
+
validates_presence_of Permit::Config.person_class.class_symbol, Permit::Config.role_class.class_symbol
|
30
|
+
validate :resource_presence
|
31
|
+
validate :authorization_uniqueness
|
32
|
+
|
33
|
+
include Permit::Models::AuthorizationExtensions::AuthorizationInstanceMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module AuthorizationInstanceMethods
|
38
|
+
protected
|
39
|
+
def authorization_uniqueness
|
40
|
+
return true unless permit_person_proxy
|
41
|
+
errors.add(Permit::Config.role_class.class_symbol, "This person is already authorized for this resource") if permit_person_proxy.authorized?(permit_role_proxy, resource)
|
42
|
+
end
|
43
|
+
|
44
|
+
def resource_presence
|
45
|
+
# Don't try to do anything if role isn't present
|
46
|
+
return true unless permit_role_proxy
|
47
|
+
|
48
|
+
errors.add(:resource, :blank) if permit_role_proxy.requires_resource? && resource.nil?
|
49
|
+
errors.add(:resource, "Specific resources may not be granted for this role.") if !permit_role_proxy.authorize_resource? && !resource.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|