has_roles 0.0.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +46 -0
- data/{MIT-LICENSE → LICENSE} +2 -2
- data/README.rdoc +92 -0
- data/Rakefile +49 -39
- data/app/models/permission.rb +48 -31
- data/app/models/role.rb +32 -24
- data/app/models/role_assignment.rb +5 -6
- data/app/models/role_permission.rb +25 -0
- data/db/migrate/{002_create_permissions.rb → 001_create_permissions.rb} +4 -5
- data/db/migrate/{003_create_roles.rb → 002_create_roles.rb} +1 -3
- data/db/migrate/003_create_role_permissions.rb +11 -0
- data/db/migrate/{005_create_role_assignments.rb → 004_create_role_assignments.rb} +2 -3
- data/lib/has_roles/authorization_helper.rb +66 -47
- data/lib/has_roles/url_helper.rb +38 -0
- data/lib/has_roles.rb +29 -56
- data/test/app_root/app/controllers/admin/base_controller.rb +2 -0
- data/test/app_root/app/controllers/admin/users_controller.rb +10 -1
- data/test/app_root/app/controllers/application_controller.rb +7 -0
- data/test/app_root/config/environment.rb +6 -19
- data/test/app_root/config/routes.rb +1 -0
- data/test/app_root/db/migrate/001_create_users.rb +1 -1
- data/test/app_root/db/migrate/002_migrate_has_roles_to_version_4.rb +13 -0
- data/test/factory.rb +63 -0
- data/test/functional/application_controller_test.rb +45 -0
- data/test/functional/has_roles_test.rb +68 -0
- data/test/helpers/authorization_helper_test.rb +92 -0
- data/test/helpers/url_helper_test.rb +35 -0
- data/test/test_helper.rb +13 -4
- data/test/unit/permission_test.rb +92 -27
- data/test/unit/role_assignment_test.rb +46 -15
- data/test/unit/role_permission_test.rb +34 -0
- data/test/unit/role_test.rb +122 -20
- metadata +93 -78
- data/CHANGELOG +0 -19
- data/README +0 -83
- data/app/models/controller.rb +0 -82
- data/db/migrate/001_create_controllers.rb +0 -13
- data/db/migrate/004_create_permissions_roles.rb +0 -13
- data/test/app_root/app/controllers/payment/pay_pal/transactions_controller.rb +0 -2
- data/test/app_root/app/controllers/payment/transactions_controller.rb +0 -2
- data/test/fixtures/controllers.yml +0 -19
- data/test/fixtures/permissions.yml +0 -27
- data/test/fixtures/permissions_roles.yml +0 -15
- data/test/fixtures/role_assignments.yml +0 -17
- data/test/fixtures/roles.yml +0 -11
- data/test/fixtures/users.yml +0 -11
- data/test/unit/authorization_helper_test.rb +0 -40
- data/test/unit/controller_test.rb +0 -95
- data/test/unit/has_roles_test.rb +0 -49
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
== master
|
2
|
+
|
3
|
+
== 0.3.0 / 2009-04-30
|
4
|
+
|
5
|
+
* Improve permission lookup performance
|
6
|
+
* Introduce RolePermission as the join model between Roles and Permissions
|
7
|
+
* Replace acts_as_enumeration with enumerate_by
|
8
|
+
* Add dependency on Rails 2.3
|
9
|
+
|
10
|
+
== 0.2.0 / 2008-12-14
|
11
|
+
|
12
|
+
* Remove the PluginAWeek namespace
|
13
|
+
|
14
|
+
== 0.1.2 / 2008-10-26
|
15
|
+
|
16
|
+
* Fix permissions for new roles always defaulting to empty even when it's passed in to the constructor
|
17
|
+
* Add authorized?, authorized_required, and authorization_denied helpers
|
18
|
+
* Moved authorized_for? helper method into the controller so that it can be used there as well as in views
|
19
|
+
* Change how the base module is included to prevent namespacing conflicts
|
20
|
+
|
21
|
+
== 0.1.1 / 2008-06-22
|
22
|
+
|
23
|
+
* Remove log files from gems
|
24
|
+
* Index permission enumerations by their path
|
25
|
+
|
26
|
+
== 0.1.0 / 2008-05-05
|
27
|
+
|
28
|
+
* Add use of named_scope instead of class finders
|
29
|
+
* Moved storage of controller paths into the Permission model
|
30
|
+
* Permission and Role are now enumerations (adding dependency on acts_as_enumeration)
|
31
|
+
* Removed unused helper methods in Permission and Role
|
32
|
+
* Added default permissions / roles
|
33
|
+
* Update documentation
|
34
|
+
|
35
|
+
== 0.0.2 / 2007-09-26
|
36
|
+
|
37
|
+
* Fix role_assignments unique index having too long a name
|
38
|
+
* Add workaround for old sqlite versions that can't handle :distinct => true
|
39
|
+
|
40
|
+
== 0.0.1 / 2007-09-05
|
41
|
+
|
42
|
+
* Add #role_ids and #role_ids=(new_ids) for models
|
43
|
+
* Added documentation
|
44
|
+
* Added helper methods for determining authorization within views
|
45
|
+
* Added unit tests
|
46
|
+
* Convert dos newlines to unix newlines
|
data/{MIT-LICENSE → LICENSE}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2006-
|
1
|
+
Copyright (c) 2006-2009 Aaron Pfeifer
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
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.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= has_roles
|
2
|
+
|
3
|
+
+has_roles+ demonstrates a reference implementation for handling role management.
|
4
|
+
|
5
|
+
== Resources
|
6
|
+
|
7
|
+
API
|
8
|
+
|
9
|
+
* http://api.pluginaweek.org/has_roles
|
10
|
+
|
11
|
+
Bugs
|
12
|
+
|
13
|
+
* http://pluginaweek.lighthouseapp.com/projects/13277-has_roles
|
14
|
+
|
15
|
+
Development
|
16
|
+
|
17
|
+
* http://github.com/pluginaweek/has_roles
|
18
|
+
|
19
|
+
Source
|
20
|
+
|
21
|
+
* git://github.com/pluginaweek/has_roles.git
|
22
|
+
|
23
|
+
== Description
|
24
|
+
|
25
|
+
One of the easiest and most straightforward techniques for adding role
|
26
|
+
management and authorization to specific parts of your application is
|
27
|
+
restricting usage on a controller/action-basis. Each role defined in your
|
28
|
+
system is mapped to one or more permissions. Each permission is a combination
|
29
|
+
of a controller and action.
|
30
|
+
|
31
|
+
== Usage
|
32
|
+
|
33
|
+
Note that this is a reference implementation and, most likely, should be
|
34
|
+
modified for your own usage.
|
35
|
+
|
36
|
+
=== Adding permissions
|
37
|
+
|
38
|
+
To add permissions, you can create an initializer like so:
|
39
|
+
|
40
|
+
config/initializers/permissions.rb:
|
41
|
+
|
42
|
+
Permission.bootstrap(
|
43
|
+
{:id => 1, :controller => 'application'},
|
44
|
+
{:id => 2, :controller => 'admin/stats'},
|
45
|
+
{:id => 3, :controller => 'comments', :action => 'create'},
|
46
|
+
...
|
47
|
+
)
|
48
|
+
|
49
|
+
=== Adding / Updating roles
|
50
|
+
|
51
|
+
To add / update roles, you can create an initializer like so:
|
52
|
+
|
53
|
+
config/initializers/roles.rb:
|
54
|
+
|
55
|
+
Role.bootstrap(
|
56
|
+
{:id => 1, :name => 'admin'},
|
57
|
+
{:id => 2, :name => 'developer'},
|
58
|
+
...
|
59
|
+
)
|
60
|
+
|
61
|
+
RolePermission.bootstrap(
|
62
|
+
{:role => 'admin', :permission => 'application/'},
|
63
|
+
{:role => 'admin', :permission => 'admin/states/'},
|
64
|
+
{:role => 'developer', :permission => 'comments/create'},
|
65
|
+
{:role => 'developer', :permission => 'admin/stats/'},
|
66
|
+
...
|
67
|
+
)
|
68
|
+
|
69
|
+
=== Checking a user's authorization
|
70
|
+
|
71
|
+
Below is an example of checking a user's authorization for a url before
|
72
|
+
displaying information:
|
73
|
+
|
74
|
+
app/views/layouts/application.rhtml:
|
75
|
+
|
76
|
+
<% if authorized_for?(:controller => 'admin/users') %>
|
77
|
+
<p>Read to start administering your website?</p>
|
78
|
+
<% end %>
|
79
|
+
|
80
|
+
== Testing
|
81
|
+
|
82
|
+
Before you can run any tests, the following gem must be installed:
|
83
|
+
* plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
|
84
|
+
|
85
|
+
To run against a specific version of Rails:
|
86
|
+
|
87
|
+
rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
|
88
|
+
|
89
|
+
== Dependencies
|
90
|
+
|
91
|
+
* Rails 2.3 or later
|
92
|
+
* enumerate_by[http://github.com/pluginaweek/enumerate_by]
|
data/Rakefile
CHANGED
@@ -3,45 +3,55 @@ require 'rake/rdoctask'
|
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/contrib/sshpublisher'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
spec = Gem::Specification.new do |s|
|
7
|
+
s.name = 'has_roles'
|
8
|
+
s.version = '0.3.0'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = 'Demonstrates a reference implementation for handling role management'
|
11
|
+
|
12
|
+
s.files = FileList['{app,db,lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
|
13
|
+
s.require_path = 'lib'
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.test_files = Dir['test/**/*_test.rb']
|
16
|
+
s.add_dependency 'enumerate_by', '>= 0.4.0'
|
17
|
+
|
18
|
+
s.author = 'Aaron Pfeifer'
|
19
|
+
s.email = 'aaron@pluginaweek.org'
|
20
|
+
s.homepage = 'http://www.pluginaweek.org'
|
21
|
+
s.rubyforge_project = 'pluginaweek'
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Default: run all tests.'
|
12
25
|
task :default => :test
|
13
26
|
|
14
|
-
desc
|
27
|
+
desc "Test the #{spec.name} plugin."
|
15
28
|
Rake::TestTask.new(:test) do |t|
|
16
29
|
t.libs << 'lib'
|
17
|
-
t.
|
30
|
+
t.test_files = spec.test_files
|
18
31
|
t.verbose = true
|
19
32
|
end
|
20
33
|
|
21
|
-
|
34
|
+
begin
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
namespace :test do
|
37
|
+
desc "Test the #{spec.name} plugin with Rcov."
|
38
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
39
|
+
t.libs << 'lib'
|
40
|
+
t.test_files = spec.test_files
|
41
|
+
t.rcov_opts << '--exclude="^(?!lib/|app/)"'
|
42
|
+
t.verbose = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Generate documentation for the #{spec.name} plugin."
|
22
49
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
23
50
|
rdoc.rdoc_dir = 'rdoc'
|
24
|
-
rdoc.title =
|
51
|
+
rdoc.title = spec.name
|
52
|
+
rdoc.template = '../rdoc_template.rb'
|
25
53
|
rdoc.options << '--line-numbers' << '--inline-source'
|
26
|
-
rdoc.rdoc_files.include('README')
|
27
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
28
|
-
end
|
29
|
-
|
30
|
-
spec = Gem::Specification.new do |s|
|
31
|
-
s.name = PKG_NAME
|
32
|
-
s.version = PKG_VERSION
|
33
|
-
s.platform = Gem::Platform::RUBY
|
34
|
-
s.summary = 'Provides simple role management based on controllers/actions'
|
35
|
-
|
36
|
-
s.files = FileList['{app,db,lib,test}/**/*'].to_a + %w(CHANGELOG init.rb MIT-LICENSE Rakefile README)
|
37
|
-
s.require_path = 'lib'
|
38
|
-
s.autorequire = 'has_roles'
|
39
|
-
s.has_rdoc = true
|
40
|
-
s.test_files = Dir['test/**/*_test.rb']
|
41
|
-
|
42
|
-
s.author = 'Aaron Pfeifer, Neil Abraham'
|
43
|
-
s.email = 'info@pluginaweek.org'
|
44
|
-
s.homepage = 'http://www.pluginaweek.org'
|
54
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb', 'app/**/*.rb')
|
45
55
|
end
|
46
56
|
|
47
57
|
Rake::GemPackageTask.new(spec) do |p|
|
@@ -50,30 +60,30 @@ Rake::GemPackageTask.new(spec) do |p|
|
|
50
60
|
p.need_zip = true
|
51
61
|
end
|
52
62
|
|
53
|
-
desc 'Publish the beta gem'
|
63
|
+
desc 'Publish the beta gem.'
|
54
64
|
task :pgem => [:package] do
|
55
|
-
Rake::SshFilePublisher.new('
|
65
|
+
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
|
56
66
|
end
|
57
67
|
|
58
|
-
desc 'Publish the API documentation'
|
68
|
+
desc 'Publish the API documentation.'
|
59
69
|
task :pdoc => [:rdoc] do
|
60
|
-
Rake::SshDirPublisher.new('
|
70
|
+
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
|
61
71
|
end
|
62
72
|
|
63
73
|
desc 'Publish the API docs and gem'
|
64
|
-
task :publish => [:pdoc, :release]
|
74
|
+
task :publish => [:pgem, :pdoc, :release]
|
65
75
|
|
66
76
|
desc 'Publish the release files to RubyForge.'
|
67
77
|
task :release => [:gem, :package] do
|
68
78
|
require 'rubyforge'
|
69
79
|
|
70
|
-
ruby_forge = RubyForge.new
|
80
|
+
ruby_forge = RubyForge.new.configure
|
71
81
|
ruby_forge.login
|
72
|
-
|
73
|
-
%w(
|
74
|
-
file = "pkg/#{
|
82
|
+
|
83
|
+
%w(gem tgz zip).each do |ext|
|
84
|
+
file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
|
75
85
|
puts "Releasing #{File.basename(file)}..."
|
76
86
|
|
77
|
-
ruby_forge.add_release(
|
87
|
+
ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
|
78
88
|
end
|
79
89
|
end
|
data/app/models/permission.rb
CHANGED
@@ -1,44 +1,61 @@
|
|
1
|
-
# A permission defines access to a single part of an application, restricted
|
2
|
-
# both controller and action specifications.
|
1
|
+
# A permission defines access to a single part of an application, restricted
|
2
|
+
# by both controller and action specifications.
|
3
3
|
#
|
4
|
-
# Permissions can be application-, controller-, or action-specific.
|
5
|
-
# using the +application+ controller are global.
|
6
|
-
# specified are controller-specific.
|
4
|
+
# Permissions can be application-, controller-, or action-specific. Those
|
5
|
+
# using the +application+ controller are global. Those without any +action+
|
6
|
+
# specified are controller-specific. Those with both +controller+ and
|
7
7
|
# +action+ specified are action-specific.
|
8
|
+
#
|
9
|
+
# == Examples
|
10
|
+
#
|
11
|
+
# Permission.bootstrap(
|
12
|
+
# {:id => 1, :controller => 'application'}, # Access to the whole application
|
13
|
+
# {:id => 2, :controller => 'users'}, # Access to the Users controller (any action)
|
14
|
+
# {:id => 3, :controller => 'users', :action => 'index'} # Access to the Users controller and index action
|
15
|
+
# )
|
8
16
|
class Permission < ActiveRecord::Base
|
9
|
-
|
10
|
-
|
17
|
+
enumerate_by :path
|
18
|
+
|
19
|
+
has_many :assigned_roles, :class_name => 'RolePermission'
|
20
|
+
has_many :roles, :through => :assigned_roles
|
21
|
+
|
22
|
+
validates_presence_of :controller
|
23
|
+
validates_length_of :action, :minimum => 1, :allow_nil => true
|
11
24
|
|
12
|
-
|
13
|
-
validates_length_of :action,
|
14
|
-
:minimum => 1,
|
15
|
-
:allow_nil => true
|
16
|
-
validates_uniqueness_of :action,
|
17
|
-
:scope => :controller_id,
|
18
|
-
:allow_nil => true
|
25
|
+
before_validation :set_path
|
19
26
|
|
20
27
|
class << self
|
21
|
-
# Is there a permission that exists which restricts the given url
|
22
|
-
#
|
28
|
+
# Is there a permission that exists which restricts the given url? If
|
29
|
+
# there is no permission that restricts the path, then anyone should be
|
30
|
+
# allowed access to it
|
23
31
|
def restricts?(options = '')
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
) > 0
|
32
|
+
controller, action = recognize_path(options)
|
33
|
+
|
34
|
+
# See if a permission exists for either the controller or controller/action
|
35
|
+
# combination. If it doesn't, then the path isn't restricted
|
36
|
+
exists?(:path => ["#{controller}/", "#{controller}/#{action}"])
|
30
37
|
end
|
31
38
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
# Parses the controller path and action from the given options. Options
|
40
|
+
# may be in any one of the following formats:
|
41
|
+
# * +string+ - A relative or absolute path in the application
|
42
|
+
# * +hash+ - A hash include the controller/action attributes
|
43
|
+
def recognize_path(options = '')
|
44
|
+
# Grab the actual url options if the path is specified
|
45
|
+
options = ActionController::Routing::Routes.recognize_path(URI.parse(options).path) if options.is_a?(String)
|
37
46
|
|
38
|
-
|
39
|
-
|
40
|
-
:conditions => ['path IN (?) AND (action IS NULL OR action = ?)', controller.possible_path_matches, action]
|
41
|
-
)
|
47
|
+
# Only return the controller/action of the url options
|
48
|
+
return options[:controller], options[:action] ? options[:action].to_s : 'index'
|
42
49
|
end
|
43
50
|
end
|
51
|
+
|
52
|
+
private
|
53
|
+
# Set full path for the controller / action
|
54
|
+
def set_path
|
55
|
+
self.path = "#{controller}/#{action}"
|
56
|
+
end
|
57
|
+
|
58
|
+
bootstrap(
|
59
|
+
{:id => 1, :controller => 'application'}
|
60
|
+
)
|
44
61
|
end
|
data/app/models/role.rb
CHANGED
@@ -1,30 +1,38 @@
|
|
1
1
|
# A role defines a group of users in the system who are able to access a
|
2
|
-
# collection of features in the application.
|
3
|
-
#
|
2
|
+
# collection of features in the application.
|
3
|
+
#
|
4
|
+
# == Examples
|
5
|
+
#
|
6
|
+
# Role.bootstrap(
|
7
|
+
# {:id => 1, :name => 'administrator'},
|
8
|
+
# {:id => 2, :name => 'developer'},
|
9
|
+
# {:id => 3, :name => 'guest'}
|
10
|
+
# )
|
4
11
|
class Role < ActiveRecord::Base
|
5
|
-
|
6
|
-
has_many :assignments,
|
7
|
-
:class_name => 'RoleAssignment',
|
8
|
-
:dependent => true
|
12
|
+
enumerate_by :name
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
has_many :assigned_permissions, :class_name => 'RolePermission'
|
15
|
+
has_many :permissions, :through => :assigned_permissions
|
16
|
+
has_many :assignments, :class_name => 'RoleAssignment'
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
# Is this role authorized for the given url? The url can be any one of the
|
19
|
+
# following formats:
|
20
|
+
# * +string+ - A relative or absolute path in the application
|
21
|
+
# * +hash+ - A hash include the controller/action attributes
|
22
|
+
#
|
23
|
+
# Using this information, the controller and action can be determined.
|
24
|
+
# Authorization is based on whether the role has a permission that either
|
25
|
+
# directly matches the path or represents a parent path (i.e. using the
|
26
|
+
# controller/class hierarchy)
|
27
|
+
named_scope :authorized_for, lambda {|*args|
|
28
|
+
options = args.first || ''
|
29
|
+
controller, action = Permission.recognize_path(options)
|
30
|
+
controllers = "#{controller.camelize}Controller".constantize.ancestors.select {|c| c < ActionController::Base}.map(&:controller_path)
|
31
|
+
|
32
|
+
{:joins => :permissions, :conditions => ['permissions.controller IN (?) AND (permissions.action IS NULL OR permissions.action = ?)', controllers, action]}
|
33
|
+
}
|
26
34
|
|
27
|
-
|
28
|
-
name
|
29
|
-
|
35
|
+
bootstrap(
|
36
|
+
{:id => 1, :name => 'admin'}
|
37
|
+
)
|
30
38
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# Represents an assignment of a role to a user (assignee) in the system
|
2
2
|
class RoleAssignment < ActiveRecord::Base
|
3
|
-
belongs_to
|
4
|
-
belongs_to
|
5
|
-
:polymorphic => true
|
3
|
+
belongs_to :role
|
4
|
+
belongs_to :assignee, :polymorphic => true
|
6
5
|
|
7
|
-
validates_presence_of :role_id,
|
8
|
-
|
9
|
-
|
6
|
+
validates_presence_of :role_id, :assignee_id, :assignee_type
|
7
|
+
|
8
|
+
attr_accessible :role
|
10
9
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Represents an assignment of a permission to a role. The permissions for a
|
2
|
+
# role determine what parts of the system that role has access to.
|
3
|
+
#
|
4
|
+
# == Examples
|
5
|
+
#
|
6
|
+
# RolePermission.bootstrap(
|
7
|
+
# {:role => 'admin', :permission => 'application/'},
|
8
|
+
# {:role => 'developer', :permission => 'comments/create'},
|
9
|
+
# {:role => 'developer', :permission => 'admin/stats/'}
|
10
|
+
# )
|
11
|
+
#
|
12
|
+
# Notice that permissions are in the form of the path defined by the
|
13
|
+
# controller/action.
|
14
|
+
class RolePermission < ActiveRecord::Base
|
15
|
+
extend EnumerateBy::Bootstrapped
|
16
|
+
|
17
|
+
belongs_to :role
|
18
|
+
belongs_to :permission
|
19
|
+
|
20
|
+
validates_presence_of :role_id, :permission_id
|
21
|
+
|
22
|
+
bootstrap(
|
23
|
+
{:role => 'admin', :permission => 'application/'}
|
24
|
+
)
|
25
|
+
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
class CreatePermissions < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
create_table :permissions do |t|
|
4
|
-
t.
|
5
|
-
t.
|
4
|
+
t.string :controller, :path, :null => false
|
5
|
+
t.string :action
|
6
6
|
end
|
7
|
-
add_index :permissions, [:controller_id, :action], :unique => true
|
8
7
|
end
|
9
|
-
|
8
|
+
|
10
9
|
def self.down
|
11
10
|
drop_table :permissions
|
12
11
|
end
|
13
|
-
end
|
12
|
+
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
class CreateRoles < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
create_table :roles do |t|
|
4
|
-
t.
|
5
|
-
t.column :description, :text
|
4
|
+
t.string :name, :null => false
|
6
5
|
end
|
7
|
-
add_index :roles, :name, :unique => true
|
8
6
|
end
|
9
7
|
|
10
8
|
def self.down
|
@@ -1,9 +1,8 @@
|
|
1
1
|
class CreateRoleAssignments < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
create_table :role_assignments do |t|
|
4
|
-
t.
|
5
|
-
t.
|
6
|
-
t.column :assignee_type, :string, :null => false
|
4
|
+
t.references :role, :null => false
|
5
|
+
t.references :assignee, :polymorphic => true, :null => false
|
7
6
|
end
|
8
7
|
add_index :role_assignments, [:role_id, :assignee_id, :assignee_type], :unique => true, :name => 'index_role_assignments_on_role_and_assignee'
|
9
8
|
end
|
@@ -1,56 +1,75 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module HasRoles
|
2
|
+
# Provides helper methods for determining whether users are authorized
|
3
|
+
# for a given url.
|
4
|
+
module AuthorizationHelper
|
5
|
+
def self.included(base) #:nodoc:
|
6
|
+
base.class_eval do
|
7
|
+
helper_method :authorized?
|
8
|
+
helper_method :authorized_for?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
# Is the user authorized for the current url? If there is no current
|
14
|
+
# user, then this will return false. If there *is* a current user, then
|
15
|
+
# this will return true/false depending on whether the user is assigned
|
16
|
+
# a role that has permission to access the current request url.
|
17
|
+
def authorized?
|
18
|
+
authorized_for?(request.request_uri)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks if the current user is authorized for the url specified by the
|
22
|
+
# given options. See <tt>HasRoles::InstanceMethods#authorized_for?</tt>
|
23
|
+
# for a description of the possible options that can be passed in.
|
24
|
+
#
|
25
|
+
# If there is no current user, then the authorization will fail.
|
26
|
+
#
|
27
|
+
# == Examples
|
28
|
+
#
|
29
|
+
# authorized_for?(:controller => 'admin/messages')
|
30
|
+
# authorized_for?(:controller => 'admin/messages', :action => 'destroy')
|
31
|
+
# authorized_for?('admin/messages')
|
32
|
+
# authorized_for?('http://localhost:3000/admin/messages')
|
33
|
+
def authorized_for?(options = {})
|
34
|
+
current_user && current_user.authorized_for?(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Ensures that the user is authorized for the current url. If not
|
38
|
+
# authorized, then a 401 "Unauthorized" response will be sent back.
|
6
39
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# is not already defined, it will assume that the current user is stored
|
10
|
-
# in the session as <tt>session['user']</tt>. If your application implements
|
11
|
-
# this differently, you can override it like so:
|
40
|
+
# This is useful for defining filters on resources that are protected
|
41
|
+
# based on the role of the current user. For example,
|
12
42
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# end
|
43
|
+
# class PostsController < ApplicationController
|
44
|
+
# before_filter :authorization_required, :only => [:destroy]
|
45
|
+
# ...
|
17
46
|
# end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def link_to_if_authorized(name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
|
41
|
-
text_on_no_authorization = html_options.delete(:show_text) ? name : ''
|
42
|
-
|
43
|
-
if authorized_for?(options)
|
44
|
-
link_to name, options, html_options, *parameters_for_method_reference, &block
|
45
|
-
else
|
46
|
-
text_on_no_authorization
|
47
|
-
end
|
48
|
-
end
|
47
|
+
#
|
48
|
+
# See #authorization_denied for the response given and how to provide
|
49
|
+
# a custom implementation for responding to unauthorized requests.
|
50
|
+
def authorization_required
|
51
|
+
authorized? || authorization_denied
|
52
|
+
end
|
53
|
+
|
54
|
+
# Renders an "unauthorized request" response to the user. By default,
|
55
|
+
# this is a simple HEAD response with the status code set to 401. This
|
56
|
+
# method should be overridden to provide a custom implementation for
|
57
|
+
# authorization denials, such as redirecting to a particular page:
|
58
|
+
#
|
59
|
+
# class ApplicationController < ActionController::Base
|
60
|
+
# ...
|
61
|
+
# protected
|
62
|
+
# def authorization_denied
|
63
|
+
# flash[:notice] = 'Unauthorized'
|
64
|
+
# redirect_to root_url
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
def authorization_denied
|
68
|
+
head :unauthorized
|
49
69
|
end
|
50
|
-
end
|
51
70
|
end
|
52
71
|
end
|
53
72
|
|
54
73
|
ActionController::Base.class_eval do
|
55
|
-
|
74
|
+
include HasRoles::AuthorizationHelper
|
56
75
|
end
|