id_please 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +5 -2
- data/VERSION +1 -1
- data/id_please.gemspec +17 -7
- data/lib/id_please/config.rb +6 -2
- data/lib/id_please/model_extensions/for_group.rb +26 -0
- data/lib/id_please/model_extensions/for_object.rb +123 -0
- data/lib/id_please/model_extensions/for_subject.rb +181 -0
- data/lib/id_please/model_extensions.rb +202 -104
- data/lib/id_please.rb +5 -0
- data/test/id_please_test.rb +0 -0
- data/test/roles_test.rb +317 -0
- data/test/support/models.rb +33 -0
- data/test/support/schema.rb +36 -0
- data/test/test_helper.rb +44 -0
- metadata +37 -6
- data/test/helper.rb +0 -12
- data/test/test_id_please.rb +0 -7
data/Rakefile
CHANGED
@@ -11,6 +11,9 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/tastyhat/id_please"
|
12
12
|
gem.authors = ["James Stuart"]
|
13
13
|
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
gem.add_development_dependency "thoughtbot-factory_girl", ">= 0"
|
15
|
+
gem.add_development_dependency "ruby-debug", ">= 0"
|
16
|
+
gem.add_dependency "searchlogic"
|
14
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
18
|
end
|
16
19
|
Jeweler::GemcutterTasks.new
|
@@ -21,7 +24,7 @@ end
|
|
21
24
|
require 'rake/testtask'
|
22
25
|
Rake::TestTask.new(:test) do |test|
|
23
26
|
test.libs << 'lib' << 'test'
|
24
|
-
test.pattern = 'test
|
27
|
+
test.pattern = 'test/**/*_test.rb'
|
25
28
|
test.verbose = true
|
26
29
|
end
|
27
30
|
|
@@ -29,7 +32,7 @@ begin
|
|
29
32
|
require 'rcov/rcovtask'
|
30
33
|
Rcov::RcovTask.new do |test|
|
31
34
|
test.libs << 'test'
|
32
|
-
test.pattern = 'test
|
35
|
+
test.pattern = 'test/**/*_test.rb'
|
33
36
|
test.verbose = true
|
34
37
|
end
|
35
38
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/id_please.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{id_please}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["James Stuart"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-28}
|
13
13
|
s.description = %q{Access control gem}
|
14
14
|
s.email = %q{tastyhat@jamesstuart.org}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,9 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/id_please/model_extensions/for_group.rb",
|
32
32
|
"lib/id_please/model_extensions/for_object.rb",
|
33
33
|
"lib/id_please/model_extensions/for_subject.rb",
|
34
|
-
"lib/id_please/model_extensions/for_user.rb"
|
35
|
-
"test/helper.rb",
|
36
|
-
"test/test_id_please.rb"
|
34
|
+
"lib/id_please/model_extensions/for_user.rb"
|
37
35
|
]
|
38
36
|
s.homepage = %q{http://github.com/tastyhat/id_please}
|
39
37
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -41,8 +39,11 @@ Gem::Specification.new do |s|
|
|
41
39
|
s.rubygems_version = %q{1.3.5}
|
42
40
|
s.summary = %q{Access control gem for rails}
|
43
41
|
s.test_files = [
|
44
|
-
"test/
|
45
|
-
"test/
|
42
|
+
"test/id_please_test.rb",
|
43
|
+
"test/roles_test.rb",
|
44
|
+
"test/support/models.rb",
|
45
|
+
"test/support/schema.rb",
|
46
|
+
"test/test_helper.rb"
|
46
47
|
]
|
47
48
|
|
48
49
|
if s.respond_to? :specification_version then
|
@@ -51,11 +52,20 @@ Gem::Specification.new do |s|
|
|
51
52
|
|
52
53
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
54
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<thoughtbot-factory_girl>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<searchlogic>, [">= 0"])
|
54
58
|
else
|
55
59
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
60
|
+
s.add_dependency(%q<thoughtbot-factory_girl>, [">= 0"])
|
61
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
62
|
+
s.add_dependency(%q<searchlogic>, [">= 0"])
|
56
63
|
end
|
57
64
|
else
|
58
65
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
66
|
+
s.add_dependency(%q<thoughtbot-factory_girl>, [">= 0"])
|
67
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
68
|
+
s.add_dependency(%q<searchlogic>, [">= 0"])
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
data/lib/id_please/config.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
module IdPlease
|
2
2
|
@@config = {
|
3
3
|
:default_role_class_name => 'Role',
|
4
|
-
:
|
4
|
+
:default_subject_class_name => 'User',
|
5
5
|
:default_group_class_name => 'Group',
|
6
6
|
:default_assignment_class_name => 'Assignment',
|
7
|
-
:
|
7
|
+
:default_group_role => 'member',
|
8
|
+
:default_subject_method => :current_user,
|
9
|
+
:default_association_name => :assigned_roles,
|
10
|
+
:groups_enabled => true,
|
11
|
+
:nested_groups => true
|
8
12
|
}
|
9
13
|
|
10
14
|
mattr_reader :config
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module IdPlease
|
2
|
+
module ModelExtensions
|
3
|
+
module ForGroup
|
4
|
+
def children(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
all_children = _auth_assign_class.role_name_eq(_auth_group_role).role_authorizable_eq(self).all(:include => :subject).collect(&:subject)
|
7
|
+
|
8
|
+
if _auth_nested_groups == true && options[:nested] != false
|
9
|
+
all_children.select { |child| child._auth_is_group == true }.each do |child|
|
10
|
+
all_children << child.children
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
all_children.flatten.uniq
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_role!(role_name, object = nil)
|
18
|
+
if object && object.kind_of?(self.class) && role_name.to_s == _auth_group_role && self.children.include?(object)
|
19
|
+
raise "Attempt to make circular membership loop"
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module IdPlease
|
2
|
+
module ModelExtensions
|
3
|
+
module ForObject
|
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, options = {})
|
13
|
+
subject.has_role?(role_name, self, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
# Add role on the object to specified subject.
|
20
|
+
#
|
21
|
+
# @param [Symbol,String] role_name Role name
|
22
|
+
# @param [Subject] subject Subject to add role for
|
23
|
+
# @see Acl9::ModelExtensions::Subject#has_role!
|
24
|
+
def accepts_role!(role_name, subject)
|
25
|
+
subject.has_role!(role_name, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def list_subjects_all_roles(*args)
|
30
|
+
options = args.extract_options!
|
31
|
+
|
32
|
+
find_subjects(nil, options)
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def list_subjects_for(role_name, *args)
|
37
|
+
options = args.extract_options!
|
38
|
+
|
39
|
+
find_subjects(role_name, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
def method_missing(method_sym, *arguments, &block)
|
45
|
+
# the first argument is a Symbol, so you need to_s it if you want to pattern match
|
46
|
+
if method_sym.to_s =~ /^list_(.+)$/
|
47
|
+
list_subjects_for($1.singularize, *arguments)
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
##
|
55
|
+
# Free specified subject of a role on this object.
|
56
|
+
#
|
57
|
+
# @param [Symbol,String] role_name Role name
|
58
|
+
# @param [Subject] subject Subject to remove role from
|
59
|
+
# @see Acl9::ModelExtensions::Subject#has_no_role!
|
60
|
+
def accepts_no_role!(role_name, subject)
|
61
|
+
subject.has_no_role!(role_name, self)
|
62
|
+
end
|
63
|
+
|
64
|
+
# ##
|
65
|
+
# # Are there any roles for the specified +subject+ on this object?
|
66
|
+
# #
|
67
|
+
# # @param [Subject] subject Subject to query roles
|
68
|
+
# # @return [Boolean] Returns true if +subject+ has any roles on this object.
|
69
|
+
# # @see Acl9::ModelExtensions::Subject#has_roles_for?
|
70
|
+
# def accepts_roles_by?(subject)
|
71
|
+
# subject.has_roles_for? self
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# alias :accepts_role_by? :accepts_roles_by?
|
75
|
+
#
|
76
|
+
# ##
|
77
|
+
# # Which roles does +subject+ have on this object?
|
78
|
+
# #
|
79
|
+
# # @return [Array<Role>] Role instances, associated both with +subject+ and +object+
|
80
|
+
# # @param [Subject] subject Subject to query roles
|
81
|
+
# # @see Acl9::ModelExtensions::Subject#roles_for
|
82
|
+
# def accepted_roles_by(subject)
|
83
|
+
# subject.roles_for self
|
84
|
+
# end
|
85
|
+
|
86
|
+
private
|
87
|
+
def get_role(role_name, object)
|
88
|
+
_auth_role_class.authorizable_eq(object).name_eq(role_name.to_s).first
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_subjects(role_name = nil, options = {})
|
92
|
+
base_subjects = if role_name
|
93
|
+
self._auth_assign_class.role_name_eq(role_name.to_s).role_authorizable_eq(self).all(:include => :subject).collect(&:subject).uniq
|
94
|
+
else
|
95
|
+
self._auth_assign_class.role_authorizable_eq(self).all(:include => :subject).collect(&:subject).uniq
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
if options[:check_groups] == false
|
100
|
+
base_subjects
|
101
|
+
else
|
102
|
+
to_find = base_subjects.dup
|
103
|
+
to_find.select { |subj| subj._auth_is_group }.each do |subj|
|
104
|
+
base_subjects << subj.children
|
105
|
+
end
|
106
|
+
|
107
|
+
base_subjects.flatten.uniq
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def _auth_role_class
|
114
|
+
self.class._auth_role_class_name.constantize
|
115
|
+
end
|
116
|
+
|
117
|
+
def _auth_assign_class
|
118
|
+
self.class._auth_assign_class_name.constantize
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
module IdPlease
|
2
|
+
module ModelExtensions
|
3
|
+
module ForSubject
|
4
|
+
def in_group!(group)
|
5
|
+
raise "Groups must be enabled for this model" unless _auth_groups_enabled
|
6
|
+
raise "Object passed in not a group" unless group.kind_of?(_auth_group_class)
|
7
|
+
|
8
|
+
self.has_role!(_auth_group_role, group)
|
9
|
+
end
|
10
|
+
|
11
|
+
def in_group?(group)
|
12
|
+
raise "Groups must be enabled for this model" unless _auth_groups_enabled
|
13
|
+
raise "Object passed in not a group" unless group.kind_of?(_auth_group_class)
|
14
|
+
|
15
|
+
self.has_role?(_auth_group_role, group)
|
16
|
+
end
|
17
|
+
|
18
|
+
def not_in_group!(group)
|
19
|
+
raise "Groups must be enabled for this model" unless _auth_groups_enabled
|
20
|
+
raise "Object passed in not a group" unless group.kind_of?(_auth_group_class)
|
21
|
+
|
22
|
+
self.has_no_role!(_auth_group_role, group)
|
23
|
+
end
|
24
|
+
|
25
|
+
def not_in_any_group!
|
26
|
+
raise "Groups must be enabled for this model" unless _auth_groups_enabled
|
27
|
+
|
28
|
+
all_groups.each { |group| self.not_in_group!(group) }
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def groups(*args)
|
33
|
+
raise "Groups must be enabled for this model" unless _auth_groups_enabled
|
34
|
+
|
35
|
+
options = args.extract_options!
|
36
|
+
all_groups = direct_parent_groups(self)
|
37
|
+
|
38
|
+
if _auth_nested_groups && options[:nested] != false
|
39
|
+
infinite_loop_counter = 25
|
40
|
+
to_find = all_groups.dup
|
41
|
+
|
42
|
+
until to_find.empty? || (infinite_loop_counter -=1) <= 0
|
43
|
+
to_find = direct_parent_groups(*to_find)
|
44
|
+
all_groups += to_find
|
45
|
+
end
|
46
|
+
|
47
|
+
all_groups.uniq
|
48
|
+
else
|
49
|
+
all_groups
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
def has_role?(role_name, object = nil, option_hash = {})
|
57
|
+
subjects_to_check = option_hash[:check_groups] == false ? [self] : self_and_groups
|
58
|
+
|
59
|
+
!Role.authorizable_eq(object).name_eq(role_name.to_s).assignments_subject_eq(*subjects_to_check).empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_role!(role_name, object = nil)
|
63
|
+
role = get_role(role_name, object)
|
64
|
+
|
65
|
+
if role.nil?
|
66
|
+
role_attrs = case object
|
67
|
+
when Class
|
68
|
+
{ :authorizable_type => object.to_s }
|
69
|
+
when nil
|
70
|
+
{}
|
71
|
+
else
|
72
|
+
{ :authorizable => object }
|
73
|
+
end.merge(:name => role_name.to_s)
|
74
|
+
|
75
|
+
role = self._auth_assigned_roles.create!(role_attrs)
|
76
|
+
else
|
77
|
+
unless assigned_to_role?(role)
|
78
|
+
self._auth_assigned_roles << role
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
role
|
83
|
+
end
|
84
|
+
|
85
|
+
def has_roles_for?(object)
|
86
|
+
!get_assigned_roles_for(object).empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
alias :has_role_for? :has_roles_for?
|
90
|
+
|
91
|
+
def roles_for(object)
|
92
|
+
get_assigned_roles_for(object)
|
93
|
+
end
|
94
|
+
|
95
|
+
def has_no_role!(role_name, object = nil)
|
96
|
+
role = get_role(role_name, object)
|
97
|
+
|
98
|
+
remove_from_role(role)
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_no_roles_for!(object = nil)
|
102
|
+
roles_for(object).each { |role| remove_from_role(role) }
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
##
|
108
|
+
# Unassign all roles from +self+.
|
109
|
+
def has_no_roles!
|
110
|
+
roles = self._auth_assigned_roles.clone
|
111
|
+
roles.each do |role|
|
112
|
+
remove_from_role(role)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def direct_parent_groups(*subjects)
|
119
|
+
_auth_role_class.assignments_subject_eq(*subjects).name_eq(_auth_group_role).authorizable_type_eq(_auth_group_class_name).all(:include => :authorizable).collect(&:authorizable)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self_and_groups
|
123
|
+
if _auth_groups_enabled
|
124
|
+
groups << self
|
125
|
+
else
|
126
|
+
[self]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def remove_from_role(role)
|
131
|
+
if role
|
132
|
+
self._auth_assigned_roles.delete(role)
|
133
|
+
|
134
|
+
role.destroy if self._auth_assign_class.role_id_eq(role).empty?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_role(role_name, object)
|
139
|
+
_auth_role_class.authorizable_eq(object).name_eq(role_name.to_s).first
|
140
|
+
end
|
141
|
+
|
142
|
+
def assigned_to_role?(role)
|
143
|
+
!!_auth_assign_class.subject_eq(self).role_id_eq(role).first
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_assigned_roles_for(object)
|
147
|
+
_auth_role_class.authorizable_eq(object).assignments_subject_eq(self)
|
148
|
+
end
|
149
|
+
|
150
|
+
protected
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
def _auth_role_class
|
155
|
+
self.class._auth_role_class_name.constantize
|
156
|
+
end
|
157
|
+
|
158
|
+
def _auth_subject_class
|
159
|
+
self.class._auth_subject_class_name.constantize
|
160
|
+
end
|
161
|
+
|
162
|
+
def _auth_group_class
|
163
|
+
self.class._auth_group_class_name.constantize
|
164
|
+
end
|
165
|
+
|
166
|
+
def _auth_assign_class
|
167
|
+
self.class._auth_assign_class_name.constantize
|
168
|
+
end
|
169
|
+
|
170
|
+
def _auth_role_assoc
|
171
|
+
self.class._auth_role_assoc_name
|
172
|
+
end
|
173
|
+
|
174
|
+
def _auth_assigned_roles
|
175
|
+
send(self._auth_role_assoc)
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'model_extensions', 'for_subject')
|
2
|
-
require File.join(File.dirname(__FILE__), 'model_extensions', 'for_object')
|
3
1
|
|
4
2
|
module IdPlease
|
5
3
|
module ModelExtensions #:nodoc:
|
@@ -8,132 +6,232 @@ module IdPlease
|
|
8
6
|
end
|
9
7
|
|
10
8
|
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
|
-
# @option options [String] :association_name (Acl9::config[:default_association_name])
|
20
|
-
# Association name (e.g. ':roles')
|
21
|
-
# @example
|
22
|
-
# class User < ActiveRecord::Base
|
23
|
-
# acts_as_authorization_subject
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# user = User.new
|
27
|
-
# user.roles #=> returns Role objects, associated with the user
|
28
|
-
# user.has_role!(...)
|
29
|
-
# user.has_no_role!(...)
|
30
|
-
#
|
31
|
-
# # other functions from Acl9::ModelExtensions::Subject are made available
|
32
|
-
#
|
33
|
-
# @see Acl9::ModelExtensions::Subject
|
34
|
-
#
|
35
9
|
def acts_as_authorization_subject(options = {})
|
36
|
-
assoc = options[:association_name] ||
|
37
|
-
role = options[:role_class_name] ||
|
38
|
-
|
39
|
-
|
10
|
+
assoc = options[:association_name] || IdPlease.config[:default_association_name]
|
11
|
+
role = options[:role_class_name] || IdPlease.config[:default_role_class_name]
|
12
|
+
role_assoc = role.downcase.underscore.to_sym
|
13
|
+
|
14
|
+
group = options[:group_class_name] || IdPlease.config[:default_group_class_name]
|
15
|
+
|
16
|
+
assign = options[:assignment_class_name] || IdPlease.config[:default_assignment_class_name]
|
17
|
+
assign_table = assign.constantize.table_name
|
18
|
+
|
19
|
+
#TODO
|
20
|
+
groups_enabled = options.has_key?(:groups_enabled) ? options[:groups_enabled] : IdPlease.config[:groups_enabled]
|
21
|
+
nested_groups = options.has_key?(:nested_groups) ? options[:nested_groups] : IdPlease.config[:nested_groups]
|
22
|
+
group_role = options[:group_role] || IdPlease.config[:default_group_role]
|
23
|
+
|
24
|
+
has_many assign_table.to_sym, :class_name => assign, :as => :subject, :dependent => :destroy
|
25
|
+
has_many assoc, :through => assign_table.to_sym, :source => role_assoc
|
40
26
|
|
41
|
-
has_and_belongs_to_many assoc, :class_name => role, :join_table => join_table
|
42
27
|
|
43
28
|
cattr_accessor :_auth_role_class_name, :_auth_subject_class_name,
|
44
|
-
:
|
29
|
+
:_auth_groups_enabled, :_auth_nested_groups,
|
30
|
+
:_auth_group_class_name, :_auth_assign_class_name,
|
31
|
+
:_auth_role_assoc_name, :_auth_group_role,
|
32
|
+
:_auth_is_group
|
45
33
|
|
46
34
|
self._auth_role_class_name = role
|
47
35
|
self._auth_subject_class_name = self.to_s
|
36
|
+
self._auth_assign_class_name = assign
|
37
|
+
self._auth_group_class_name = group
|
48
38
|
self._auth_role_assoc_name = assoc
|
39
|
+
self._auth_group_role = group_role
|
40
|
+
self._auth_groups_enabled = groups_enabled
|
41
|
+
self._auth_nested_groups = nested_groups
|
42
|
+
self._auth_is_group = false
|
43
|
+
|
49
44
|
|
50
|
-
include
|
45
|
+
include IdPlease::ModelExtensions::ForSubject
|
51
46
|
end
|
47
|
+
|
48
|
+
def acts_as_authorization_group(options = {})
|
49
|
+
assoc = options[:association_name] || IdPlease.config[:default_association_name]
|
50
|
+
role = options[:role_class_name] || IdPlease.config[:default_role_class_name]
|
51
|
+
role_assoc = role.downcase.underscore.to_sym
|
52
|
+
|
53
|
+
subject = options[:subject_class_name] || IdPlease.config[:default_subject_class_name]
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# class Product < ActiveRecord::Base
|
62
|
-
# acts_as_authorization_object
|
63
|
-
# end
|
64
|
-
#
|
65
|
-
# product = Product.new
|
66
|
-
# product.accepted_roles #=> returns Role objects, associated with the product
|
67
|
-
# product.users #=> returns User objects, associated with the product
|
68
|
-
# product.accepts_role!(...)
|
69
|
-
# product.accepts_no_role!(...)
|
70
|
-
# # other functions from Acl9::ModelExtensions::Object are made available
|
71
|
-
#
|
72
|
-
# @see Acl9::ModelExtensions::Object
|
73
|
-
#
|
74
|
-
def acts_as_authorization_object(options = {})
|
75
|
-
subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
|
76
|
-
subj_table = subject.constantize.table_name
|
77
|
-
subj_col = subject.underscore
|
55
|
+
assign = options[:assignment_class_name] || IdPlease.config[:default_assignment_class_name]
|
56
|
+
assign_table = assign.constantize.table_name
|
57
|
+
|
58
|
+
#TODO
|
59
|
+
|
60
|
+
groups_enabled = options.has_key?(:groups_enabled) ? options[:groups_enabled] : IdPlease.config[:groups_enabled]
|
61
|
+
nested_groups = options.has_key?(:nested_groups) ? options[:nested_groups] : IdPlease.config[:nested_groups]
|
62
|
+
raise "Do not use acts_as_authorization_group if groups are disabled." unless groups_enabled
|
78
63
|
|
79
|
-
role = options[:role_class_name] || Acl9::config[:default_role_class_name]
|
80
|
-
role_table = role.constantize.table_name
|
81
64
|
|
82
|
-
|
83
|
-
FROM #{subj_table}
|
84
|
-
INNER JOIN #{role_table}_#{subj_table} ON #{subj_col}_id = #{subj_table}.id
|
85
|
-
INNER JOIN #{role_table} ON #{role_table}.id = #{role.underscore}_id
|
86
|
-
EOS
|
65
|
+
group_role = options[:group_role] || IdPlease.config[:default_group_role]
|
87
66
|
|
88
|
-
|
89
|
-
|
90
|
-
AND authorizable_id = #{column_for_attribute(self.class.primary_key).text? ? "'#{id}'": id}
|
91
|
-
EOS
|
67
|
+
has_many assign_table.to_sym, :class_name => assign, :as => :subject, :dependent => :destroy
|
68
|
+
has_many assoc, :through => :assignments, :class_name => role, :source => role_assoc
|
92
69
|
|
93
|
-
has_many :accepted_roles, :as => :authorizable, :class_name => role, :dependent => :destroy
|
94
70
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
71
|
+
cattr_accessor :_auth_role_class_name, :_auth_subject_class_name,
|
72
|
+
:_auth_groups_enabled, :_auth_nested_groups,
|
73
|
+
:_auth_group_class_name, :_auth_assign_class_name,
|
74
|
+
:_auth_role_assoc_name, :_auth_group_role,
|
75
|
+
:_auth_is_group
|
76
|
+
|
77
|
+
self._auth_role_class_name = role
|
78
|
+
self._auth_subject_class_name = subject
|
79
|
+
self._auth_assign_class_name = assign
|
80
|
+
self._auth_group_class_name = self.to_s
|
81
|
+
self._auth_role_assoc_name = assoc
|
82
|
+
self._auth_groups_enabled = groups_enabled
|
83
|
+
self._auth_nested_groups = nested_groups
|
84
|
+
self._auth_group_role = group_role
|
85
|
+
self._auth_is_group = true
|
86
|
+
|
87
|
+
include IdPlease::ModelExtensions::ForSubject
|
88
|
+
include IdPlease::ModelExtensions::ForGroup
|
89
|
+
include IdPlease::ModelExtensions::ForObject
|
90
|
+
end
|
91
|
+
|
92
|
+
|
99
93
|
|
100
|
-
|
94
|
+
def acts_as_authorization_object(options = {})
|
95
|
+
subject = options[:subject_class_name] || IdPlease.config[:default_subject_class_name]
|
96
|
+
subj_table = subject.constantize.table_name
|
97
|
+
|
98
|
+
role = options[:role_class_name] || IdPlease.config[:default_role_class_name]
|
99
|
+
role_table = role.constantize.table_name
|
100
|
+
|
101
|
+
assign = options[:assignment_class_name] || IdPlease.config[:default_assignment_class_name]
|
102
|
+
|
103
|
+
groups_enabled = options[:groups_enabled] || IdPlease.config[:groups_enabled]
|
104
|
+
nested_groups = options[:nested_groups] || IdPlease.config[:nested_groups]
|
105
|
+
group_role = options[:group_role] || IdPlease.config[:group_role]
|
106
|
+
|
107
|
+
|
108
|
+
has_many role_table.to_sym, :dependent => :destroy, :as => :authorizable
|
109
|
+
|
110
|
+
|
111
|
+
cattr_accessor :_auth_role_class_name, :_auth_groups_enabled,
|
112
|
+
:_auth_nested_groups, :_auth_assign_class_name,
|
113
|
+
:_auth_group_role
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
self._auth_role_class_name = role
|
118
|
+
self._auth_assign_class_name = assign
|
119
|
+
self._auth_group_role = group_role
|
120
|
+
self._auth_groups_enabled = groups_enabled
|
121
|
+
self._auth_nested_groups = nested_groups
|
122
|
+
|
123
|
+
|
124
|
+
include IdPlease::ModelExtensions::ForObject
|
101
125
|
end
|
102
126
|
|
103
|
-
# Make a class an auth role class.
|
104
|
-
#
|
105
|
-
# You'll probably never create or use objects of this class directly.
|
106
|
-
# Various auth. subject and object methods will do that for you
|
107
|
-
# internally.
|
108
|
-
#
|
109
|
-
# @param [Hash] options the options for tuning
|
110
|
-
# @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
|
111
|
-
# Subject class name (e.g. 'User', or 'Account)
|
112
|
-
# @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
|
113
|
-
# Join table name (e.g. 'accounts_account_roles')
|
114
|
-
#
|
115
|
-
# @example
|
116
|
-
# class Role < ActiveRecord::Base
|
117
|
-
# acts_as_authorization_role
|
118
|
-
# end
|
119
|
-
#
|
120
|
-
# @see Acl9::ModelExtensions::Subject#has_role!
|
121
|
-
# @see Acl9::ModelExtensions::Subject#has_role?
|
122
|
-
# @see Acl9::ModelExtensions::Subject#has_no_role!
|
123
|
-
# @see Acl9::ModelExtensions::Object#accepts_role!
|
124
|
-
# @see Acl9::ModelExtensions::Object#accepts_role?
|
125
|
-
# @see Acl9::ModelExtensions::Object#accepts_no_role!
|
126
127
|
def acts_as_authorization_role(options = {})
|
127
|
-
|
128
|
-
|
129
|
-
|
128
|
+
validates_presence_of :name
|
129
|
+
validates_uniqueness_of :name, :scope => [:authorizable_type, :authorizable_id]
|
130
|
+
|
131
|
+
subject = options[:subject_class_name] || IdPlease.config[:default_subject_class_name]
|
132
|
+
|
133
|
+
assign = options[:assignment_class_name] || IdPlease.config[:default_assignment_class_name]
|
134
|
+
assign_table = assign.constantize.table_name
|
135
|
+
|
136
|
+
groups_enabled = options[:groups_enabled] || IdPlease.config[:groups_enabled]
|
130
137
|
|
131
|
-
|
132
|
-
|
133
|
-
:join_table => join_table
|
138
|
+
nested_groups = options[:nested_groups] || IdPlease.config[:nested_groups]
|
139
|
+
group_role = options[:group_role] || IdPlease.config[:group_role]
|
134
140
|
|
141
|
+
role_table = self.table_name
|
142
|
+
|
143
|
+
has_many assign_table.to_sym, :dependent => :destroy
|
135
144
|
belongs_to :authorizable, :polymorphic => true
|
145
|
+
|
146
|
+
|
147
|
+
self.named_scope :authorizable_eq, lambda { |*objs|
|
148
|
+
obj_hash = {}
|
149
|
+
cond_sql = []
|
150
|
+
cond_parameters = []
|
151
|
+
|
152
|
+
if objs.include?(nil) || objs.empty?
|
153
|
+
cond_sql << "#{role_table}.authorizable_type IS NULL AND #{role_table}.authorizable_id IS NULL"
|
154
|
+
end
|
155
|
+
|
156
|
+
objs.compact!
|
157
|
+
|
158
|
+
unless objs.nil? || objs.length == 0
|
159
|
+
obj_hash = {}
|
160
|
+
|
161
|
+
objs.each { |obj|
|
162
|
+
obj_hash.has_key?(obj.class) ? obj_hash[obj.class] << obj.id : obj_hash[obj.class] = [obj.id]
|
163
|
+
}
|
164
|
+
|
165
|
+
|
166
|
+
obj_hash.each_pair { |class_name, ids|
|
167
|
+
cond_sql << "#{role_table}.authorizable_type = '#{class_name}' AND #{role_table}.authorizable_id IN(?)"
|
168
|
+
cond_parameters << ids
|
169
|
+
}
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
{:conditions => [cond_sql.join(" OR "), *cond_parameters].compact}
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
include IdPlease::ModelExtensions::ForRole
|
178
|
+
|
179
|
+
|
180
|
+
cattr_accessor :_auth_role_class_name, :_auth_groups_enabled,
|
181
|
+
:_auth_nested_groups, :_auth_assign_class_name
|
182
|
+
|
183
|
+
self._auth_role_class_name = self.to_s
|
184
|
+
self._auth_assign_class_name = assign
|
185
|
+
|
186
|
+
self._auth_groups_enabled = groups_enabled
|
187
|
+
self._auth_nested_groups = nested_groups
|
188
|
+
|
189
|
+
|
190
|
+
|
136
191
|
end
|
192
|
+
|
193
|
+
|
194
|
+
def acts_as_authorization_assignment(options = {})
|
195
|
+
role = options[:role_class_name] || IdPlease.config[:default_role_class_name]
|
196
|
+
role_assoc = role.downcase.underscore.to_sym
|
197
|
+
|
198
|
+
assignment_table = self.table_name
|
199
|
+
|
200
|
+
belongs_to :subject, :polymorphic => true
|
201
|
+
#TODO :fix:
|
202
|
+
belongs_to role_assoc
|
203
|
+
|
204
|
+
|
205
|
+
self.named_scope :subject_eq, lambda { |*objs|
|
206
|
+
obj_hash = {}
|
207
|
+
cond_sql = []
|
208
|
+
cond_parameters = []
|
209
|
+
|
210
|
+
if objs.include?(nil) || objs.empty?
|
211
|
+
cond_sql << "#{assignment_table}.subject_type IS NULL AND #{assignment_table}.subject_id IS NULL"
|
212
|
+
end
|
213
|
+
|
214
|
+
objs.compact!
|
215
|
+
|
216
|
+
unless objs.nil? || objs.length == 0
|
217
|
+
obj_hash = {}
|
218
|
+
|
219
|
+
objs.each { |obj|
|
220
|
+
obj_hash.has_key?(obj.class) ? obj_hash[obj.class] << obj.id : obj_hash[obj.class] = [obj.id]
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
obj_hash.each_pair { |class_name, ids|
|
225
|
+
cond_sql << "#{assignment_table}.subject_type = '#{class_name}' AND #{assignment_table}.subject_id IN(?)"
|
226
|
+
cond_parameters << ids
|
227
|
+
}
|
228
|
+
|
229
|
+
end
|
230
|
+
{:conditions => [cond_sql.join(" OR "), *cond_parameters].compact}
|
231
|
+
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
137
235
|
end
|
138
236
|
end
|
139
237
|
end
|
data/lib/id_please.rb
CHANGED
@@ -2,6 +2,11 @@ require 'id_please/config'
|
|
2
2
|
|
3
3
|
if defined? ActiveRecord::Base
|
4
4
|
require 'id_please/model_extensions'
|
5
|
+
require 'id_please/model_extensions/for_subject'
|
6
|
+
require 'id_please/model_extensions/for_subject_with_group'
|
7
|
+
require 'id_please/model_extensions/for_object'
|
8
|
+
require 'id_please/model_extensions/for_group'
|
9
|
+
require 'id_please/model_extensions/for_role'
|
5
10
|
ActiveRecord::Base.send(:include, IdPlease::ModelExtensions)
|
6
11
|
end
|
7
12
|
|
File without changes
|
data/test/roles_test.rb
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/models'
|
3
|
+
load 'support/schema.rb'
|
4
|
+
|
5
|
+
class ActiveSupport::TestCase
|
6
|
+
def self.should_handle_basic_role_manipulation(subject_class)
|
7
|
+
context "for #{subject_class.to_s}: " do
|
8
|
+
setup do
|
9
|
+
Role.delete_all
|
10
|
+
[subject_class, Foo, Bar, Uuid].each { |c| c.delete_all }
|
11
|
+
@subject1 = subject_class.create!
|
12
|
+
@subject2 = subject_class.create!
|
13
|
+
@foo = Foo.create!
|
14
|
+
@bar = Bar.create!
|
15
|
+
@uuid = Uuid.create!(:uuid => "C41642EE-2780-0001-189F-17F3101B26E0")
|
16
|
+
end
|
17
|
+
|
18
|
+
should "not have any roles by default" do
|
19
|
+
%w(user manager admin owner).each do |role|
|
20
|
+
assert_false @subject1.has_role?(role)
|
21
|
+
assert_false @subject1.has_role?(role, @foo)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "Given a global role" do
|
26
|
+
setup do
|
27
|
+
@subject1.has_role!(:admin)
|
28
|
+
end
|
29
|
+
|
30
|
+
should_change "create a role successfully", :by => 1 do
|
31
|
+
Role.count
|
32
|
+
end
|
33
|
+
|
34
|
+
should "not distinguish between symbols and strings" do
|
35
|
+
assert @subject1.has_role?("admin")
|
36
|
+
end
|
37
|
+
|
38
|
+
should "assign roles to a specific user" do
|
39
|
+
assert @subject1.has_role?(:admin)
|
40
|
+
assert_false @subject2.has_role?(:admin)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "distinguish between names of roles" do
|
44
|
+
assert_false @subject1.has_role?(:manager)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "not count a global role as an object role" do
|
48
|
+
assert_false @subject1.has_role?(:admin, @foo)
|
49
|
+
assert_false @subject1.has_roles_for?(@foo)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "not let objects accept it" do
|
53
|
+
[@foo,@bar].each do |obj|
|
54
|
+
assert_false obj.accepts_role?(:admin, @subject1)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
should "reading roles should not add another role" do
|
59
|
+
assert_no_difference "Role.count" do
|
60
|
+
@subject1.has_role!(:admin)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "with two users" do
|
65
|
+
setup do
|
66
|
+
@subject2.has_role!(:admin)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "remove assignments to roles through has_no_role!" do
|
70
|
+
@subject1.has_no_role!(:admin)
|
71
|
+
assert_false @subject1.has_role?(:admin)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "only delete roles when they have no assignments" do
|
75
|
+
assert_no_difference 'Role.count' do
|
76
|
+
@subject1.has_no_role!(:admin)
|
77
|
+
end
|
78
|
+
|
79
|
+
assert_difference 'Role.count', -1 do
|
80
|
+
@subject2.has_no_role!(:admin)
|
81
|
+
end
|
82
|
+
|
83
|
+
assert_false @subject1.has_role?(:admin)
|
84
|
+
assert_false @subject2.has_role?(:admin)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "Given an object role" do
|
91
|
+
setup do
|
92
|
+
@subject1.has_role!(:admin, @foo)
|
93
|
+
end
|
94
|
+
|
95
|
+
should_change "create a role successfully", :by => 1 do
|
96
|
+
Role.count
|
97
|
+
end
|
98
|
+
|
99
|
+
should "assign roles to a specific user" do
|
100
|
+
assert @subject1.has_role?(:admin, @foo)
|
101
|
+
assert @subject1.has_roles_for?(@foo)
|
102
|
+
assert @subject1.has_role_for?(@foo)
|
103
|
+
|
104
|
+
roles = @subject1.roles_for(@foo)
|
105
|
+
assert_equal 1, roles.length
|
106
|
+
assert_equal "admin", roles.first.name
|
107
|
+
|
108
|
+
assert_false @subject1.has_role?(:manager, @foo)
|
109
|
+
assert_false @subject2.has_role?(:admin, @foo)
|
110
|
+
assert_false @subject2.has_roles_for?(@foo)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "let the object accept it" do
|
114
|
+
assert @foo.accepts_role?(:admin, @subject1)
|
115
|
+
assert_false @bar.accepts_role?(:admin, @subject1)
|
116
|
+
|
117
|
+
@bar.accepts_role!(:admin, @subject1)
|
118
|
+
assert @bar.accepts_role?(:admin, @subject1)
|
119
|
+
|
120
|
+
@bar.accepts_no_role!(:admin, @subject1)
|
121
|
+
assert_false @bar.accepts_role?(:admin, @subject1)
|
122
|
+
end
|
123
|
+
|
124
|
+
should "allow the object to list its roles" do
|
125
|
+
assert_equal 1, @foo.roles.size
|
126
|
+
end
|
127
|
+
|
128
|
+
should "not count an object role as an global role" do
|
129
|
+
assert_false @subject1.has_role?(:admin)
|
130
|
+
end
|
131
|
+
|
132
|
+
should "reuse roles" do
|
133
|
+
assert_no_difference 'Role.count' do
|
134
|
+
@subject2.has_role!(:admin, @foo)
|
135
|
+
end
|
136
|
+
|
137
|
+
assert_difference 'Role.count', 1 do
|
138
|
+
@subject2.has_role!(:manager, @foo)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "with two users" do
|
143
|
+
setup do
|
144
|
+
@subject1.has_role!(:admin)
|
145
|
+
@subject2.has_role!(:admin, @foo)
|
146
|
+
@subject1.has_role!(:admin, @bar)
|
147
|
+
end
|
148
|
+
|
149
|
+
should "let objects be able to query their subjects" do
|
150
|
+
assert_same_elements @foo.list_subjects_for(:admin), [@subject1, @subject2]
|
151
|
+
assert_same_elements @foo.list_subjects_all_roles, [@subject1, @subject2]
|
152
|
+
end
|
153
|
+
|
154
|
+
should "dynamically build methods to query" do
|
155
|
+
assert_same_elements @foo.list_subjects_for(:admin), @foo.list_admins
|
156
|
+
end
|
157
|
+
|
158
|
+
should "remove assignments to object roles through has_no_role!" do
|
159
|
+
@subject1.has_no_role!(:admin, @foo)
|
160
|
+
|
161
|
+
assert @subject1.has_role?(:admin)
|
162
|
+
assert @subject1.has_role?(:admin, @bar)
|
163
|
+
assert_false @subject1.has_role?(:admin, @foo)
|
164
|
+
end
|
165
|
+
|
166
|
+
should "only delete roles when they have no assignments" do
|
167
|
+
assert_difference 'Role.count', 0 do
|
168
|
+
@subject2.has_no_role!(:admin, @foo)
|
169
|
+
end
|
170
|
+
|
171
|
+
assert_false @subject2.has_role?(:admin)
|
172
|
+
|
173
|
+
assert_difference 'Role.count', -1 do
|
174
|
+
@subject1.has_no_role!(:admin, @foo)
|
175
|
+
end
|
176
|
+
|
177
|
+
assert_false @subject1.has_role?(:admin, @foo)
|
178
|
+
end
|
179
|
+
|
180
|
+
should "delete all roles for a given object with has_no_roles_for!" do
|
181
|
+
assert_difference 'Role.count', 1 do
|
182
|
+
@subject1.has_role!(:manager, @bar)
|
183
|
+
end
|
184
|
+
|
185
|
+
assert_difference '@subject1.assigned_roles.size', -2 do
|
186
|
+
@subject1.has_no_roles_for!(@bar)
|
187
|
+
end
|
188
|
+
|
189
|
+
assert_false @subject1.has_roles_for?(@bar)
|
190
|
+
assert @subject1.has_role?(:admin)
|
191
|
+
assert @subject1.has_role?(:admin, @foo)
|
192
|
+
end
|
193
|
+
|
194
|
+
should "delete all roles with has_no_roles!" do
|
195
|
+
@subject1.has_no_roles!
|
196
|
+
assert_equal @subject1.assigned_roles.length, 0
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class RolesTest < ActiveSupport::TestCase
|
207
|
+
context "role testing" do
|
208
|
+
should_handle_basic_role_manipulation(User)
|
209
|
+
should_handle_basic_role_manipulation(Group)
|
210
|
+
should_handle_basic_role_manipulation(UserNoGroup)
|
211
|
+
|
212
|
+
should "models without groups enabled raise errors when group commands are queried" do
|
213
|
+
@usernogroup = UserNoGroup.create!
|
214
|
+
assert_raises RuntimeError do
|
215
|
+
@usernogroup.groups
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "group testing: " do
|
220
|
+
setup do
|
221
|
+
@user1 = User.create!
|
222
|
+
@user2 = User.create!
|
223
|
+
@user3 = User.create!
|
224
|
+
@group1 = Group.create!
|
225
|
+
@group2 = Group.create!
|
226
|
+
@group3 = Group.create!
|
227
|
+
@group4 = Group.create!
|
228
|
+
@group5 = Group.create!
|
229
|
+
|
230
|
+
|
231
|
+
@foo = Foo.create!
|
232
|
+
@bar = Bar.create!
|
233
|
+
end
|
234
|
+
|
235
|
+
should "be able to assign users to a group" do
|
236
|
+
@user1.in_group!(@group1)
|
237
|
+
@user2.in_group!(@group1)
|
238
|
+
@user1.in_group!(@group2)
|
239
|
+
|
240
|
+
assert_same_elements @user1.groups, [@group1, @group2]
|
241
|
+
assert_same_elements @group1.children, [@user1, @user2]
|
242
|
+
assert_equal @group2.children, [@user1]
|
243
|
+
|
244
|
+
@user1.not_in_group!(@group1)
|
245
|
+
|
246
|
+
assert_same_elements @user1.groups, [@group2]
|
247
|
+
end
|
248
|
+
|
249
|
+
context "given test users and groups" do
|
250
|
+
setup do
|
251
|
+
@user1.in_group!(@group1)
|
252
|
+
@group1.in_group!(@group2)
|
253
|
+
@group2.in_group!(@group4)
|
254
|
+
|
255
|
+
@user1.in_group!(@group3)
|
256
|
+
@user2.in_group!(@group2)
|
257
|
+
end
|
258
|
+
|
259
|
+
should "avoid circularity" do
|
260
|
+
@group4.in_group!(@group5)
|
261
|
+
|
262
|
+
assert_raises RuntimeError do
|
263
|
+
@group5.in_group!(@group4)
|
264
|
+
end
|
265
|
+
|
266
|
+
assert_raises RuntimeError do
|
267
|
+
@group5.in_group!(@group1)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
should "not follow meaningless member assignments" do
|
272
|
+
@group5.has_role!(:member, @user1)
|
273
|
+
assert_equal @group5.children, []
|
274
|
+
end
|
275
|
+
|
276
|
+
should "handle nested groups" do
|
277
|
+
assert_same_elements @user1.groups(:nested => false), [@group1, @group3]
|
278
|
+
assert_same_elements @user1.groups, [@group1, @group2, @group3, @group4]
|
279
|
+
assert_same_elements @user2.groups, [@group2, @group4]
|
280
|
+
|
281
|
+
assert_same_elements @group4.children, [@group2, @group1, @user1, @user2]
|
282
|
+
assert_same_elements @group2.children, [@group1, @user1, @user2]
|
283
|
+
|
284
|
+
assert @group4.accepts_role?(:member, @user1)
|
285
|
+
assert @group2.accepts_role?(:member, @user1)
|
286
|
+
|
287
|
+
assert_false @group4.accepts_role?(:member, @user1, :check_groups => false)
|
288
|
+
end
|
289
|
+
|
290
|
+
should "check roles based on groups" do
|
291
|
+
|
292
|
+
assert @user2.has_role?(:member, @group2)
|
293
|
+
|
294
|
+
@group2.has_role!(:owner, @foo)
|
295
|
+
|
296
|
+
assert @user2.has_role?(:owner, @foo)
|
297
|
+
assert_false @user2.has_role?(:owner, @foo, :check_groups => false)
|
298
|
+
|
299
|
+
assert @user1.has_role?(:owner, @foo)
|
300
|
+
assert_false @user1.has_role?(:owner, @foo, :check_groups => false)
|
301
|
+
|
302
|
+
|
303
|
+
assert_same_elements @foo.list_owners, [@group2, @group1, @user1, @user2]
|
304
|
+
assert_same_elements @foo.list_owners(:check_groups => false), [@group2]
|
305
|
+
|
306
|
+
assert @foo.accepts_role?(:owner, @user1)
|
307
|
+
assert_false @foo.accepts_role?(:owner, @user1, :check_groups => false)
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
class Assignment < ActiveRecord::Base
|
3
|
+
acts_as_authorization_assignment
|
4
|
+
end
|
5
|
+
|
6
|
+
class Role < ActiveRecord::Base
|
7
|
+
acts_as_authorization_role
|
8
|
+
end
|
9
|
+
|
10
|
+
class User < ActiveRecord::Base
|
11
|
+
acts_as_authorization_subject
|
12
|
+
end
|
13
|
+
|
14
|
+
class UserNoGroup < ActiveRecord::Base
|
15
|
+
acts_as_authorization_subject :groups_enabled => false, :nested_groups => false
|
16
|
+
end
|
17
|
+
|
18
|
+
class Group < ActiveRecord::Base
|
19
|
+
acts_as_authorization_group
|
20
|
+
end
|
21
|
+
|
22
|
+
class Foo < ActiveRecord::Base
|
23
|
+
acts_as_authorization_object
|
24
|
+
end
|
25
|
+
|
26
|
+
class Bar < ActiveRecord::Base
|
27
|
+
acts_as_authorization_object
|
28
|
+
end
|
29
|
+
|
30
|
+
class Uuid < ActiveRecord::Base
|
31
|
+
set_primary_key "uuid"
|
32
|
+
acts_as_authorization_object
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table "assignments", :force => true do |t|
|
3
|
+
t.integer "subject_id", :null => false
|
4
|
+
t.string "subject_type", :limit => 20, :null => false
|
5
|
+
t.integer "role_id", :null => false
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table "users", :force => true do |t| end
|
10
|
+
|
11
|
+
create_table "user_no_groups", :force => true do |t| end
|
12
|
+
|
13
|
+
create_table "roles", :force => true do |t|
|
14
|
+
t.string "name", :limit => 50, :null => false
|
15
|
+
t.integer "authorizable_id"
|
16
|
+
t.string "authorizable_type", :limit => 30
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table "groups", :force => true do |t| end
|
21
|
+
|
22
|
+
create_table "foos", :force => true do |t|
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table "bars", :force => true do |t|
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
create_table "uuids", :id => false, :force => true do |t|
|
32
|
+
t.string "uuid"
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'ruby-debug'
|
5
|
+
rescue LoadError
|
6
|
+
puts "ruby-debug not loaded"
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
require 'active_record'
|
11
|
+
require 'active_support'
|
12
|
+
require 'active_support/test_case'
|
13
|
+
|
14
|
+
|
15
|
+
gem 'sqlite3-ruby'
|
16
|
+
gem 'searchlogic'
|
17
|
+
require 'searchlogic'
|
18
|
+
require "shoulda"
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
ROOT = File.join(File.dirname(__FILE__), '..')
|
23
|
+
RAILS_ROOT = ROOT
|
24
|
+
RAILS_ENV = "test"
|
25
|
+
|
26
|
+
|
27
|
+
$LOAD_PATH << File.join(ROOT, 'lib')
|
28
|
+
$LOAD_PATH << File.join(ROOT, 'lib', 'id_please')
|
29
|
+
|
30
|
+
require File.join(ROOT, 'lib', 'id_please.rb')
|
31
|
+
|
32
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
33
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
34
|
+
ActiveRecord::Base.establish_connection(config['test'])
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
module Test::Unit::Assertions
|
40
|
+
def assert_false(object, message="")
|
41
|
+
assert_equal(false, object, message)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: id_please
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Stuart
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-28 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,6 +22,36 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0"
|
24
24
|
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: thoughtbot-factory_girl
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: ruby-debug
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: searchlogic
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
25
55
|
description: Access control gem
|
26
56
|
email: tastyhat@jamesstuart.org
|
27
57
|
executables: []
|
@@ -47,8 +77,6 @@ files:
|
|
47
77
|
- lib/id_please/model_extensions/for_object.rb
|
48
78
|
- lib/id_please/model_extensions/for_subject.rb
|
49
79
|
- lib/id_please/model_extensions/for_user.rb
|
50
|
-
- test/helper.rb
|
51
|
-
- test/test_id_please.rb
|
52
80
|
has_rdoc: true
|
53
81
|
homepage: http://github.com/tastyhat/id_please
|
54
82
|
licenses: []
|
@@ -78,5 +106,8 @@ signing_key:
|
|
78
106
|
specification_version: 3
|
79
107
|
summary: Access control gem for rails
|
80
108
|
test_files:
|
81
|
-
- test/
|
82
|
-
- test/
|
109
|
+
- test/id_please_test.rb
|
110
|
+
- test/roles_test.rb
|
111
|
+
- test/support/models.rb
|
112
|
+
- test/support/schema.rb
|
113
|
+
- test/test_helper.rb
|
data/test/helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'test/unit'
|
3
|
-
require 'shoulda'
|
4
|
-
require 'active_record'
|
5
|
-
require 'active_support'
|
6
|
-
|
7
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
-
require 'id_please'
|
10
|
-
|
11
|
-
class Test::Unit::TestCase
|
12
|
-
end
|