acl9 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.textile +32 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +888 -0
- data/Rakefile +40 -0
- data/TODO +42 -0
- data/VERSION.yml +4 -0
- data/lib/acl9.rb +16 -0
- data/lib/acl9/config.rb +10 -0
- data/lib/acl9/controller_extensions.rb +85 -0
- data/lib/acl9/controller_extensions/dsl_base.rb +229 -0
- data/lib/acl9/controller_extensions/generators.rb +197 -0
- data/lib/acl9/helpers.rb +19 -0
- data/lib/acl9/model_extensions.rb +133 -0
- data/lib/acl9/model_extensions/object.rb +59 -0
- data/lib/acl9/model_extensions/subject.rb +175 -0
- data/test/access_control_test.rb +338 -0
- data/test/dsl_base_test.rb +758 -0
- data/test/helpers_test.rb +93 -0
- data/test/roles_test.rb +310 -0
- data/test/support/controllers.rb +207 -0
- data/test/support/models.rb +47 -0
- data/test/support/schema.rb +69 -0
- data/test/test_helper.rb +31 -0
- metadata +103 -0
data/lib/acl9/helpers.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Acl9
|
2
|
+
module Helpers
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def access_control(method, opts = {}, &block)
|
9
|
+
subject_method = opts.delete(:subject_method) || Acl9::config[:default_subject_method]
|
10
|
+
raise ArgumentError, "Block must be supplied to access_control" unless block
|
11
|
+
|
12
|
+
generator = Acl9::Dsl::Generators::HelperMethod.new(subject_method, method)
|
13
|
+
|
14
|
+
generator.acl_block!(&block)
|
15
|
+
generator.install_on(self, opts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'model_extensions', 'subject')
|
2
|
+
require File.join(File.dirname(__FILE__), 'model_extensions', 'object')
|
3
|
+
|
4
|
+
module Acl9
|
5
|
+
module ModelExtensions #:nodoc:
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Add #has_role? and other role methods to the class.
|
12
|
+
# Makes a class a auth. subject class.
|
13
|
+
#
|
14
|
+
# @param [Hash] options the options for tuning
|
15
|
+
# @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
|
16
|
+
# Class name of the role class (e.g. 'AccountRole')
|
17
|
+
# @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
|
18
|
+
# Join table name (e.g. 'accounts_account_roles')
|
19
|
+
# @example
|
20
|
+
# class User < ActiveRecord::Base
|
21
|
+
# acts_as_authorization_subject
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# user = User.new
|
25
|
+
# user.roles #=> returns Role objects, associated with the user
|
26
|
+
# user.has_role!(...)
|
27
|
+
# user.has_no_role!(...)
|
28
|
+
#
|
29
|
+
# # other functions from Acl9::ModelExtensions::Subject are made available
|
30
|
+
#
|
31
|
+
# @see Acl9::ModelExtensions::Subject
|
32
|
+
#
|
33
|
+
def acts_as_authorization_subject(options = {})
|
34
|
+
role = options[:role_class_name] || Acl9::config[:default_role_class_name]
|
35
|
+
join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
|
36
|
+
join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(role))
|
37
|
+
|
38
|
+
has_and_belongs_to_many :role_objects, :class_name => role, :join_table => join_table
|
39
|
+
|
40
|
+
cattr_accessor :_auth_role_class_name, :_auth_subject_class_name
|
41
|
+
self._auth_role_class_name = role
|
42
|
+
self._auth_subject_class_name = self.to_s
|
43
|
+
|
44
|
+
include Acl9::ModelExtensions::Subject
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add role query and set methods to the class (making it an auth object class).
|
48
|
+
#
|
49
|
+
# @param [Hash] options the options for tuning
|
50
|
+
# @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
|
51
|
+
# Subject class name (e.g. 'User', or 'Account)
|
52
|
+
# @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
|
53
|
+
# Role class name (e.g. 'AccountRole')
|
54
|
+
# @example
|
55
|
+
# class Product < ActiveRecord::Base
|
56
|
+
# acts_as_authorization_object
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# product = Product.new
|
60
|
+
# product.accepted_roles #=> returns Role objects, associated with the product
|
61
|
+
# product.users #=> returns User objects, associated with the product
|
62
|
+
# product.accepts_role!(...)
|
63
|
+
# product.accepts_no_role!(...)
|
64
|
+
# # other functions from Acl9::ModelExtensions::Object are made available
|
65
|
+
#
|
66
|
+
# @see Acl9::ModelExtensions::Object
|
67
|
+
#
|
68
|
+
def acts_as_authorization_object(options = {})
|
69
|
+
subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
|
70
|
+
subj_table = subject.constantize.table_name
|
71
|
+
subj_col = subject.underscore
|
72
|
+
|
73
|
+
role = options[:role_class_name] || Acl9::config[:default_role_class_name]
|
74
|
+
role_table = role.constantize.table_name
|
75
|
+
|
76
|
+
sql_tables = <<-EOS
|
77
|
+
FROM #{subj_table}
|
78
|
+
INNER JOIN #{role_table}_#{subj_table} ON #{subj_col}_id = #{subj_table}.id
|
79
|
+
INNER JOIN #{role_table} ON #{role_table}.id = #{role.underscore}_id
|
80
|
+
EOS
|
81
|
+
|
82
|
+
sql_where = <<-'EOS'
|
83
|
+
WHERE authorizable_type = '#{self.class.base_class.to_s}'
|
84
|
+
AND authorizable_id = #{id}
|
85
|
+
EOS
|
86
|
+
|
87
|
+
has_many :accepted_roles, :as => :authorizable, :class_name => role, :dependent => :destroy
|
88
|
+
|
89
|
+
has_many :"#{subj_table}",
|
90
|
+
:finder_sql => ("SELECT DISTINCT #{subj_table}.*" + sql_tables + sql_where),
|
91
|
+
:counter_sql => ("SELECT COUNT(DISTINCT #{subj_table}.id)" + sql_tables + sql_where),
|
92
|
+
:readonly => true
|
93
|
+
|
94
|
+
include Acl9::ModelExtensions::Object
|
95
|
+
end
|
96
|
+
|
97
|
+
# Make a class an auth role class.
|
98
|
+
#
|
99
|
+
# You'll probably never create or use objects of this class directly.
|
100
|
+
# Various auth. subject and object methods will do that for you
|
101
|
+
# internally.
|
102
|
+
#
|
103
|
+
# @param [Hash] options the options for tuning
|
104
|
+
# @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
|
105
|
+
# Subject class name (e.g. 'User', or 'Account)
|
106
|
+
# @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
|
107
|
+
# Join table name (e.g. 'accounts_account_roles')
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# class Role < ActiveRecord::Base
|
111
|
+
# acts_as_authorization_role
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# @see Acl9::ModelExtensions::Subject#has_role!
|
115
|
+
# @see Acl9::ModelExtensions::Subject#has_role?
|
116
|
+
# @see Acl9::ModelExtensions::Subject#has_no_role!
|
117
|
+
# @see Acl9::ModelExtensions::Object#accepts_role!
|
118
|
+
# @see Acl9::ModelExtensions::Object#accepts_role?
|
119
|
+
# @see Acl9::ModelExtensions::Object#accepts_no_role!
|
120
|
+
def acts_as_authorization_role(options = {})
|
121
|
+
subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
|
122
|
+
join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
|
123
|
+
join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(subject))
|
124
|
+
|
125
|
+
has_and_belongs_to_many subject.demodulize.tableize.to_sym,
|
126
|
+
:class_name => subject,
|
127
|
+
:join_table => join_table
|
128
|
+
|
129
|
+
belongs_to :authorizable, :polymorphic => true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Acl9
|
2
|
+
module ModelExtensions
|
3
|
+
module Object
|
4
|
+
##
|
5
|
+
# Role check.
|
6
|
+
#
|
7
|
+
# @return [Boolean] Returns true if +subject+ has a role +role_name+ on this object.
|
8
|
+
#
|
9
|
+
# @param [Symbol,String] role_name Role name
|
10
|
+
# @param [Subject] subject Subject to add role for
|
11
|
+
# @see Acl9::ModelExtensions::Subject#has_role?
|
12
|
+
def accepts_role?(role_name, subject)
|
13
|
+
subject.has_role? role_name, self
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Add role on the object to specified subject.
|
18
|
+
#
|
19
|
+
# @param [Symbol,String] role_name Role name
|
20
|
+
# @param [Subject] subject Subject to add role for
|
21
|
+
# @see Acl9::ModelExtensions::Subject#has_role!
|
22
|
+
def accepts_role!(role_name, subject)
|
23
|
+
subject.has_role! role_name, self
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Free specified subject of a role on this object.
|
28
|
+
#
|
29
|
+
# @param [Symbol,String] role_name Role name
|
30
|
+
# @param [Subject] subject Subject to remove role from
|
31
|
+
# @see Acl9::ModelExtensions::Subject#has_no_role!
|
32
|
+
def accepts_no_role!(role_name, subject)
|
33
|
+
subject.has_no_role! role_name, self
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Are there any roles for the specified +subject+ on this object?
|
38
|
+
#
|
39
|
+
# @param [Subject] subject Subject to query roles
|
40
|
+
# @return [Boolean] Returns true if +subject+ has any roles on this object.
|
41
|
+
# @see Acl9::ModelExtensions::Subject#has_roles_for?
|
42
|
+
def accepts_roles_by?(subject)
|
43
|
+
subject.has_roles_for? self
|
44
|
+
end
|
45
|
+
|
46
|
+
alias :accepts_role_by? :accepts_roles_by?
|
47
|
+
|
48
|
+
##
|
49
|
+
# Which roles does +subject+ have on this object?
|
50
|
+
#
|
51
|
+
# @return [Array<Role>] Role instances, associated both with +subject+ and +object+
|
52
|
+
# @param [Subject] subject Subject to query roles
|
53
|
+
# @see Acl9::ModelExtensions::Subject#roles_for
|
54
|
+
def accepted_roles_by(subject)
|
55
|
+
subject.roles_for self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Acl9
|
2
|
+
module ModelExtensions
|
3
|
+
module Subject
|
4
|
+
##
|
5
|
+
# Role check.
|
6
|
+
#
|
7
|
+
# There is a global option, +Acl9.config[:protect_global_roles]+, which governs
|
8
|
+
# this method behavior.
|
9
|
+
#
|
10
|
+
# If protect_global_roles is +false+, an object role is automatically counted
|
11
|
+
# as global role. E.g.
|
12
|
+
#
|
13
|
+
# Acl9.config[:protect_global_roles] = false
|
14
|
+
# user.has_role!(:manager, @foo)
|
15
|
+
# user.has_role?(:manager, @foo) # => true
|
16
|
+
# user.has_role?(:manager) # => true
|
17
|
+
#
|
18
|
+
# In this case manager is anyone who "manages" at least one object.
|
19
|
+
#
|
20
|
+
# However, if protect_global_roles option set to +true+, you'll need to
|
21
|
+
# explicitly grant global role with same name.
|
22
|
+
#
|
23
|
+
# Acl9.config[:protect_global_roles] = true
|
24
|
+
# user.has_role!(:manager, @foo)
|
25
|
+
# user.has_role?(:manager) # => false
|
26
|
+
# user.has_role!(:manager)
|
27
|
+
# user.has_role?(:manager) # => true
|
28
|
+
#
|
29
|
+
# protect_global_roles option is +false+ by default as for now, but this
|
30
|
+
# may change in future!
|
31
|
+
#
|
32
|
+
# @return [Boolean] Whether +self+ has a role +role_name+ on +object+.
|
33
|
+
# @param [Symbol,String] role_name Role name
|
34
|
+
# @param [Object] object Object to query a role on
|
35
|
+
#
|
36
|
+
# @see Acl9::ModelExtensions::Object#accepts_role?
|
37
|
+
def has_role?(role_name, object = nil)
|
38
|
+
!! if object.nil? && !::Acl9.config[:protect_global_roles]
|
39
|
+
self.role_objects.find_by_name(role_name.to_s) ||
|
40
|
+
self.role_objects.member?(get_role(role_name, nil))
|
41
|
+
else
|
42
|
+
role = get_role(role_name, object)
|
43
|
+
role && self.role_objects.exists?(role.id)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Add specified role on +object+ to +self+.
|
49
|
+
#
|
50
|
+
# @param [Symbol,String] role_name Role name
|
51
|
+
# @param [Object] object Object to add a role for
|
52
|
+
# @see Acl9::ModelExtensions::Object#accepts_role!
|
53
|
+
def has_role!(role_name, object = nil)
|
54
|
+
role = get_role(role_name, object)
|
55
|
+
|
56
|
+
if role.nil?
|
57
|
+
role_attrs = case object
|
58
|
+
when Class then { :authorizable_type => object.to_s }
|
59
|
+
when nil then {}
|
60
|
+
else { :authorizable => object }
|
61
|
+
end.merge( { :name => role_name.to_s })
|
62
|
+
|
63
|
+
role = self._auth_role_class.create(role_attrs)
|
64
|
+
end
|
65
|
+
|
66
|
+
self.role_objects << role if role && !self.role_objects.exists?(role.id)
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Free +self+ from a specified role on +object+.
|
71
|
+
#
|
72
|
+
# @param [Symbol,String] role_name Role name
|
73
|
+
# @param [Object] object Object to remove a role on
|
74
|
+
# @see Acl9::ModelExtensions::Object#accepts_no_role!
|
75
|
+
def has_no_role!(role_name, object = nil)
|
76
|
+
delete_role(get_role(role_name, object))
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Are there any roles for +self+ on +object+?
|
81
|
+
#
|
82
|
+
# @param [Object] object Object to query roles
|
83
|
+
# @return [Boolean] Returns true if +self+ has any roles on +object+.
|
84
|
+
# @see Acl9::ModelExtensions::Object#accepts_roles_by?
|
85
|
+
def has_roles_for?(object)
|
86
|
+
!!self.role_objects.detect(&role_selecting_lambda(object))
|
87
|
+
end
|
88
|
+
|
89
|
+
alias :has_role_for? :has_roles_for?
|
90
|
+
|
91
|
+
##
|
92
|
+
# Which roles does +self+ have on +object+?
|
93
|
+
#
|
94
|
+
# @return [Array<Role>] Role instances, associated both with +self+ and +object+
|
95
|
+
# @param [Object] object Object to query roles
|
96
|
+
# @see Acl9::ModelExtensions::Object#accepted_roles_by
|
97
|
+
# @example
|
98
|
+
# user = User.find(...)
|
99
|
+
# product = Product.find(...)
|
100
|
+
#
|
101
|
+
# user.roles_for(product).map(&:name).sort #=> role names in alphabetical order
|
102
|
+
def roles_for(object)
|
103
|
+
self.role_objects.select(&role_selecting_lambda(object))
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Unassign any roles on +object+ from +self+.
|
108
|
+
#
|
109
|
+
# @param [Object,nil] object Object to unassign roles for. +nil+ means unassign global roles.
|
110
|
+
def has_no_roles_for!(object = nil)
|
111
|
+
roles_for(object).each { |role| delete_role(role) }
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Unassign all roles from +self+.
|
116
|
+
def has_no_roles!
|
117
|
+
# for some reason simple
|
118
|
+
#
|
119
|
+
# self.roles.each { |role| delete_role(role) }
|
120
|
+
#
|
121
|
+
# doesn't work. seems like a bug in ActiveRecord
|
122
|
+
self.role_objects.map(&:id).each do |role_id|
|
123
|
+
delete_role self._auth_role_class.find(role_id)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def role_selecting_lambda(object)
|
130
|
+
case object
|
131
|
+
when Class
|
132
|
+
lambda { |role| role.authorizable_type == object.to_s }
|
133
|
+
when nil
|
134
|
+
lambda { |role| role.authorizable.nil? }
|
135
|
+
else
|
136
|
+
lambda do |role|
|
137
|
+
role.authorizable_type == object.class.base_class.to_s && role.authorizable == object
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_role(role_name, object)
|
143
|
+
role_name = role_name.to_s
|
144
|
+
|
145
|
+
cond = case object
|
146
|
+
when Class
|
147
|
+
[ 'name = ? and authorizable_type = ? and authorizable_id IS NULL', role_name, object.to_s ]
|
148
|
+
when nil
|
149
|
+
[ 'name = ? and authorizable_type IS NULL and authorizable_id IS NULL', role_name ]
|
150
|
+
else
|
151
|
+
[
|
152
|
+
'name = ? and authorizable_type = ? and authorizable_id = ?',
|
153
|
+
role_name, object.class.base_class.to_s, object.id
|
154
|
+
]
|
155
|
+
end
|
156
|
+
|
157
|
+
self._auth_role_class.first :conditions => cond
|
158
|
+
end
|
159
|
+
|
160
|
+
def delete_role(role)
|
161
|
+
if role
|
162
|
+
self.role_objects.delete role
|
163
|
+
|
164
|
+
role.destroy if role.send(self._auth_subject_class_name.demodulize.tableize).empty?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
def _auth_role_class
|
171
|
+
self.class._auth_role_class_name.constantize
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'acl9')
|
3
|
+
require 'support/controllers'
|
4
|
+
|
5
|
+
#######################################################################
|
6
|
+
|
7
|
+
class Admin
|
8
|
+
def has_role?(role, obj = nil)
|
9
|
+
role == "admin"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class OwnerOfFoo
|
14
|
+
def has_role?(role, obj)
|
15
|
+
role == 'owner' && obj == MyDearFoo.instance
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Bartender
|
20
|
+
def has_role?(role, obj)
|
21
|
+
role == 'bartender' && obj == ACLIvars::VenerableBar
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class TheOnlyUser
|
26
|
+
include Singleton
|
27
|
+
|
28
|
+
def has_role?(role, subj)
|
29
|
+
role == "the_only_one"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Beholder
|
34
|
+
def initialize(role)
|
35
|
+
@role = role.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_role?(role, obj)
|
39
|
+
role.to_s == @role
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#######################################################################
|
44
|
+
|
45
|
+
module BaseTests
|
46
|
+
# permit anonymous to index and show and admin everywhere else
|
47
|
+
def self.included(klass)
|
48
|
+
klass.class_eval do
|
49
|
+
[:index, :show].each do |act|
|
50
|
+
it "should permit anonymous to #{act}" do
|
51
|
+
get act
|
52
|
+
@response.body.should == 'OK'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[:new, :edit, :update, :delete, :destroy].each do |act|
|
57
|
+
it "should forbid anonymous to #{act}" do
|
58
|
+
get act
|
59
|
+
@response.body.should == 'AccessDenied'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
[:index, :show, :new, :edit, :update, :delete, :destroy].each do |act|
|
64
|
+
it "should permit admin to #{act}" do
|
65
|
+
get act, :user => Admin.new
|
66
|
+
@response.body.should == 'OK'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module ShouldRespondToAcl
|
74
|
+
def self.included(klass)
|
75
|
+
klass.class_eval do
|
76
|
+
it "should add :acl as a method" do
|
77
|
+
@controller.should respond_to(:acl)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should_not add :acl? as a method" do
|
81
|
+
@controller.should_not respond_to(:acl?)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#######################################################################
|
88
|
+
|
89
|
+
class ACLBlockTest < ActionController::TestCase
|
90
|
+
tests ACLBlock
|
91
|
+
|
92
|
+
include BaseTests
|
93
|
+
end
|
94
|
+
|
95
|
+
class ACLMethodTest < ActionController::TestCase
|
96
|
+
tests ACLMethod
|
97
|
+
|
98
|
+
include BaseTests
|
99
|
+
include ShouldRespondToAcl
|
100
|
+
end
|
101
|
+
|
102
|
+
class ACLMethod2Test < ActionController::TestCase
|
103
|
+
tests ACLMethod2
|
104
|
+
|
105
|
+
include BaseTests
|
106
|
+
include ShouldRespondToAcl
|
107
|
+
end
|
108
|
+
|
109
|
+
class ACLArgumentsTest < ActionController::TestCase
|
110
|
+
tests ACLArguments
|
111
|
+
|
112
|
+
include BaseTests
|
113
|
+
end
|
114
|
+
|
115
|
+
class ACLBooleanMethodTest < ActionController::TestCase
|
116
|
+
tests ACLBooleanMethod
|
117
|
+
|
118
|
+
include BaseTests
|
119
|
+
end
|
120
|
+
|
121
|
+
class ACLIvarsTest < ActionController::TestCase
|
122
|
+
tests ACLIvars
|
123
|
+
|
124
|
+
it "should allow owner of foo to destroy" do
|
125
|
+
delete :destroy, :user => OwnerOfFoo.new
|
126
|
+
@response.body.should == 'OK'
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should allow bartender to destroy" do
|
130
|
+
delete :destroy, :user => Bartender.new
|
131
|
+
@response.body.should == 'OK'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class ACLSubjectMethodTest < ActionController::TestCase
|
136
|
+
tests ACLSubjectMethod
|
137
|
+
|
138
|
+
it "should allow the only user to index" do
|
139
|
+
get :index, :user => TheOnlyUser.instance
|
140
|
+
@response.body.should == 'OK'
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should deny anonymous to index" do
|
144
|
+
get :index
|
145
|
+
@response.body.should == 'AccessDenied'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class ACLObjectsHashTest < ActionController::TestCase
|
150
|
+
tests ACLObjectsHash
|
151
|
+
|
152
|
+
it "should consider objects hash and prefer it to @ivar" do
|
153
|
+
get :allow, :user => OwnerOfFoo.new
|
154
|
+
@response.body.should == 'OK'
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should return AccessDenied when not logged in" do
|
158
|
+
get :allow
|
159
|
+
@response.body.should == 'AccessDenied'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class ACLActionOverrideTest < ActionController::TestCase
|
164
|
+
tests ACLActionOverride
|
165
|
+
|
166
|
+
it "should allow index action to anonymous" do
|
167
|
+
get :check_allow, :_action => :index
|
168
|
+
@response.body.should == 'OK'
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should deny show action to anonymous" do
|
172
|
+
get :check_allow, :_action => :show
|
173
|
+
@response.body.should == 'AccessDenied'
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should deny edit action to regular user" do
|
177
|
+
get :check_allow_with_foo, :_action => :edit, :user => TheOnlyUser.instance
|
178
|
+
|
179
|
+
@response.body.should == 'AccessDenied'
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should allow edit action to owner of foo" do
|
183
|
+
get :check_allow_with_foo, :_action => :edit, :user => OwnerOfFoo.new
|
184
|
+
|
185
|
+
@response.body.should == 'OK'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class ACLHelperMethodTest < ActionController::TestCase
|
190
|
+
tests ACLHelperMethod
|
191
|
+
|
192
|
+
it "should return OK checking helper method" do
|
193
|
+
get :allow, :user => OwnerOfFoo.new
|
194
|
+
@response.body.should == 'OK'
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should return AccessDenied when not logged in" do
|
198
|
+
get :allow
|
199
|
+
@response.body.should == 'AccessDenied'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
#######################################################################
|
204
|
+
|
205
|
+
module ACLQueryMixin
|
206
|
+
def self.included(base)
|
207
|
+
base.class_eval do
|
208
|
+
describe "#acl_question_mark" do # describe "#acl?" doesn't work
|
209
|
+
before do
|
210
|
+
@editor = Beholder.new(:editor)
|
211
|
+
@viewer = Beholder.new(:viewer)
|
212
|
+
@owneroffoo = OwnerOfFoo.new
|
213
|
+
end
|
214
|
+
|
215
|
+
[:edit, :update, :destroy].each do |meth|
|
216
|
+
it "should return true for editor/#{meth}" do
|
217
|
+
@controller.current_user = @editor
|
218
|
+
@controller.acl?(meth).should == true
|
219
|
+
@controller.acl?(meth.to_s).should == true
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should return false for viewer/#{meth}" do
|
223
|
+
@controller.current_user = @viewer
|
224
|
+
@controller.acl?(meth).should == false
|
225
|
+
@controller.acl?(meth.to_s).should == false
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
[:index, :show].each do |meth|
|
230
|
+
it "should return false for editor/#{meth}" do
|
231
|
+
@controller.current_user = @editor
|
232
|
+
@controller.acl?(meth).should == false
|
233
|
+
@controller.acl?(meth.to_s).should == false
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should return true for viewer/#{meth}" do
|
237
|
+
@controller.current_user = @viewer
|
238
|
+
@controller.acl?(meth).should == true
|
239
|
+
@controller.acl?(meth.to_s).should == true
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should return false for editor/fooize" do
|
244
|
+
@controller.current_user = @editor
|
245
|
+
@controller.acl?(:fooize).should == false
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should return true for foo owner" do
|
249
|
+
@controller.current_user = @owneroffoo
|
250
|
+
@controller.acl?(:fooize, :foo => MyDearFoo.instance).should == true
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class ACLQueryMethodTest < ActionController::TestCase
|
258
|
+
tests ACLQueryMethod
|
259
|
+
|
260
|
+
it "should respond to :acl?" do
|
261
|
+
@controller.should respond_to(:acl?)
|
262
|
+
end
|
263
|
+
|
264
|
+
include ACLQueryMixin
|
265
|
+
end
|
266
|
+
|
267
|
+
class ACLQueryMethodWithLambdaTest < ActionController::TestCase
|
268
|
+
tests ACLQueryMethodWithLambda
|
269
|
+
|
270
|
+
it "should respond to :acl?" do
|
271
|
+
@controller.should respond_to(:acl?)
|
272
|
+
end
|
273
|
+
|
274
|
+
include ACLQueryMixin
|
275
|
+
end
|
276
|
+
|
277
|
+
#######################################################################
|
278
|
+
|
279
|
+
class ACLNamedQueryMethodTest < ActionController::TestCase
|
280
|
+
tests ACLNamedQueryMethod
|
281
|
+
|
282
|
+
it "should respond to :allow_ay" do
|
283
|
+
@controller.should respond_to(:allow_ay)
|
284
|
+
end
|
285
|
+
|
286
|
+
include ACLQueryMixin
|
287
|
+
end
|
288
|
+
|
289
|
+
#######################################################################
|
290
|
+
|
291
|
+
class ArgumentsCheckingTest < ActiveSupport::TestCase
|
292
|
+
def arg_err(&block)
|
293
|
+
lambda do
|
294
|
+
block.call
|
295
|
+
end.should raise_error(ArgumentError)
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should raise ArgumentError without a block" do
|
299
|
+
arg_err do
|
300
|
+
class FailureController < ApplicationController
|
301
|
+
access_control
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
it "should raise ArgumentError with 1st argument which is not a symbol" do
|
307
|
+
arg_err do
|
308
|
+
class FailureController < ApplicationController
|
309
|
+
access_control 123 do end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should raise ArgumentError with more than 1 positional argument" do
|
315
|
+
arg_err do
|
316
|
+
class FailureController < ApplicationController
|
317
|
+
access_control :foo, :bar do end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should raise ArgumentError with :helper => true and no method name" do
|
323
|
+
arg_err do
|
324
|
+
class FailureController < ApplicationController
|
325
|
+
access_control :helper => true do end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should raise ArgumentError with :helper => :method and a method name" do
|
331
|
+
arg_err do
|
332
|
+
class FailureController < ApplicationController
|
333
|
+
access_control :meth, :helper => :another_meth do end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|