has_roles 0.0.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG.rdoc +46 -0
  2. data/{MIT-LICENSE → LICENSE} +2 -2
  3. data/README.rdoc +92 -0
  4. data/Rakefile +49 -39
  5. data/app/models/permission.rb +48 -31
  6. data/app/models/role.rb +32 -24
  7. data/app/models/role_assignment.rb +5 -6
  8. data/app/models/role_permission.rb +25 -0
  9. data/db/migrate/{002_create_permissions.rb → 001_create_permissions.rb} +4 -5
  10. data/db/migrate/{003_create_roles.rb → 002_create_roles.rb} +1 -3
  11. data/db/migrate/003_create_role_permissions.rb +11 -0
  12. data/db/migrate/{005_create_role_assignments.rb → 004_create_role_assignments.rb} +2 -3
  13. data/lib/has_roles/authorization_helper.rb +66 -47
  14. data/lib/has_roles/url_helper.rb +38 -0
  15. data/lib/has_roles.rb +29 -56
  16. data/test/app_root/app/controllers/admin/base_controller.rb +2 -0
  17. data/test/app_root/app/controllers/admin/users_controller.rb +10 -1
  18. data/test/app_root/app/controllers/application_controller.rb +7 -0
  19. data/test/app_root/config/environment.rb +6 -19
  20. data/test/app_root/config/routes.rb +1 -0
  21. data/test/app_root/db/migrate/001_create_users.rb +1 -1
  22. data/test/app_root/db/migrate/002_migrate_has_roles_to_version_4.rb +13 -0
  23. data/test/factory.rb +63 -0
  24. data/test/functional/application_controller_test.rb +45 -0
  25. data/test/functional/has_roles_test.rb +68 -0
  26. data/test/helpers/authorization_helper_test.rb +92 -0
  27. data/test/helpers/url_helper_test.rb +35 -0
  28. data/test/test_helper.rb +13 -4
  29. data/test/unit/permission_test.rb +92 -27
  30. data/test/unit/role_assignment_test.rb +46 -15
  31. data/test/unit/role_permission_test.rb +34 -0
  32. data/test/unit/role_test.rb +122 -20
  33. metadata +93 -78
  34. data/CHANGELOG +0 -19
  35. data/README +0 -83
  36. data/app/models/controller.rb +0 -82
  37. data/db/migrate/001_create_controllers.rb +0 -13
  38. data/db/migrate/004_create_permissions_roles.rb +0 -13
  39. data/test/app_root/app/controllers/payment/pay_pal/transactions_controller.rb +0 -2
  40. data/test/app_root/app/controllers/payment/transactions_controller.rb +0 -2
  41. data/test/fixtures/controllers.yml +0 -19
  42. data/test/fixtures/permissions.yml +0 -27
  43. data/test/fixtures/permissions_roles.yml +0 -15
  44. data/test/fixtures/role_assignments.yml +0 -17
  45. data/test/fixtures/roles.yml +0 -11
  46. data/test/fixtures/users.yml +0 -11
  47. data/test/unit/authorization_helper_test.rb +0 -40
  48. data/test/unit/controller_test.rb +0 -95
  49. 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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2007 Aaron Pfeifer & Neil Abraham
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
- PKG_NAME = 'has_roles'
7
- PKG_VERSION = '0.0.2'
8
- PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
9
- RUBY_FORGE_PROJECT = 'pluginaweek'
10
-
11
- desc 'Default: run unit tests.'
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 'Test the has_roles plugin.'
27
+ desc "Test the #{spec.name} plugin."
15
28
  Rake::TestTask.new(:test) do |t|
16
29
  t.libs << 'lib'
17
- t.pattern = 'test/**/*_test.rb'
30
+ t.test_files = spec.test_files
18
31
  t.verbose = true
19
32
  end
20
33
 
