has_roles 0.0.2

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.
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
+