has_roles 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +19 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +83 -0
  4. data/Rakefile +79 -0
  5. data/app/models/controller.rb +82 -0
  6. data/app/models/permission.rb +44 -0
  7. data/app/models/role.rb +30 -0
  8. data/app/models/role_assignment.rb +10 -0
  9. data/db/migrate/001_create_controllers.rb +13 -0
  10. data/db/migrate/002_create_permissions.rb +13 -0
  11. data/db/migrate/003_create_roles.rb +13 -0
  12. data/db/migrate/004_create_permissions_roles.rb +13 -0
  13. data/db/migrate/005_create_role_assignments.rb +14 -0
  14. data/init.rb +1 -0
  15. data/lib/has_roles.rb +65 -0
  16. data/lib/has_roles/authorization_helper.rb +56 -0
  17. data/test/app_root/app/controllers/admin/users_controller.rb +2 -0
  18. data/test/app_root/app/controllers/home_controller.rb +2 -0
  19. data/test/app_root/app/controllers/payment/pay_pal/transactions_controller.rb +2 -0
  20. data/test/app_root/app/controllers/payment/transactions_controller.rb +2 -0
  21. data/test/app_root/app/controllers/users_controller.rb +2 -0
  22. data/test/app_root/app/models/user.rb +3 -0
  23. data/test/app_root/config/environment.rb +25 -0
  24. data/test/app_root/config/routes.rb +3 -0
  25. data/test/app_root/db/migrate/001_create_users.rb +11 -0
  26. data/test/fixtures/controllers.yml +19 -0
  27. data/test/fixtures/permissions.yml +27 -0
  28. data/test/fixtures/permissions_roles.yml +15 -0
  29. data/test/fixtures/role_assignments.yml +17 -0
  30. data/test/fixtures/roles.yml +11 -0
  31. data/test/fixtures/users.yml +11 -0
  32. data/test/test_helper.rb +9 -0
  33. data/test/unit/authorization_helper_test.rb +40 -0
  34. data/test/unit/controller_test.rb +95 -0
  35. data/test/unit/has_roles_test.rb +49 -0
  36. data/test/unit/permission_test.rb +53 -0
  37. data/test/unit/role_assignment_test.rb +29 -0
  38. data/test/unit/role_test.rb +38 -0
  39. metadata +103 -0
