authorization 1.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +170 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +512 -0
- data/README.txt +451 -0
- data/README_developers.txt +43 -0
- data/Rakefile +39 -0
- data/UPGRADE.rdoc +13 -0
- data/VERSION +1 -0
- data/about.yml +8 -0
- data/authorization.gemspec +66 -0
- data/doc/authorization_example.gif +0 -0
- data/doc/authorization_example.rb +38 -0
- data/generators/role_model/USAGE +20 -0
- data/generators/role_model/role_model_generator.rb +35 -0
- data/generators/role_model/templates/fixtures.yml +5 -0
- data/generators/role_model/templates/migration.rb +21 -0
- data/generators/role_model/templates/model.rb +8 -0
- data/generators/role_model/templates/role_model.rb +9 -0
- data/generators/role_model/templates/role_user_model.rb +5 -0
- data/generators/role_model/templates/unit_test.rb +10 -0
- data/init.rb +1 -0
- data/install.rb +2 -0
- data/lib/authorization.rb +176 -0
- data/lib/publishare/exceptions.rb +41 -0
- data/lib/publishare/hardwired_roles.rb +82 -0
- data/lib/publishare/identity.rb +119 -0
- data/lib/publishare/object_roles_table.rb +119 -0
- data/lib/publishare/parser.rb +210 -0
- data/tasks/authorization_tasks.rake +4 -0
- metadata +98 -0
data/CHANGELOG.txt
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
TO DO
|
2
|
+
+ Add Right model generator and DB-backed way of handling rights in addition to inlined "permit" checks
|
3
|
+
+ Added namespacing to @options instance variable to prevent possible name clashes
|
4
|
+
+ Add test generator instead of handling tests in test apps
|
5
|
+
+ Add support for groups
|
6
|
+
+ Extend grammar to allow "(admin or moderator or some_role) of some_model" (?) [Chris Hapgood]
|
7
|
+
+ Extend coverage to models. Look at Bruce Perens's ModelSecurity and access with_scope. (9/3006 - Recently investigated extension to model and the most programmer-friendly DSLs may require too much hacking on ActiveRecord.)
|
8
|
+
|
9
|
+
|
10
|
+
CHANGES (from most recent to oldest)
|
11
|
+
|
12
|
+
|
13
|
+
=== 1.0.10 release (February 27, 2008)
|
14
|
+
|
15
|
+
* Patch Series : Granular redirection configuration submitted by Thomas Weibel
|
16
|
+
|
17
|
+
WARNING : If you are upgrading from a previous install you may need
|
18
|
+
to change some configuration settings in your environment.rb file.
|
19
|
+
|
20
|
+
Remove DEFAULT_REDIRECTION_HASH config
|
21
|
+
Added granular LOGIN_REQUIRED_REDIRECTION hash or path config
|
22
|
+
Added granular PERMISSION_DENIED_REDIRECTION hash or path config
|
23
|
+
Added STORE_LOCATION_METHOD config
|
24
|
+
Support custom flash messages for each redirection type
|
25
|
+
Updated README.txt to provide instructions.
|
26
|
+
Enhanced support for integration with restful_authentication plugin.
|
27
|
+
|
28
|
+
=== 1.0.9 release (February 26, 2008)
|
29
|
+
|
30
|
+
* Patch #8571 : Add type argument to is_role_of_what submitted by Aslak Hellesøy (aslak_hellesoy)
|
31
|
+
|
32
|
+
In my RESTful index views for an AR type I often want to list all of the records *for a given type* for which the current
|
33
|
+
user has the role "show". (As opposed to getting *any* record for which the user has the role)
|
34
|
+
|
35
|
+
In order to achieve this, I have patched identity.rb so tht I can do this:
|
36
|
+
|
37
|
+
def index
|
38
|
+
if current_user.permit? 'admin'
|
39
|
+
# show all projects
|
40
|
+
@projects = Project.find(:all)
|
41
|
+
else
|
42
|
+
@projects = current_user.is_show_for_what(Project)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
=== 1.0.8 release (February 26, 2008)
|
47
|
+
|
48
|
+
* Patch #11352 : Fixes a bug with role_regex and simple quoted roles submitted by 'a French RoR developer'
|
49
|
+
|
50
|
+
Documentation says:
|
51
|
+
|
52
|
+
<role> ::= /\w+/ | /'.*'/
|
53
|
+
|
54
|
+
But the next permission string isn't well parsed: " 'abcd:efgh' or 'abcd:ijkl' "
|
55
|
+
You get an error because the role_regex defined in parser.rb eats every simple quote between the first and the last
|
56
|
+
simple quote in the string.
|
57
|
+
|
58
|
+
So i patched the two instances of role_regex in parser.rb, from this:
|
59
|
+
role_regex = '\s*(\'\s*(.+)\s*\'|([A-Za-z]\w*))\s*'
|
60
|
+
|
61
|
+
to this (the question mark ends the first pattern as soon as possible, avoiding the inner simple quotes to be eaten):
|
62
|
+
role_regex = '\s*(\'\s*(.+?)\s*\'|([A-Za-z]\w*))\s*'
|
63
|
+
|
64
|
+
=== 1.0.7 release (February 25, 2008)
|
65
|
+
|
66
|
+
* Patch #9431 : Fixes a bug in identity.rb submitted by Michel Martens (blaumag)
|
67
|
+
|
68
|
+
If some authorizable instance accepts a role, then it responds true when queried for has_[role_name]?
|
69
|
+
|
70
|
+
Example:
|
71
|
+
country.has_kings? #=> false
|
72
|
+
|
73
|
+
user.has_role "king", country
|
74
|
+
country.has_kings? #=> true
|
75
|
+
|
76
|
+
user.has_no_role "king", country
|
77
|
+
country.has_kings? #=> true
|
78
|
+
|
79
|
+
The last time, country.has_kings? should be false.
|
80
|
+
|
81
|
+
=== 1.0.6 release (February 25, 2008)
|
82
|
+
|
83
|
+
* Patch #12170 : Additional HABTM options for acts_as_authorized_user
|
84
|
+
A very simple patch that allows options to be passed to the has_and_belogs_to_many relationship. This seems necessary
|
85
|
+
if the "User" object has a different name from the table name. has_and_belong_to_many does not automatically
|
86
|
+
use the table set by the "User" object so it must be specified (along with the foreign key if applicable).
|
87
|
+
|
88
|
+
Patch submitted by Eric Anderson (eric1234)
|
89
|
+
|
90
|
+
=== 1.0.5 release (February 25, 2008)
|
91
|
+
|
92
|
+
* Feature : Add additional test for current_user being set to the symbol ':false'.
|
93
|
+
This is for compatibility with the restful_authentication plugin which will
|
94
|
+
set current_user to :false on a bad login. Previously we were only testing
|
95
|
+
for current_user.nil? which was incomplete.
|
96
|
+
|
97
|
+
=== 1.0.4 release (February 25, 2008)
|
98
|
+
|
99
|
+
* Bugfix : RubyForge bug #9368. Problems with about.yml
|
100
|
+
Fixes a minor bug in the about.yml plugin metadata file
|
101
|
+
so that it will parse cleanly. [GR]
|
102
|
+
|
103
|
+
=== 1.0.3 release (February 17, 2008)
|
104
|
+
|
105
|
+
* Minor changes to USAGE text for ./script/generate role_model
|
106
|
+
|
107
|
+
=== 1.0.2 release (February 17, 2008)
|
108
|
+
|
109
|
+
* From this release forward the plugin requires use of Ruby on Rails version 2.x. Version 1.0.1 is the final release fully compatible with Rails 1.2.x.
|
110
|
+
* Upgraded the database migration generator to create the new Rails 2.0.x style 'sexy migrations'.
|
111
|
+
|
112
|
+
=== 1.0.1 release (February 17, 2008)
|
113
|
+
|
114
|
+
* Moved source code to public Git repository at GitHub.com (http://github.com/DocSavage/rails-authorization-plugin/tree/master)
|
115
|
+
* Removed attr_protected declaration from acts_as_authorized_user, acts_as_authorizable methods. These conflicted with usage of the Authorization plugin with models generated by the restful_authentication generator or any model that specified the safer attr_accessible whitelist. RA encourages the safer attr_accessible whitelisting of attributes that are accessible from its models. You cannot apply both attr_accessible and attr_protected in the same model. Users are encouraged to specify a whitelist of attr_accessible model attributes for their applications security. [grempe]
|
116
|
+
|
117
|
+
=== SVN
|
118
|
+
|
119
|
+
* Performance improvement for has_role? [Sean Geoghegan]
|
120
|
+
|
121
|
+
* Allow customization of message on redirection after failed authorization (:redirect_message option) [Joey Geiger]
|
122
|
+
|
123
|
+
* Patch to allow authorizable objects that use single table inheritance (STI) [Sean Geoghegan]
|
124
|
+
|
125
|
+
=== 1.0 release (Sept 13, 2006)
|
126
|
+
|
127
|
+
* Added attr_protected for habtm and has_many role ids to block security concern if developers use update_attributes(params[:auth_obj]) on an authorizable object [Michael Schuerig]
|
128
|
+
|
129
|
+
* Use before_filter rather than prepend_before_filter so necessary instance variables (and methods) can be established before trying authorization checks. This fix came about for Mephisto blog where a class-level permit "admin of site" was used. The site attribute was set in a before_filter. If you prepend your authorization filter, it will execute before any other before_filter, which is probably not a good idea.
|
130
|
+
|
131
|
+
* Add "about" yaml for future Rails plugin directory.
|
132
|
+
|
133
|
+
* Cleaned up exception handling a little [due to suggestion by Michael Schuerig]
|
134
|
+
|
135
|
+
* Add generator for role model and migration, e.g., "script/generate role_model Role".
|
136
|
+
Role model must be called "Role" at this time. More general naming as a TO DO.
|
137
|
+
|
138
|
+
* Removed simple_roles_table to simplify plugin.
|
139
|
+
|
140
|
+
* Moved all files in Authorization namespace into /publishare subdirectory
|
141
|
+
to reduce danger of clashes in load path [nod to Michael Schuerig].
|
142
|
+
|
143
|
+
* Small code refinement patch [Michael Schuerig]
|
144
|
+
|
145
|
+
* The colon preceding a model name in the authorization expression is now optional. The parser uses accepted prepositions to disambiguate models from roles.
|
146
|
+
|
147
|
+
* Change default parser from Recursive Descent parser to Eval parser.
|
148
|
+
Currently implemented recursive descent parser doesn't handle left-sided
|
149
|
+
boolean expressions well. Eval parser relies on Ruby (good thing), but
|
150
|
+
wherever there's an eval, we have to be more careful.
|
151
|
+
|
152
|
+
* Will start linking to and monitoring forum area at RubyForge
|
153
|
+
http://rubyforge.org/forum/?group_id=1797
|
154
|
+
|
155
|
+
* Added changelog :)
|
156
|
+
|
157
|
+
* Added return false to handle_redirection to short-circuit filters if
|
158
|
+
redirect occurs. This is second fix to prevent double renders.
|
159
|
+
|
160
|
+
* Changed the requires to pull files from the plugin directory. (Necessary for name conflicts between plugin and apps)
|
161
|
+
|
162
|
+
* Minor fixes to update documentation
|
163
|
+
|
164
|
+
=== 1.0 rc3 (July 19, 2006)
|
165
|
+
|
166
|
+
* Fix to prevent double redirect
|
167
|
+
|
168
|
+
* Fix to migration examples
|
169
|
+
|
170
|
+
... see svn log
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 William T Katz
|
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.rdoc
ADDED
@@ -0,0 +1,512 @@
|
|
1
|
+
= Authorization plugin
|
2
|
+
|
3
|
+
This plugin provides a flexible way to add authorization to Rails.
|
4
|
+
|
5
|
+
The authorization process decides whether a user is allowed access to some
|
6
|
+
feature. It is distinct from the authentication process, which tries to
|
7
|
+
confirm a user is authentic, not an imposter. There are many authentication
|
8
|
+
systems available for Rails, e.g., acts_as_authenticated and LoginEngine. This
|
9
|
+
authorization system will play nicely with them as long as some simple
|
10
|
+
requirements are met:
|
11
|
+
|
12
|
+
1. User objects are available that implement a has_role?(role,
|
13
|
+
authorizable_object = nil) method. This requirement can be easily
|
14
|
+
handled by using acts_as_authorized_user in the User-like class.
|
15
|
+
|
16
|
+
2. If you want to use "role of model" authorization expressions, like "owner of
|
17
|
+
resource" or "eligible for :award", then your models with roles must
|
18
|
+
implement an accepts_role?(role, user) method. This requirement can
|
19
|
+
be handled by using acts_as_authorizable in the model class.
|
20
|
+
|
21
|
+
The authorization plugin provides the following:
|
22
|
+
|
23
|
+
* A simple way of checking authorization at either the class or instance method
|
24
|
+
level using #permit and #permit?
|
25
|
+
|
26
|
+
* Authorization using roles for the entire application, a model class, or an
|
27
|
+
instance of a model (i.e., a particular object).
|
28
|
+
|
29
|
+
* Some english-like dynamic methods that draw on the defined roles. You will be
|
30
|
+
able to use methods like "user.is_fan_of angelina" or "angelina.has_fans?",
|
31
|
+
where a 'fan' is only defined in the roles table.
|
32
|
+
|
33
|
+
* Pick-and-choose a mixin for your desired level of database complexity. For
|
34
|
+
all the features, you will want to use "object roles table" (see below)
|
35
|
+
|
36
|
+
|
37
|
+
== Example Usage
|
38
|
+
|
39
|
+
class MeetingController < ApplicationController
|
40
|
+
|
41
|
+
permit "rubyists and wanna_be_rubyists", :except => :public_page
|
42
|
+
|
43
|
+
def public_page
|
44
|
+
render :text => "We're all in Chicago"
|
45
|
+
end
|
46
|
+
|
47
|
+
def secret_info
|
48
|
+
permit "interested in Answers and (matz or dhh)" do
|
49
|
+
render :text => "The Answer = 42"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_apprentice
|
54
|
+
@founder = User.find_by_name('matz')
|
55
|
+
permit "'inner circle' of :founder" do
|
56
|
+
if request.post?
|
57
|
+
apprentice = User.find_by_skillset(params[:uber_hacker])
|
58
|
+
ruby_community = Group.find_by_name('Ruby')
|
59
|
+
ruby_community.accepts_role 'yarv_builder', apprentice
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def rails_conf
|
65
|
+
@meeting = Meeting.find_by_name('RailsConf')
|
66
|
+
permit "attendees of :meeting or swedish_mensa_supermodels" do
|
67
|
+
venue = Hotel.find_by_name("Wyndham O'Hare")
|
68
|
+
current_user.is_traveller_to venue
|
69
|
+
if permit? "traveller to :venue and not speaker"
|
70
|
+
Partay.all_night_long
|
71
|
+
@misdeeds = current_user.is_participant_in_what
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
== Installation
|
80
|
+
|
81
|
+
Installation of the Authorization plugin is quick and easy.
|
82
|
+
|
83
|
+
=== Step 1
|
84
|
+
|
85
|
+
Open a terminal and change directory to the root of your
|
86
|
+
Ruby on Rails application referred to here as 'RAILS_ROOT'. You
|
87
|
+
can choose to install the plugin using the standard Ruby on Rails tools (recommended),
|
88
|
+
as a Git sub-module, or by grabbing a tarball.
|
89
|
+
|
90
|
+
=== Step 2a (Standard install, recommended)
|
91
|
+
|
92
|
+
NOTE : This assumes you are using a current (Rails 2.1.x +) version of Rails which
|
93
|
+
now has built in support for installing a plugin directly from a git repo. This also assumes
|
94
|
+
you have Git installed and working properly. You can always use the manual install shown below
|
95
|
+
if this is not true.
|
96
|
+
|
97
|
+
Run the following command in your RAILS_ROOT:
|
98
|
+
|
99
|
+
./script/plugin install git://github.com/DocSavage/rails-authorization-plugin.git
|
100
|
+
|
101
|
+
This will install the latest version of the plugin from our GitHub master repo
|
102
|
+
into your RAILS_ROOT/vendor/plugins/rails-authorization-plugin directory.
|
103
|
+
|
104
|
+
=== Step 2b (Alternative install using Git sub-module, for advanced users of the Git SCM)
|
105
|
+
|
106
|
+
The source code for this plugin is maintained in a Git SCM
|
107
|
+
repository. The Git repository will always have the latest
|
108
|
+
version of the code.
|
109
|
+
|
110
|
+
You can install the plugin using Git sub-modules (which
|
111
|
+
are akin to using SVN externals). Installing this way allows
|
112
|
+
you to update the plugin code later if needed (but note that
|
113
|
+
it will not update any generated code created earlier by this
|
114
|
+
plugin such as migrations, which you will need to update manually).
|
115
|
+
Also note that if you are deploying your code using Capistrano
|
116
|
+
this method may cause issues if you are not careful (e.g. the code
|
117
|
+
will be deployed but the sub-modules will not be updated or
|
118
|
+
installed at all).
|
119
|
+
|
120
|
+
From your RAILS_ROOT directory run:
|
121
|
+
|
122
|
+
git submodule add git://github.com/DocSavage/rails-authorization-plugin.git vendor/plugins/authorization
|
123
|
+
|
124
|
+
You should be able to update this plugin in the future with
|
125
|
+
the simple command (again from RAILS_ROOT):
|
126
|
+
|
127
|
+
git submodule update
|
128
|
+
|
129
|
+
=== Step 2c (Alternative manual install from tarball)
|
130
|
+
|
131
|
+
If you like to install the old school manual way, feel free to download a copy of
|
132
|
+
latest master branch plugin code from:
|
133
|
+
|
134
|
+
http://github.com/docsavage/rails-authorization-plugin/tarball/master
|
135
|
+
|
136
|
+
Once downloaded, rename the top level dir to 'rails-authorization-plugin' and then
|
137
|
+
you can stuff that directory in your RAILS_ROOT/vendor/plugins directory.
|
138
|
+
|
139
|
+
|
140
|
+
== Configuration
|
141
|
+
|
142
|
+
These instructions will show you how to do the initial configuration
|
143
|
+
of the plugin.
|
144
|
+
|
145
|
+
=== Choose a Mixin Type
|
146
|
+
|
147
|
+
==== Hardwired Roles
|
148
|
+
|
149
|
+
This is the simplest way to use the plugin and requires no database.
|
150
|
+
Roles are assumed to be coded into the Model classes using the
|
151
|
+
<tt>has_role?(role, obj = nil)</tt> method. This method is however more
|
152
|
+
limited in the functionality available to you.
|
153
|
+
|
154
|
+
==== Object Roles (Recommended, DB Required)
|
155
|
+
|
156
|
+
The Object Roles Table mixin provides full support for authorization
|
157
|
+
expressions within a database by add a polymorphic field to the
|
158
|
+
Role table. Because roles have polymorphic associations to an
|
159
|
+
authorizable object, we can assign a user to a role for any model
|
160
|
+
instance. So you could declare user X to be a moderator for workshop Y,
|
161
|
+
or you could make user A be the owner of resource B.
|
162
|
+
|
163
|
+
The identity module adds a number of dynamic methods that use defined
|
164
|
+
roles. The user-like model gets methods like `user.is_moderator_of
|
165
|
+
group (sets user to "moderator" of group`), user.is_moderator? (returns
|
166
|
+
true/false if user has some role "moderator"), and group.has_moderators
|
167
|
+
(returns an array of users that have role "moderator" for the group). If
|
168
|
+
you prefer not to have these dynamic methods available, you can simply
|
169
|
+
comment out the inclusion of the identity module within object_roles_table.rb.
|
170
|
+
|
171
|
+
=== Initial Configuration Instructions
|
172
|
+
|
173
|
+
Choose one of the installation types identified above and make sure your
|
174
|
+
application provides a current_user method or something that returns the
|
175
|
+
current user object (resful_authentication provides this out of the box).
|
176
|
+
|
177
|
+
At the top of your RAILS_ROOT/config/environment.rb file add something
|
178
|
+
like the following (customized for your controllers and actions of course):
|
179
|
+
|
180
|
+
...
|
181
|
+
|
182
|
+
# Authorization plugin for role based access control
|
183
|
+
# You can override default authorization system constants here.
|
184
|
+
|
185
|
+
# Can be 'object roles' or 'hardwired'
|
186
|
+
AUTHORIZATION_MIXIN = "object roles"
|
187
|
+
|
188
|
+
# NOTE : If you use modular controllers like '/admin/products' be sure
|
189
|
+
# to redirect to something like '/sessions' controller (with a leading slash)
|
190
|
+
# as shown in the example below or you will not get redirected properly
|
191
|
+
#
|
192
|
+
# This can be set to a hash or to an explicit path like '/login'
|
193
|
+
#
|
194
|
+
LOGIN_REQUIRED_REDIRECTION = { :controller => '/sessions', :action => 'new' }
|
195
|
+
PERMISSION_DENIED_REDIRECTION = { :controller => '/home', :action => 'index' }
|
196
|
+
|
197
|
+
# The method your auth scheme uses to store the location to redirect back to
|
198
|
+
STORE_LOCATION_METHOD = :store_location
|
199
|
+
|
200
|
+
# standard rails config below here
|
201
|
+
Rails::Initializer.run do |config|
|
202
|
+
|
203
|
+
...
|
204
|
+
|
205
|
+
* Set the AUTHORIZATION_MIXIN constant to object roles or hardwired. (See init.rb in this plugin for how the role support is mixed in.)
|
206
|
+
* Set the LOGIN_REQUIRED_REDIRECTION to match the path or a hash with the :controller and :action for your applications login page.
|
207
|
+
* Set the PERMISSION_DENIED_REDIRECTION to match the path or a hash with the :controller and :action for your applications permission denied page.
|
208
|
+
* Set the STORE_LOCATION_METHOD to the method your application uses for storing the current URL that the user should return to after authentication (e.g. store_location).
|
209
|
+
* See the PLUGIN_DIR\lib\authorization.rb file for the default values of LOGIN_REQUIRED_REDIRECTION, PERMISSION_DENIED_REDIRECTION and STORE_LOCATION_METHOD.
|
210
|
+
|
211
|
+
|
212
|
+
=== Create the database tables
|
213
|
+
|
214
|
+
If you plan to use the object roles method you will need to setup a few
|
215
|
+
database tables. We have provided a database migration file
|
216
|
+
(Rails 2.0+ compatible) that will make this process easy for you.
|
217
|
+
If you plan to use the hardwired mixin, no extra database tables
|
218
|
+
are required. and you can skip to the next step.
|
219
|
+
|
220
|
+
Run the following command from your RAILS_ROOT (Note : The generator
|
221
|
+
takes a model name as its argument, which at this time must be 'Role'.):
|
222
|
+
|
223
|
+
./script/generate role_model Role
|
224
|
+
|
225
|
+
This will create:
|
226
|
+
|
227
|
+
Model: RAILS_ROOT/app/models/role.rb
|
228
|
+
Test: RAILS_ROOT/test/unit/role_test.rb
|
229
|
+
Fixtures: RAILS_ROOT/test/fixtures/roles.yml
|
230
|
+
Migration: RAILS_ROOT/db/migrate/###_add_role.rb
|
231
|
+
|
232
|
+
And now you will need to run a database migration from your RAILS_ROOT:
|
233
|
+
|
234
|
+
rake db:migrate
|
235
|
+
|
236
|
+
=== Jumpstarting with a mixin
|
237
|
+
|
238
|
+
Now we need to add the methods needed by each of your models that will
|
239
|
+
participate in role based authorization. Typically these models fall into
|
240
|
+
two categories, the User model, and all other models that will have
|
241
|
+
roles available for use.
|
242
|
+
|
243
|
+
For a typical installation you would add both mixins to your User model.
|
244
|
+
|
245
|
+
class User < ActiveRecord::Base
|
246
|
+
|
247
|
+
# Authorization plugin
|
248
|
+
acts_as_authorized_user
|
249
|
+
acts_as_authorizable
|
250
|
+
|
251
|
+
...
|
252
|
+
|
253
|
+
Then in each additional model that you want to be able to restrict based
|
254
|
+
on role you would add just the acts_as_authorizable mixin like this:
|
255
|
+
|
256
|
+
class Event < ActiveRecord::Base
|
257
|
+
|
258
|
+
acts_as_authorizable
|
259
|
+
|
260
|
+
...
|
261
|
+
|
262
|
+
You are done with the configuration!
|
263
|
+
|
264
|
+
|
265
|
+
== The Specifics
|
266
|
+
|
267
|
+
=== permit and permit?
|
268
|
+
|
269
|
+
permit and permit? take an authorization expression and a hash of options that
|
270
|
+
typically includes any objects that need to be queried:
|
271
|
+
|
272
|
+
permit <authorization expression> [, options hash ]
|
273
|
+
permit? <authorization expression> [, options hash ]
|
274
|
+
|
275
|
+
The difference between permit and permit? is redirection. permit is a
|
276
|
+
declarative statement and redirects by default. It can also be used as a class
|
277
|
+
or an instance method, gating the access to an entire controller in a
|
278
|
+
before_filter fashion.
|
279
|
+
|
280
|
+
permit? is only an instance method, can be used within expressions, does not
|
281
|
+
redirect by default.
|
282
|
+
|
283
|
+
The authorization expression is a boolean expression made up of permitted
|
284
|
+
roles, prepositions, and authorizable models. Examples include "admin" (User
|
285
|
+
model assumed), "moderator of :workshop" (looks at options hash and then
|
286
|
+
@workshop), "'top salesman' at :company" (multiword roles delimited by single
|
287
|
+
quotes), or "scheduled for Exam" (queries class method of Exam).
|
288
|
+
|
289
|
+
Note that we can use several permitted prepositions ('of', 'for', 'in', 'on',
|
290
|
+
'to', 'at', 'by'). In the discussion below, we assume you use the "of"
|
291
|
+
preposition. You can modify the permitted prepositions by changing the constant
|
292
|
+
in Authorization::Base::Parser.
|
293
|
+
|
294
|
+
* If a specified role has no "of <model>" designation, we assume it is a user
|
295
|
+
role (i.e., the model is the user-like object).
|
296
|
+
|
297
|
+
* If an "of model" designation is given but no "model" key/value is supplied in
|
298
|
+
the hash, we check if an instance variable @model if it's available.
|
299
|
+
|
300
|
+
* If the model is capitalized, we assume it's a class and query
|
301
|
+
<tt>Model#self.accepts_role?</tt> (the class method) for the
|
302
|
+
permission. (Currently only available in ObjectRolesTable mixin.)
|
303
|
+
|
304
|
+
For each role, a query is sent to the appropriate model object.
|
305
|
+
|
306
|
+
The grammar for the authorization expression is:
|
307
|
+
|
308
|
+
<expr> ::= (<expr>) | not <expr> | <term> or <expr> | <term> and <expr> | <term>
|
309
|
+
<term> ::= <role> | <role> <preposition> <model>
|
310
|
+
<preposition> ::= of | for | in | on | to | at | by
|
311
|
+
<model> ::= /:*\w+/
|
312
|
+
<role> ::= /\w+/ | /'.*'/
|
313
|
+
|
314
|
+
Parentheses should be used to clarify permissions. Note that you may prefix the
|
315
|
+
model with an optional ":" -- the first versions of Authorization plugin made
|
316
|
+
this mandatory but it's now optional since the mandatory preposition makes
|
317
|
+
models unambiguous.
|
318
|
+
|
319
|
+
==== Options
|
320
|
+
|
321
|
+
:allow_guests => false.
|
322
|
+
|
323
|
+
We can allow permission processing without a
|
324
|
+
current user object. The default is false.
|
325
|
+
|
326
|
+
:user => YourUserObject.
|
327
|
+
|
328
|
+
The name of your user object.
|
329
|
+
|
330
|
+
:get_user_method => method_name
|
331
|
+
|
332
|
+
The method name provided should return a user
|
333
|
+
object. Default is #current_user, which is the how
|
334
|
+
acts_as_authenticated works.
|
335
|
+
|
336
|
+
:only => [ :method1, :method2 ]
|
337
|
+
|
338
|
+
Array of methods to apply permit (not valid when used in instance methods)
|
339
|
+
|
340
|
+
:except => [ :method1, :method2 ]
|
341
|
+
|
342
|
+
Array of methods that won't have permission checking (not valid when used in instance methods)
|
343
|
+
|
344
|
+
:redirect => boolean
|
345
|
+
|
346
|
+
Default is true. If false, permit will not redirect to denied page.
|
347
|
+
|
348
|
+
:login_required_redirection => path or hash
|
349
|
+
|
350
|
+
default is "{ :controller => 'session', :action => 'new' }"
|
351
|
+
|
352
|
+
Path or Hash where user will be redirected if not logged in ()
|
353
|
+
|
354
|
+
:login_required_message => 'my message'
|
355
|
+
|
356
|
+
A string to present to your users when login is required. Default is 'Login is required to access the requested page.'
|
357
|
+
|
358
|
+
:permission_denied_redirection => path or hash
|
359
|
+
|
360
|
+
Path or Hash where user will be redirected if logged in but not authorized (default is '')
|
361
|
+
|
362
|
+
:permission_denied_message => 'my message'
|
363
|
+
|
364
|
+
Message that will be presented to the user when permission is denied. Default is 'Permission
|
365
|
+
denied. You cannot access the requested page.'
|
366
|
+
|
367
|
+
=== Setting and getting the roles
|
368
|
+
|
369
|
+
Roles are set by #has_role and #accepts_role methods that are mixed into the
|
370
|
+
User-like object and the authorizable models. User objects can set roles and
|
371
|
+
optionally an object scope for that role:
|
372
|
+
|
373
|
+
user.has_role 'site_admin'
|
374
|
+
user.has_role 'moderator', group
|
375
|
+
user.has_no_role 'site_admin'
|
376
|
+
user.has_no_role 'moderator', group
|
377
|
+
user.has_role 'member', Group
|
378
|
+
|
379
|
+
Note that the last method sets role "member" on a class "Group". Roles can be
|
380
|
+
set with three scopes: entire application (no class or object specified), a
|
381
|
+
model class, or an instance of a model (i.e., a model object).
|
382
|
+
|
383
|
+
Models set roles for specific users:
|
384
|
+
|
385
|
+
a_model.accepts_role 'moderator', user
|
386
|
+
a_model.accepts_no_role 'moderator', user
|
387
|
+
Model.accepts_role 'class moderator', user
|
388
|
+
|
389
|
+
The method language has been chosen to aid memory of the argument order. A user
|
390
|
+
has a role "foo", so the role string immediately follows has_role. Similarly, a
|
391
|
+
model accepts a role "foo", so the role string immediately follows
|
392
|
+
accepts_role. Then we append the scope.
|
393
|
+
|
394
|
+
Sometimes the user-like object might be an authorizable object as well, for example, when you
|
395
|
+
allow 'friend' roles for users. In this case, the user-like object can be declared to be
|
396
|
+
<tt>acts_as_authorizable</tt> as well as <tt>acts_as_authorized_user</tt>.
|
397
|
+
|
398
|
+
Role queries follow the same pattern as the setting of roles:
|
399
|
+
|
400
|
+
user.has_role? 'moderator'
|
401
|
+
user.has_role? 'moderator', group
|
402
|
+
user.has_role? 'member', Group
|
403
|
+
|
404
|
+
a_model.accepts_role? 'moderator', user
|
405
|
+
Model.accepts_role? 'moderator', user
|
406
|
+
|
407
|
+
When a user is queried without specifying either a model class or object, it
|
408
|
+
returns true if the user has *any* matching role. For example,
|
409
|
+
<tt>user.has_role? 'moderator'</tt> returns true if the user is 'moderator' of
|
410
|
+
a class, a model object, or just a generic 'moderator'. Note that if you say
|
411
|
+
<tt>user.has_role 'moderator'</tt>, the user does not become 'moderator' for
|
412
|
+
all classes and model objects; the user simply has a generic role 'moderator'.
|
413
|
+
|
414
|
+
==== Dynamic methods through the Identity mixin
|
415
|
+
|
416
|
+
The Object Roles Table version includes some dynamic methods that use the roles
|
417
|
+
table. For example, if you have roles like "eligible", "moderator", and
|
418
|
+
"owner", you'll be able to use the following:
|
419
|
+
|
420
|
+
user.is_eligible_for_what --> returns array of authorizable objects for which user has role "eligible"
|
421
|
+
user.is_moderator_of? group --> returns true/false
|
422
|
+
user.is_moderator_of group --> sets user to have role "moderator" for object group.
|
423
|
+
user.is_administrator --> sets user to have role "administrator" not really tied to any object.
|
424
|
+
|
425
|
+
Models get has_* methods:
|
426
|
+
|
427
|
+
group.has_moderators --> returns array of users with role "moderator" on that group
|
428
|
+
group.has_moderators? --> returns true/false
|
429
|
+
|
430
|
+
Allowed prepositions are optional in the above dynamic methods. They are simply
|
431
|
+
syntactic sugar. For example, the following are equivalent:
|
432
|
+
|
433
|
+
user.is_member_of group
|
434
|
+
user.is_member_for group
|
435
|
+
user.is_member group
|
436
|
+
|
437
|
+
Allowed prepositions are required in the authorization expressions because they
|
438
|
+
are used to distinguish "role" and "role of :model" and "role of Model".
|
439
|
+
|
440
|
+
If you prefer not to pollute your namespace with these dynamic methods, do not
|
441
|
+
include the Identity module in <tt>object_roles_table.rb</tt>.
|
442
|
+
|
443
|
+
=== Pattern of use
|
444
|
+
|
445
|
+
We expect the application to provide the following methods:
|
446
|
+
|
447
|
+
==== #current_user
|
448
|
+
|
449
|
+
Returns some user object, like an instance of my favorite class,
|
450
|
+
<tt>UserFromMars</tt>. A <tt>user</tt> object, from the Authorization
|
451
|
+
viewpoint, is simply an object that provides a <tt>has_role?</tt> method.
|
452
|
+
|
453
|
+
Note that duck typing means we don't care what else the <tt>UserFromMars</tt>
|
454
|
+
might be doing. We only care that we can get an id from whatever it is, and we
|
455
|
+
can check if a given role string is associated with it. By using
|
456
|
+
<tt>acts_as_authorized_user</tt>, we inject what we need into the user object.
|
457
|
+
|
458
|
+
If you use an authorization expression "admin of :foo", we check permission by
|
459
|
+
asking <tt>foo</tt> if it <tt>accepts_role?('admin', user)</tt>. So for each
|
460
|
+
model that is used in an expression, we assume that it provides the
|
461
|
+
<tt>accepts_role?(role, user)</tt> method.
|
462
|
+
|
463
|
+
Note that <tt>user</tt> can be <tt>nil</tt> if <tt>:allow_guests => true</tt>.
|
464
|
+
|
465
|
+
==== #store_location (optional)
|
466
|
+
|
467
|
+
This method will be called if authorization fails and the user is about to be
|
468
|
+
redirected to the login action. This allows the application to return to the
|
469
|
+
desired page after login. If the application doesn't provide this method, the
|
470
|
+
method will not be called.
|
471
|
+
|
472
|
+
The name of the method for storing a location can be modified by changing the
|
473
|
+
constant STORE_LOCATION_METHOD in environment.rb. Also, the default login and
|
474
|
+
permission denied pages are defined by the constants LOGIN_REQUIRED_REDIRECTION
|
475
|
+
and PERMISSION_DENIED_REDIRECTION in authorization.rb and can be overriden in
|
476
|
+
your environment.rb.
|
477
|
+
|
478
|
+
=== Conventions
|
479
|
+
|
480
|
+
Roles specified without the "of model" designation:
|
481
|
+
|
482
|
+
1. We see if there is a <tt>current_user</tt> method available that will return
|
483
|
+
a user object. This method can be overridden with the <tt>:user</tt> hash.
|
484
|
+
|
485
|
+
2. Once a user object is determined, we pass the role to
|
486
|
+
<tt>user.has_role?</tt> and expect a true return value if the user has the
|
487
|
+
given role.
|
488
|
+
|
489
|
+
Roles specified with "of model" designation:
|
490
|
+
|
491
|
+
1. We attempt to query an object in the options hash that has a matching
|
492
|
+
key. Example: <tt>permit "knight for justice", :justice =>
|
493
|
+
@abstract_idea</tt>
|
494
|
+
|
495
|
+
2. If there is no object with a matching key, we see if there's a matching
|
496
|
+
instance variable. Example: @meeting defined before we use <tt>permit
|
497
|
+
"moderator of meeting"</tt>
|
498
|
+
|
499
|
+
3. Once the model object is determined, we pass the role and user (determined
|
500
|
+
in the manner above) to <tt>model.accepts_role?</tt>
|
501
|
+
|
502
|
+
|
503
|
+
== Upgrading
|
504
|
+
|
505
|
+
When upgrading this plugin, please refer to the following file:
|
506
|
+
|
507
|
+
http://github.com/DocSavage/rails-authorization-plugin/tree/master/UPGRADE.rdoc
|
508
|
+
|
509
|
+
|
510
|
+
== Developers Note : Contributing Patches
|
511
|
+
|
512
|
+
Please see the file README_developers.txt for the methods we would like you to use to submit new code features, bugfixes and patches.
|