permit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.mkd +238 -0
  5. data/Rakefile +69 -0
  6. data/VERSION.yml +5 -0
  7. data/generators/permit/USAGE +40 -0
  8. data/generators/permit/permit_generator.rb +25 -0
  9. data/generators/permit/templates/authorization.rb +2 -0
  10. data/generators/permit/templates/initializer.rb +37 -0
  11. data/generators/permit/templates/migration.rb +28 -0
  12. data/generators/permit/templates/role.rb +2 -0
  13. data/init.rb +1 -0
  14. data/install.rb +1 -0
  15. data/lib/models/association.rb +89 -0
  16. data/lib/models/authorizable.rb +31 -0
  17. data/lib/models/authorization.rb +54 -0
  18. data/lib/models/person.rb +148 -0
  19. data/lib/models/role.rb +59 -0
  20. data/lib/permit/controller.rb +132 -0
  21. data/lib/permit/permit_rule.rb +198 -0
  22. data/lib/permit/permit_rules.rb +141 -0
  23. data/lib/permit/support.rb +67 -0
  24. data/lib/permit.rb +134 -0
  25. data/permit.gemspec +91 -0
  26. data/rails/init.rb +7 -0
  27. data/spec/models/alternate_models_spec.rb +54 -0
  28. data/spec/models/authorizable_spec.rb +78 -0
  29. data/spec/models/authorization_spec.rb +77 -0
  30. data/spec/models/person_spec.rb +278 -0
  31. data/spec/models/role_spec.rb +121 -0
  32. data/spec/permit/controller_spec.rb +308 -0
  33. data/spec/permit/permit_rule_spec.rb +452 -0
  34. data/spec/permit/permit_rules_spec.rb +273 -0
  35. data/spec/permit_spec.rb +58 -0
  36. data/spec/spec_helper.rb +73 -0
  37. data/spec/support/helpers.rb +13 -0
  38. data/spec/support/models.rb +38 -0
  39. data/spec/support/permits_controller.rb +7 -0
  40. data/tasks/permit_tasks.rake +4 -0
  41. data/uninstall.rb +1 -0
  42. metadata +107 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .yardoc
2
+ doc
3
+ README.html
4
+ coverage
5
+ pkg
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --protected
2
+ -
3
+ generators/permit/USAGE
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,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 9
4
+ :patch: 0
5
+ :build:
@@ -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,2 @@
1
+ class <%=authorization_class%> < ActiveRecord::Base
2
+ 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
@@ -0,0 +1,2 @@
1
+ class <%=role_class%> < ActiveRecord::Base
2
+ 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