@@ -0,0 +1,19 @@
1
+ *SVN*
2
+
3
+ *0.0.2* (September 26th, 2007)
4
+
5
+ * Fix role_assignments unique index having too long a name
6
+
7
+ * Add workaround for old sqlite versions that can't handle :distinct => true
8
+
9
+ *0.0.1* (September 5th, 2007)
10
+
11
+ * Add #role_ids and #role_ids=(new_ids) for models
12
+
13
+ * Added documentation
14
+
15
+ * Added helper methods for determining authorization within views
16
+
17
+ * Added unit tests
18
+
19
+ * Convert dos newlines to unix newlines
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2007 Aaron Pfeifer & Neil Abraham
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 ADDED
@@ -0,0 +1,83 @@
1
+ = has_roles
2
+
3
+ +has_roles+ provides simple role management based on controllers/actions.
4
+
5
+ == Resources
6
+
7
+ API
8
+
9
+ * http://api.pluginaweek.org/has_roles
10
+
11
+ Wiki
12
+
13
+ * http://wiki.pluginaweek.org/Has_roles
14
+
15
+ Announcement
16
+
17
+ * http://www.pluginaweek.org
18
+
19
+ Source
20
+
21
+ * http://svn.pluginaweek.org/trunk/plugins/active_record/has/has_roles
22
+
23
+ Development
24
+
25
+ * http://dev.pluginaweek.org/browser/trunk/plugins/active_record/has/has_roles
26
+
27
+ == Description
28
+
29
+ One of the easiest and most straightforward techniques for adding role management
30
+ and authorization to specific parts of your application is restricting usage on
31
+ controller/action-basis. Each role defined in your system is mapped to one or
32
+ more permissions. Each permission is a combination of a controller and action.
33
+
34
+ == Usage
35
+
36
+ === Checking a user's authorization
37
+
38
+ Below is an example of checking a user's authorization for a url before display
39
+ information:
40
+
41
+ app/views/layouts/application.rhtml:
42
+
43
+ <% if authorized_for?(:controller => 'admin/users') -%>
44
+ <p>Read to start administering your website?</p>
45
+ <% end -%>
46
+
47
+ === Global authorization
48
+
49
+ You can define a global permission that will add access for all controllers by
50
+ defining a controller with the path 'application'. See the test fixtures for
51
+ more information.
52
+
53
+ === Running migrations
54
+
55
+ To migrate the tables required for this plugin, you can either run the
56
+ migration from the command line like so:
57
+
58
+ rake db:migrate:plugins PLUGIN=has_roles
59
+
60
+ or (more ideally) generate a migration file that will integrate into your main
61
+ application's migration path:
62
+
63
+ ruby script/generate plugin_migration has_roles
64
+
65
+ == Testing
66
+
67
+ Before you can run any tests, the following gems must be installed:
68
+ * plugin_test_helper[http://wiki.pluginaweek.org/Plugin_test_helper]
69
+ * dry_validity_assertions[http://wiki.pluginaweek.org/Dry_validity_assertions]
70
+
71
+ == Dependencies
72
+
73
+ This plugin is a plugin+. That means that it contains a slice of an
74
+ application, such as models and migrations. To test or use a plugin+, you
75
+ must have the following plugins/gems installed:
76
+ * plugin_dependencies[http://wiki.pluginaweek.org/Plugin_dependencies]
77
+ * loaded_plugins[http://wiki.pluginaweek.org/Loaded_plugins]
78
+ * appable_plugins[http://wiki.pluginaweek.org/Appable_plugins]
79
+ * plugin_migrations[http://wiki.pluginaweek.org/Plugin_migrations]
80
+
81
+ Instead of installing each individual plugin+ feature, you can install them all
82
+ at once using the plugins+[http://wiki.pluginaweek.org/Plugins_plus] meta package,
83
+ which contains all additional features.
@@ -0,0 +1,79 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
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.'
12
+ task :default => :test
13
+
14
+ desc 'Test the has_roles plugin.'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'lib'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = true
19
+ end
20
+
21
+ desc 'Generate documentation for the has_roles plugin.'
22
+ Rake::RDocTask.new(:rdoc) do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'HasRoles'
25
+ 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'
45
+ end
46
+
47
+ Rake::GemPackageTask.new(spec) do |p|
48
+ p.gem_spec = spec
49
+ p.need_tar = true
50
+ p.need_zip = true
51
+ end
52
+
53
+ desc 'Publish the beta gem'
54
+ task :pgem => [:package] do
55
+ Rake::SshFilePublisher.new('pluginaweek@pluginaweek.org', '/home/pluginaweek/gems.pluginaweek.org/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
56
+ end
57
+
58
+ desc 'Publish the API documentation'
59
+ task :pdoc => [:rdoc] do
60
+ Rake::SshDirPublisher.new('pluginaweek@pluginaweek.org', "/home/pluginaweek/api.pluginaweek.org/#{PKG_NAME}", 'rdoc').upload
61
+ end
62
+
63
+ desc 'Publish the API docs and gem'
64
+ task :publish => [:pdoc, :release]
65
+
66
+ desc 'Publish the release files to RubyForge.'
67
+ task :release => [:gem, :package] do
68
+ require 'rubyforge'
69
+
70
+ ruby_forge = RubyForge.new
71
+ ruby_forge.login
72
+
73
+ %w( gem tgz zip ).each do |ext|
74
+ file = "pkg/#{PKG_FILE_NAME}.#{ext}"
75
+ puts "Releasing #{File.basename(file)}..."
76
+
77
+ ruby_forge.add_release(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
78
+ end
79
+ end
@@ -0,0 +1,82 @@
1
+ # Represents the name of a controller in the application. The path of the controller
2
+ # includes its namespace, if any. For example:
3
+ #
4
+ # users # => UsersController
5
+ # admin/users # => Admin::UsersController
6
+ class Controller < ActiveRecord::Base
7
+ has_many :permissions,
8
+ :dependent => :destroy
9
+
10
+ validates_presence_of :path
11
+ validates_format_of :path,
12
+ :with => /^[a-z]+(\/[a-z]+)*$/i,
13
+ :allow_nil => true
14
+ validates_uniqueness_of :path,
15
+ :allow_nil => true
16
+
17
+ class << self
18
+ # Finds all of the permissioned controllers and maps/orders them by parent
19
+ def find_all_mapped_by_parent
20
+ controllers_by_parent = Controller.find(:all).inject(ActiveSupport::OrderedHash.new) do |controllers, controller|
21
+ controller.klass.parents.each {|parent| controllers[parent] ||= []}
22
+ controllers[controller.klass.parent] << controller
23
+ controllers
24
+ end
25
+
26
+ # Sort all the controllers within each parent alphabetically
27
+ controllers_by_parent.each do |parent, controllers|
28
+ controllers.sort! do |c1, c2|
29
+ c1.name <=> c2.name
30
+ end
31
+ end
32
+
33
+ # Sort the parents (first by closest to Object, then alphabetically)
34
+ controllers_by_parent.sort! do |c1, c2|
35
+ if c1.first == Object
36
+ -1
37
+ elsif c2.first == Object
38
+ 1
39
+ else
40
+ c1.first.name <=> c2.first.name
41
+ end
42
+ end
43
+ end
44
+
45
+ # Parses the controller path and action from the given options
46
+ def recognize_path(options = '')
47
+ options = ActionController::Routing::Routes.recognize_path(URI.parse(options).path) if options.is_a?(String)
48
+ controller_path, action = options[:controller], options[:action]
49
+ controller_path = controller_path.controller_path if controller_path.is_a?(Class)
50
+
51
+ return controller_path, action ? action.to_s : 'index'
52
+ end
53
+ end
54
+
55
+ # Returns the class that this controller represents
56
+ def klass
57
+ @klass ||= "#{path.camelize}Controller".constantize
58
+ end
59
+
60
+ # The humanized name of this controller. This will not include the parent's name.
61
+ def name
62
+ @name ||= klass.controller_name.titleize
63
+ end
64
+
65
+ # Possible paths that can match this controller. This includes paths from all
66
+ # superclasses up to ApplicationController.
67
+ def possible_path_matches
68
+ klass.ancestors.select {|c| Class === c && c < ActionController::Base}.map(&:controller_path)
69
+ end
70
+
71
+ # Is this a global controller? If a user has a permission on the global
72
+ # controller, then he has permissions on all controllers. At least one user
73
+ # in the system should have global access.
74
+ def global?
75
+ path == 'application'
76
+ end
77
+
78
+ # Returns the name of the controller
79
+ def to_s #:nodoc
80
+ name
81
+ end
82
+ end
@@ -0,0 +1,44 @@
1
+ # A permission defines access to a single part of an application, restricted by
2
+ # both controller and action specifications.
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
7
+ # +action+ specified are action-specific.
8
+ class Permission < ActiveRecord::Base
9
+ belongs_to :controller
10
+ has_and_belongs_to_many :roles
11
+
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
19
+
20
+ class << self
21
+ # Is there a permission that exists which restricts the given url?. See
22
+ # <tt>Controller#recognize_path</tt> for possible options.
23
+ 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
30
+ end
31
+
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)
37
+
38
+ find(:all,
39
+ :include => :controller,
40
+ :conditions => ['path IN (?) AND (action IS NULL OR action = ?)', controller.possible_path_matches, action]
41
+ )
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
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'.
4
+ class Role < ActiveRecord::Base
5
+ has_and_belongs_to_many :permissions
6
+ has_many :assignments,
7
+ :class_name => 'RoleAssignment',
8
+ :dependent => true
9
+
10
+ validates_presence_of :name
11
+ validates_uniqueness_of :name,
12
+ :allow_nil => true
13
+
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
26
+
27
+ def to_s #:nodoc
28
+ name
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ # Represents an assignment of a role to a user (assignee) in the system
2
+ class RoleAssignment < ActiveRecord::Base
3
+ belongs_to :role
4
+ belongs_to :assignee,
5
+ :polymorphic => true
6
+
7
+ validates_presence_of :role_id,
8
+ :assignee_id,
9
+ :assignee_type
10
+ end
@@ -0,0 +1,13 @@
1
+ class CreateControllers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :controllers do |t|
4
+ t.column :path, :string, :null => false
5
+ t.column :description, :text
6
+ end
7
+ add_index :controllers, :path, :unique => true
8
+ end
9
+
10
+ def self.down
11
+ drop_table :controllers
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePermissions < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :permissions do |t|
4
+ t.column :controller_id, :integer, :null => false
5
+ t.column :action, :string
6
+ end
7
+ add_index :permissions, [:controller_id, :action], :unique => true
8
+ end
9
+
10
+ def self.down
11
+ drop_table :permissions
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateRoles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :roles do |t|
4
+ t.column :name, :string, :null => false
5
+ t.column :description, :text
6
+ end
7
+ add_index :roles, :name, :unique => true
8
+ end
9
+
10
+ def self.down
11
+ drop_table :roles
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePermissionsRoles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :permissions_roles, :id => false do |t|
4
+ t.column :permission_id, :integer, :null => false
5
+ t.column :role_id, :integer, :null => false
6
+ end
7
+ add_index :permissions_roles, [:permission_id, :role_id], :unique => true
8
+ end
9
+
10
+ def self.down
11
+ drop_table :permissions_roles
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class CreateRoleAssignments < ActiveRecord::Migration
2
+ def self.up
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
7
+ end
8
+ add_index :role_assignments, [:role_id, :assignee_id, :assignee_type], :unique => true, :name => 'index_role_assignments_on_role_and_assignee'
9
+ end
10
+
11
+ def self.down
12
+ drop_table :role_assignments
13
+ end
14
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_roles'
@@ -0,0 +1,65 @@
1
+ require 'has_roles/authorization_helper'
2
+
3
+ module PluginAWeek #:nodoc:
4
+ module Has #:nodoc:
5
+ # Provides dead simple role management
6
+ module Roles
7
+ def self.included(base) #:nodoc:
8
+ base.extend(MacroMethods)
9
+ end
10
+
11
+ module MacroMethods
12
+ # Indicates that the model has roles
13
+ def has_roles
14
+ has_many :role_assignments,
15
+ :class_name => 'RoleAssignment',
16
+ :as => :assignee,
17
+ :dependent => :destroy
18
+ has_many :roles,
19
+ :through => :role_assignments
20
+
21
+ include PluginAWeek::Has::Roles::InstanceMethods
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ # Checks whether this user is authorized to access the given url.
27
+ # Caching of authorizations is baked into the method. So long as the
28
+ # same user object is used, an authorization for the same path will be
29
+ # cached and returned.
30
+ #
31
+ # See <tt>Controller#recognize_path</tt> for more information about the possible
32
+ # options that can be used.
33
+ def authorized_for?(options = '')
34
+ @authorizations ||= {}
35
+
36
+ controller_path, action = Controller.recognize_path(options)
37
+ options = {:controller => controller_path, :action => action}
38
+ @authorizations["#{controller_path}/#{action}"] ||= Permission.restricts?(options) ? roles.find_all_authorized_for(options).any? : true
39
+ end
40
+
41
+ # Gets the ids of all roles associated with this user
42
+ def role_ids
43
+ roles.map(&:id)
44
+ end
45
+
46
+ # Sets the topics to associate with this fact.
47
+ def role_ids=(ids)
48
+ removed_ids = role_ids - ids
49
+ new_ids = ids - role_ids
50
+
51
+ transaction do
52
+ role_assignments.delete(role_assignments.select {|assignment| removed_ids.include?(assignment.role_id)})
53
+ new_ids.each {|id| role_assignments.create!(:role_id => id)}
54
+ end
55
+
56
+ roles.reload
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ ActiveRecord::Base.class_eval do
64
+ include PluginAWeek::Has::Roles
65
+ end
@@ -0,0 +1,56 @@
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.
6
+ #
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:
12
+ #
13
+ # module ApplicationHelper
14
+ # def current_user
15
+ # session['user_id'] ? User.find(session['user_id']) : nil
16
+ # end
17
+ # 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
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ActionController::Base.class_eval do
55
+ helper PluginAWeek::Has::Roles::AuthorizationHelper
56
+ end
@@ -0,0 +1,2 @@
1
+ class Admin::UsersController < ApplicationController
2
+ end
@@ -0,0 +1,2 @@
1
+ class HomeController < ApplicationController
2
+ end
@@ -0,0 +1,2 @@
1
+ class Payment::PayPal::TransactionsController < Payment::TransactionsController
2
+ end
@@ -0,0 +1,2 @@
1
+ class Payment::TransactionsController < ApplicationController
2
+ end
@@ -0,0 +1,2 @@
1
+ class UsersController < ApplicationController
2
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_roles
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'config/boot'
2
+
3
+ $:.unshift("#{RAILS_ROOT}/../../../../../rails/plugin_dependencies/lib")
4
+ begin
5
+ require 'plugin_dependencies'
6
+ rescue Exception => e
7
+ end
8
+
9
+ Rails::Initializer.run do |config|
10
+ config.plugin_paths.concat([
11
+ "#{RAILS_ROOT}/../../..",
12
+ "#{RAILS_ROOT}/../../../../migrations",
13
+ "#{RAILS_ROOT}/../../../../../rails",
14
+ "#{RAILS_ROOT}/../../../../../test"
15
+ ])
16
+ config.plugins = [
17
+ 'loaded_plugins',
18
+ 'appable_plugins',
19
+ 'plugin_migrations',
20
+ File.basename(File.expand_path("#{RAILS_ROOT}/../..")),
21
+ 'dry_validity_assertions'
22
+ ]
23
+ config.cache_classes = false
24
+ config.whiny_nils = true
25
+ end
@@ -0,0 +1,3 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.connect ':controller/:action/:id'
3
+ end
@@ -0,0 +1,11 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.column :login, :string, :null => false
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :users
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ application:
2
+ id: 1
3
+ path: application
4
+
5
+ users:
6
+ id: 2
7
+ path: users
8
+
9
+ admin_users:
10
+ id: 3
11
+ path: admin/users
12
+
13
+ payment_transactions:
14
+ id: 4
15
+ path: payment/transactions
16
+
17
+ payment_pay_pal_transactions:
18
+ id: 5
19
+ path: payment/pay_pal/transactions
@@ -0,0 +1,27 @@
1
+ crud_application:
2
+ id: 1
3
+ controller_id: 1
4
+
5
+ read_application:
6
+ id: 2
7
+ controller_id: 1
8
+ action: index
9
+
10
+ read_users:
11
+ id: 3
12
+ controller_id: 2
13
+ action: index
14
+
15
+ update_users:
16
+ id: 4
17
+ controller_id: 2
18
+ action: update
19
+
20
+ crud_admin_users:
21
+ id: 5
22
+ controller_id: 3
23
+
24
+ create_pay_pal_payment_transactions:
25
+ id: 6
26
+ controller_id: 5
27
+ action: create
@@ -0,0 +1,15 @@
1
+ administrator_crud_application:
2
+ role_id: 1
3
+ permission_id: 1
4
+
5
+ moderator_crud_admin_users:
6
+ role_id: 2
7
+ permission_id: 5
8
+
9
+ guest_read_application:
10
+ role_id: 3
11
+ permission_id: 2
12
+
13
+ guest_create_pay_pal_payment_transactions:
14
+ role_id: 3
15
+ permission_id: 6
@@ -0,0 +1,17 @@
1
+ administrator:
2
+ id: 1
3
+ role_id: 1
4
+ assignee_id: 1
5
+ assignee_type: User
6
+
7
+ moderator:
8
+ id: 2
9
+ role_id: 2
10
+ assignee_id: 2
11
+ assignee_type: User
12
+
13
+ guest:
14
+ id: 3
15
+ role_id: 3
16
+ assignee_id: 3
17
+ assignee_type: User
@@ -0,0 +1,11 @@
1
+ administrator:
2
+ id: 1
3
+ name: Administrator
4
+
5
+ moderator:
6
+ id: 2
7
+ name: Moderator
8
+
9
+ guest:
10
+ id: 3
11
+ name: Guest
@@ -0,0 +1,11 @@
1
+ administrator:
2
+ id: 1
3
+ login: admin
4
+
5
+ moderator:
6
+ id: 2
7
+ login: moderator
8
+
9
+ guest:
10
+ id: 3
11
+ login: guest
@@ -0,0 +1,9 @@
1
+ # Load the plugin testing framework
2
+ $:.unshift("#{File.dirname(__FILE__)}/../../../../test/plugin_test_helper/lib")
3
+ require 'rubygems'
4
+ require 'plugin_test_helper'
5
+
6
+ PluginAWeek::PluginMigrations.migrate('has_roles')
7
+
8
+ # Run the migrations
9
+ ActiveRecord::Migrator.migrate("#{RAILS_ROOT}/db/migrate")
@@ -0,0 +1,40 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class AuthorizationHelperTest < Test::Unit::TestCase
4
+ include ActionView::Helpers::TagHelper
5
+ include ActionView::Helpers::UrlHelper
6
+ include PluginAWeek::Has::Roles::AuthorizationHelper
7
+
8
+ fixtures :controllers, :permissions, :roles, :permissions_roles, :users, :role_assignments
9
+
10
+ def setup
11
+ @controller = HomeController.new
12
+ @controller.request = ActionController::TestRequest.new
13
+ @controller.instance_eval {@_params = request.path_parameters}
14
+ @controller.send(:initialize_current_url)
15
+ end
16
+
17
+ def current_user
18
+ users(:guest)
19
+ end
20
+
21
+ def test_should_be_authorized_if_user_has_proper_permissions
22
+ assert authorized_for?('/users/index')
23
+ end
24
+
25
+ def test_should_not_be_authorized_if_user_doesnt_have_proper_permissions
26
+ assert !authorized_for?('/admin/users/destroy')
27
+ end
28
+
29
+ def test_should_link_to_nothing_if_not_authorized_and_not_showing_text
30
+ assert_equal '', link_to_if_authorized('Destroy User', '/admin/users/destroy', :show_text => false)
31
+ end
32
+
33
+ def test_should_display_text_if_not_authorized_and_showing_text
34
+ assert_equal 'Destroy User', link_to_if_authorized('Destroy User', '/admin/users/destroy', :show_text => true)
35
+ end
36
+
37
+ def test_should_link_to_url_if_authorized
38
+ assert_equal '<a href="/users">Destroy User</a>', link_to_if_authorized('Destroy User', {:controller => 'users', :action => 'index'}, :show_text => false)
39
+ end
40
+ end
@@ -0,0 +1,95 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class ControllerTest < Test::Unit::TestCase
4
+ fixtures :controllers, :permissions
5
+
6
+ def test_should_be_valid
7
+ assert_valid controllers(:users)
8
+ end
9
+
10
+ def test_should_require_path
11
+ assert_invalid controllers(:users), :path, nil, ''
12
+ end
13
+
14
+ def test_should_require_specific_format_for_paths
15
+ assert_invalid controllers(:users), :path, '/users', '//users', 'users/', 'admin//users', '1'
16
+ end
17
+
18
+ def test_should_require_unique_path
19
+ assert_invalid controllers(:users).clone, :path
20
+ end
21
+
22
+ def test_should_have_associated_permissions
23
+ assert_equal [permissions(:read_users), permissions(:update_users)], controllers(:users).permissions
24
+ end
25
+
26
+ def test_should_destroy_associated_permissions_when_destroyed
27
+ controllers(:users).destroy
28
+ assert_nil Permission.find_by_controller_id(2)
29
+ end
30
+
31
+ def test_should_use_path_for_klass
32
+ assert_equal UsersController, controllers(:users).klass
33
+ end
34
+
35
+ def test_should_use_namespaced_path_for_klass
36
+ assert_equal Admin::UsersController, controllers(:admin_users).klass
37
+ end
38
+
39
+ def test_should_use_controller_name_for_name_if_controller_is_not_namespaced
40
+ assert_equal 'Users', controllers(:users).name
41
+ end
42
+
43
+ def test_should_use_demodulized_controller_name_for_name_if_controller_is_namespaced
44
+ assert_equal 'Users', controllers(:admin_users).name
45
+ end
46
+
47
+ def test_possible_path_matches_should_include_all_superclasses_except_base_controller
48
+ assert_equal %w(users application), controllers(:users).possible_path_matches
49
+ assert_equal %w(admin/users application), controllers(:admin_users).possible_path_matches
50
+ assert_equal %w(payment/pay_pal/transactions payment/transactions application), controllers(:payment_pay_pal_transactions).possible_path_matches
51
+ end
52
+
53
+ def test_should_be_global_if_path_is_application
54
+ assert controllers(:application).global?
55
+ end
56
+
57
+ def test_should_not_be_global_if_path_is_not_application
58
+ assert !controllers(:users).global?
59
+ assert !controllers(:admin_users).global?
60
+ end
61
+
62
+ def test_stringification_should_use_name
63
+ assert_equal 'Users', controllers(:users).to_s
64
+ end
65
+
66
+ def test_should_map_parents_for_all_controllers
67
+ expected = [
68
+ [Object, [controllers(:application), controllers(:users)]],
69
+ [Admin, [controllers(:admin_users)]],
70
+ [Payment, [controllers(:payment_transactions)]],
71
+ [Payment::PayPal, [controllers(:payment_pay_pal_transactions)]]
72
+ ]
73
+ assert_equal expected, Controller.find_all_mapped_by_parent
74
+ end
75
+
76
+ def test_should_recognize_path_by_string
77
+ assert_equal ['users', 'index'], Controller.recognize_path('/users')
78
+ end
79
+
80
+ def test_should_recognize_namespaced_path_by_string
81
+ assert_equal ['admin/users', 'update'], Controller.recognize_path('/admin/users/update')
82
+ end
83
+
84
+ def test_should_recognize_path_by_hash
85
+ assert_equal ['users', 'index'], Controller.recognize_path(:controller => 'users', :action => 'index')
86
+ end
87
+
88
+ def test_should_recognize_namespaced_path_by_hash
89
+ assert_equal ['admin/users', 'update'], Controller.recognize_path(:controller => 'admin/users', :action => 'update', :id => 1)
90
+ end
91
+
92
+ def test_should_recognize_path_if_action_not_specified
93
+ assert_equal ['users', 'index'], Controller.recognize_path(:controller => 'users')
94
+ end
95
+ end
@@ -0,0 +1,49 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class HasRolesTest < Test::Unit::TestCase
4
+ fixtures :controllers, :permissions, :roles, :permissions_roles, :users, :role_assignments
5
+
6
+ def test_should_have_role_assignments_association
7
+ assert_equal [role_assignments(:administrator)], users(:administrator).role_assignments
8
+ end
9
+
10
+ def test_should_destroy_role_assignments_when_destroyed
11
+ users(:administrator).destroy
12
+ assert_nil RoleAssignment.find_by_assignee_id(1)
13
+ end
14
+
15
+ def test_should_have_roles_association
16
+ assert_equal [roles(:administrator)], users(:administrator).roles
17
+ end
18
+
19
+ def test_should_be_authorized_if_user_has_proper_permissions
20
+ assert users(:guest).authorized_for?('/users/index')
21
+ end
22
+
23
+ def test_should_not_be_authorized_if_user_doesnt_have_proper_permissions
24
+ assert !users(:guest).authorized_for?('/admin/users/destroy')
25
+ end
26
+
27
+ def test_roles_ids_should_map_all_ids
28
+ assert_equal [1], users(:administrator).role_ids
29
+ end
30
+
31
+
32
+ def test_should_destroy_old_roles_when_replaced
33
+ user = users(:administrator)
34
+ user.role_ids = []
35
+ assert_equal [], user.roles
36
+ end
37
+
38
+ def test_should_add_new_roles_when_replaced
39
+ user = users(:administrator)
40
+ user.role_ids = [1, 2]
41
+ assert_equal [roles(:administrator), roles(:moderator)], user.roles
42
+ end
43
+
44
+ def test_should_destroy_old_roles_and_add_new_roles_when_replaced
45
+ user = users(:administrator)
46
+ user.role_ids = [2]
47
+ assert_equal [roles(:moderator)], user.roles
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class PermissionTest < Test::Unit::TestCase
4
+ fixtures :controllers, :permissions, :roles, :permissions_roles
5
+
6
+ def test_should_be_valid
7
+ assert_valid permissions(:crud_application)
8
+ end
9
+
10
+ def test_should_require_controller_id
11
+ assert_invalid permissions(:crud_application), :controller_id, nil
12
+ end
13
+
14
+ def test_should_require_unique_action_per_controller
15
+ assert_invalid permissions(:read_application).clone, :action
16
+ end
17
+
18
+ def test_should_not_require_action
19
+ assert_valid permissions(:crud_application), :action, nil, 'show', 'update'
20
+ end
21
+
22
+ def test_should_require_specific_action_length_if_action_specified
23
+ assert_invalid permissions(:crud_application), :action, ''
24
+ end
25
+
26
+ def test_should_have_controller_association
27
+ assert_equal controllers(:application), permissions(:crud_application).controller
28
+ end
29
+
30
+ def test_should_have_roles_association
31
+ assert_equal [roles(:administrator)], permissions(:crud_application).roles
32
+ end
33
+
34
+ def test_should_restrict_path_if_permissioned_action_exists
35
+ assert Permission.restricts?('/users/index')
36
+ end
37
+
38
+ def test_should_restrict_path_if_permissioned_controller_exists
39
+ assert Permission.restricts?('/admin/users')
40
+ end
41
+
42
+ def test_should_not_restrict_path_if_permissioned_action_doesnt_exist
43
+ assert !Permission.restricts?(:controller => 'home', :action => 'index')
44
+ end
45
+
46
+ def test_should_not_restrict_path_if_permissioned_controller_doesnt_exist
47
+ assert !Permission.restricts?(:controller => 'home')
48
+ end
49
+
50
+ def test_should_find_all_permissions_authorized_for_path
51
+ assert_equal [permissions(:crud_application), permissions(:read_application), permissions(:read_users)], Permission.find_all_authorized_for('/users/index')
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class RoleAssignmentTest < Test::Unit::TestCase
4
+ fixtures :roles, :users, :role_assignments
5
+
6
+ def test_should_be_valid
7
+ assert_valid role_assignments(:administrator)
8
+ end
9
+
10
+ def test_should_require_role_id
11
+ assert_invalid role_assignments(:administrator), :role_id, nil
12
+ end
13
+
14
+ def test_should_require_assignee_id
15
+ assert_invalid role_assignments(:administrator), :assignee_id, nil
16
+ end
17
+
18
+ def test_should_require_assignee_type
19
+ assert_invalid role_assignments(:administrator), :assignee_type, nil
20
+ end
21
+
22
+ def test_should_have_role_association
23
+ assert_equal roles(:administrator), role_assignments(:administrator).role
24
+ end
25
+
26
+ def test_should_have_assignee_association
27
+ assert_equal users(:administrator), role_assignments(:administrator).assignee
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class RoleTest < Test::Unit::TestCase
4
+ fixtures :controllers, :permissions, :roles, :permissions_roles, :users, :role_assignments
5
+
6
+ def test_should_be_valid
7
+ assert_valid roles(:administrator)
8
+ end
9
+
10
+ def test_should_require_name
11
+ assert_invalid roles(:administrator), :name, nil, ''
12
+ end
13
+
14
+ def test_should_require_unique_name
15
+ assert_invalid roles(:administrator).clone, :name
16
+ end
17
+
18
+ def test_should_have_permissions_association
19
+ assert_equal [permissions(:crud_application)], roles(:administrator).permissions
20
+ end
21
+
22
+ def test_should_have_assignments_association
23
+ assert_equal [role_assignments(:administrator)], roles(:administrator).assignments
24
+ end
25
+
26
+ def test_should_destroy_assignments_when_destroyed
27
+ roles(:administrator).destroy
28
+ assert_nil RoleAssignment.find_by_role_id(1)
29
+ end
30
+
31
+ def test_should_use_name_for_strinigification
32
+ assert_equal 'Administrator', roles(:administrator).to_s
33
+ end
34
+
35
+ def test_should_find_all_roles_authorized_for_path
36
+ assert_equal [roles(:administrator), roles(:guest)], Role.find_all_authorized_for('/users/index')
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: has_roles
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.2
7
+ date: 2007-09-26 00:00:00 -04:00
8
+ summary: Provides simple role management based on controllers/actions
9
+ require_paths:
10
+ - lib
11
+ email: info@pluginaweek.org
12
+ homepage: http://www.pluginaweek.org
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: has_roles
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Aaron Pfeifer, Neil Abraham
31
+ files:
32
+ - app/models
33
+ - app/models/role.rb
34
+ - app/models/controller.rb
35
+ - app/models/role_assignment.rb
36
+ - app/models/permission.rb
37
+ - db/migrate
38
+ - db/migrate/003_create_roles.rb
39
+ - db/migrate/004_create_permissions_roles.rb
40
+ - db/migrate/001_create_controllers.rb
41
+ - db/migrate/005_create_role_assignments.rb
42
+ - db/migrate/002_create_permissions.rb
43
+ - lib/has_roles.rb
44
+ - lib/has_roles
45
+ - lib/has_roles/authorization_helper.rb
46
+ - test/test_helper.rb
47
+ - test/fixtures
48
+ - test/app_root
49
+ - test/unit
50
+ - test/fixtures/controllers.yml
51
+ - test/fixtures/users.yml
52
+ - test/fixtures/role_assignments.yml
53
+ - test/fixtures/permissions.yml
54
+ - test/fixtures/permissions_roles.yml
55
+ - test/fixtures/roles.yml
56
+ - test/app_root/config
57
+ - test/app_root/app
58
+ - test/app_root/db
59
+ - test/app_root/config/routes.rb
60
+ - test/app_root/config/environment.rb
61
+ - test/app_root/app/models
62
+ - test/app_root/app/controllers
63
+ - test/app_root/app/models/user.rb
64
+ - test/app_root/app/controllers/payment
65
+ - test/app_root/app/controllers/users_controller.rb
66
+ - test/app_root/app/controllers/admin
67
+ - test/app_root/app/controllers/home_controller.rb
68
+ - test/app_root/app/controllers/payment/pay_pal
69
+ - test/app_root/app/controllers/payment/transactions_controller.rb
70
+ - test/app_root/app/controllers/payment/pay_pal/transactions_controller.rb
71
+ - test/app_root/app/controllers/admin/users_controller.rb
72
+ - test/app_root/db/migrate
73
+ - test/app_root/db/migrate/001_create_users.rb
74
+ - test/unit/role_test.rb
75
+ - test/unit/permission_test.rb
76
+ - test/unit/role_assignment_test.rb
77
+ - test/unit/has_roles_test.rb
78
+ - test/unit/controller_test.rb
79
+ - test/unit/authorization_helper_test.rb
80
+ - CHANGELOG
81
+ - init.rb
82
+ - MIT-LICENSE
83
+ - Rakefile
84
+ - README
85
+ test_files:
86
+ - test/unit/role_test.rb
87
+ - test/unit/permission_test.rb
88
+ - test/unit/role_assignment_test.rb
89
+ - test/unit/has_roles_test.rb
90
+ - test/unit/controller_test.rb
91
+ - test/unit/authorization_helper_test.rb
92
+ rdoc_options: []
93
+
94
+ extra_rdoc_files: []
95
+
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ requirements: []
101
+
102
+ dependencies: []
103
+