21
- desc 'Generate documentation for the has_roles plugin.'
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 = 'HasRoles'
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('pluginaweek@pluginaweek.org', '/home/pluginaweek/gems.pluginaweek.org/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
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('pluginaweek@pluginaweek.org', "/home/pluginaweek/api.pluginaweek.org/#{PKG_NAME}", 'rdoc').upload
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( gem tgz zip ).each do |ext|
74
- file = "pkg/#{PKG_FILE_NAME}.#{ext}"
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(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
87
+ ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
78
88
  end
79
89
  end
@@ -1,44 +1,61 @@
1
- # A permission defines access to a single part of an application, restricted by
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. Permissions
5
- # using the +application+ controller are global. Permissions without any +action+
6
- # specified are controller-specific. Permissions with both +controller+ and
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
- belongs_to :controller
10
- has_and_belongs_to_many :roles
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
- validates_presence_of :controller_id
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?. See
22
- # <tt>Controller#recognize_path</tt> for possible options.
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
- controller_path, action = Controller.recognize_path(options)
25
- count(
26
- :include => :controller,
27
- :conditions => ['path = ? AND (action IS NULL OR action = ?)', controller_path, action],
28
- :distinct => true # TODO: Workaround for old sqlite versions until Rails 1.2
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
- # Finds all permissions that are authorized for the given url. See
33
- # <tt>Controller#recognize_path</tt> for possible options.
34
- def find_all_authorized_for(options = '')
35
- controller_path, action = Controller.recognize_path(options)
36
- controller = Controller.new(:path => controller_path)
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
- find(:all,
39
- :include => :controller,
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. Examples of roles could be
3
- # 'Administrator', 'Developer', or 'Guest'.
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
- has_and_belongs_to_many :permissions
6
- has_many :assignments,
7
- :class_name => 'RoleAssignment',
8
- :dependent => true
12
+ enumerate_by :name
9
13
 
10
- validates_presence_of :name
11
- validates_uniqueness_of :name,
12
- :allow_nil => true
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
- class << self
15
- # Finds all roles that are authorized for the given url
16
- def find_all_authorized_for(options = '')
17
- controller_path, action = Controller.recognize_path(options)
18
- controller = Controller.new(:path => controller_path)
19
-
20
- find(:all,
21
- :include => {:permissions => :controller},
22
- :conditions => ['path IN (?) AND (action IS NULL OR action = ?)', controller.possible_path_matches, action]
23
- )
24
- end
25
- end
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
- def to_s #:nodoc
28
- name
29
- end
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 :role
4
- belongs_to :assignee,
5
- :polymorphic => true
3
+ belongs_to :role
4
+ belongs_to :assignee, :polymorphic => true
6
5
 
7
- validates_presence_of :role_id,
8
- :assignee_id,
9
- :assignee_type
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.column :controller_id, :integer, :null => false
5
- t.column :action, :string
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.column :name, :string, :null => false
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
@@ -0,0 +1,11 @@
1
+ class CreateRolePermissions < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :role_permissions do |t|
4
+ t.references :role, :permission, :null => false
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :role_permissions
10
+ end
11
+ end
@@ -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.column :role_id, :integer, :null => false
5
- t.column :assignee_id, :integer, :null => false
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 PluginAWeek #:nodoc:
2
- module Has #:nodoc:
3
- module Roles
4
- # Provides helper methods for determining whether users are authorized
5
- # for a given url.
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
- # In order to determine who the current user is, the helper methods
8
- # assume that a +current_user+ helper has been defined. If this method
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
- # module ApplicationHelper
14
- # def current_user
15
- # session['user_id'] ? User.find(session['user_id']) : nil
16
- # end
43
+ # class PostsController < ApplicationController
44
+ # before_filter :authorization_required, :only => [:destroy]
45
+ # ...
17
46
  # end
18
- module AuthorizationHelper
19
- def self.included(base) #:nodoc:
20
- unless base.instance_methods.include?('current_user')
21
- base.class_eval do
22
- def current_user
23
- session['user']
24
- end
25
- end
26
- end
27
- end
28
-
29
- # Check if the user is authorized for the url specified by the given options.
30
- # See <tt>Controller#recognize_path</tt> for a description of the possible
31
- # options that can be passed in.
32
- def authorized_for?(options = {})
33
- current_user && current_user.authorized_for?(options)
34
- end
35
-
36
- # Only link to the url if the user is authorized to access it. In
37
- # addition to the options normally available in +link_to+, the following
38
- # options can be specified:
39
- # * +show_text+ - If set to true, will only display the text if the user is not authorized for the link. (Default is +false+).
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
- helper PluginAWeek::Has::Roles::AuthorizationHelper
74
+ include HasRoles::AuthorizationHelper
56
75
  end