erbac 0.0.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/.gitignore +16 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +114 -0
- data/Rakefile +11 -0
- data/erbac.gemspec +28 -0
- data/lib/erbac/action_controller_support.rb +15 -0
- data/lib/erbac/active_record_support.rb +241 -0
- data/lib/erbac/auth_manage.rb +102 -0
- data/lib/erbac/configure.rb +44 -0
- data/lib/erbac/errors.rb +5 -0
- data/lib/erbac/railtie.rb +16 -0
- data/lib/erbac/utils.rb +10 -0
- data/lib/erbac/version.rb +3 -0
- data/lib/erbac.rb +12 -0
- data/lib/generators/active_record/erbac_generator.rb +54 -0
- data/lib/generators/active_record/templates/README +0 -0
- data/lib/generators/active_record/templates/migration.rb +27 -0
- data/lib/generators/erbac/erbac_generator.rb +65 -0
- data/lib/generators/erbac/templates/README +13 -0
- data/lib/generators/erbac/templates/initializer.rb +27 -0
- data/lib/generators/erbac/user_generator.rb +27 -0
- data/spec/erbac/user_instance_spec.rb +160 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/adapters/active_record.rb +33 -0
- data/spec/support/data.rb +43 -0
- data/spec/support/schema.rb +44 -0
- metadata +166 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
erbac (0.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actionmailer (3.2.13)
|
10
|
+
actionpack (= 3.2.13)
|
11
|
+
mail (~> 2.5.3)
|
12
|
+
actionpack (3.2.13)
|
13
|
+
activemodel (= 3.2.13)
|
14
|
+
activesupport (= 3.2.13)
|
15
|
+
builder (~> 3.0.0)
|
16
|
+
erubis (~> 2.7.0)
|
17
|
+
journey (~> 1.0.4)
|
18
|
+
rack (~> 1.4.5)
|
19
|
+
rack-cache (~> 1.2)
|
20
|
+
rack-test (~> 0.6.1)
|
21
|
+
sprockets (~> 2.2.1)
|
22
|
+
activemodel (3.2.13)
|
23
|
+
activesupport (= 3.2.13)
|
24
|
+
builder (~> 3.0.0)
|
25
|
+
activerecord (3.2.13)
|
26
|
+
activemodel (= 3.2.13)
|
27
|
+
activesupport (= 3.2.13)
|
28
|
+
arel (~> 3.0.2)
|
29
|
+
tzinfo (~> 0.3.29)
|
30
|
+
activeresource (3.2.13)
|
31
|
+
activemodel (= 3.2.13)
|
32
|
+
activesupport (= 3.2.13)
|
33
|
+
activesupport (3.2.13)
|
34
|
+
i18n (= 0.6.1)
|
35
|
+
multi_json (~> 1.0)
|
36
|
+
arel (3.0.2)
|
37
|
+
builder (3.0.4)
|
38
|
+
diff-lcs (1.2.4)
|
39
|
+
erubis (2.7.0)
|
40
|
+
hike (1.2.3)
|
41
|
+
i18n (0.6.1)
|
42
|
+
journey (1.0.4)
|
43
|
+
json (1.8.0)
|
44
|
+
mail (2.5.4)
|
45
|
+
mime-types (~> 1.16)
|
46
|
+
treetop (~> 1.4.8)
|
47
|
+
mime-types (1.23)
|
48
|
+
multi_json (1.7.8)
|
49
|
+
polyglot (0.3.3)
|
50
|
+
rack (1.4.5)
|
51
|
+
rack-cache (1.2)
|
52
|
+
rack (>= 0.4)
|
53
|
+
rack-ssl (1.3.3)
|
54
|
+
rack
|
55
|
+
rack-test (0.6.2)
|
56
|
+
rack (>= 1.0)
|
57
|
+
rails (3.2.13)
|
58
|
+
actionmailer (= 3.2.13)
|
59
|
+
actionpack (= 3.2.13)
|
60
|
+
activerecord (= 3.2.13)
|
61
|
+
activeresource (= 3.2.13)
|
62
|
+
activesupport (= 3.2.13)
|
63
|
+
bundler (~> 1.0)
|
64
|
+
railties (= 3.2.13)
|
65
|
+
railties (3.2.13)
|
66
|
+
actionpack (= 3.2.13)
|
67
|
+
activesupport (= 3.2.13)
|
68
|
+
rack-ssl (~> 1.3.2)
|
69
|
+
rake (>= 0.8.7)
|
70
|
+
rdoc (~> 3.4)
|
71
|
+
thor (>= 0.14.6, < 2.0)
|
72
|
+
rake (10.1.0)
|
73
|
+
rdoc (3.12.2)
|
74
|
+
json (~> 1.4)
|
75
|
+
rspec (2.14.1)
|
76
|
+
rspec-core (~> 2.14.0)
|
77
|
+
rspec-expectations (~> 2.14.0)
|
78
|
+
rspec-mocks (~> 2.14.0)
|
79
|
+
rspec-core (2.14.4)
|
80
|
+
rspec-expectations (2.14.0)
|
81
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
82
|
+
rspec-mocks (2.14.2)
|
83
|
+
rspec-rails (2.14.0)
|
84
|
+
actionpack (>= 3.0)
|
85
|
+
activesupport (>= 3.0)
|
86
|
+
railties (>= 3.0)
|
87
|
+
rspec-core (~> 2.14.0)
|
88
|
+
rspec-expectations (~> 2.14.0)
|
89
|
+
rspec-mocks (~> 2.14.0)
|
90
|
+
sprockets (2.2.2)
|
91
|
+
hike (~> 1.2)
|
92
|
+
multi_json (~> 1.0)
|
93
|
+
rack (~> 1.0)
|
94
|
+
tilt (~> 1.1, != 1.3.0)
|
95
|
+
sqlite3 (1.3.7-x86-mingw32)
|
96
|
+
thor (0.18.1)
|
97
|
+
tilt (1.4.1)
|
98
|
+
treetop (1.4.14)
|
99
|
+
polyglot
|
100
|
+
polyglot (>= 0.3.1)
|
101
|
+
tzinfo (0.3.37)
|
102
|
+
|
103
|
+
PLATFORMS
|
104
|
+
x86-mingw32
|
105
|
+
|
106
|
+
DEPENDENCIES
|
107
|
+
activerecord (>= 3.0.0)
|
108
|
+
bundler
|
109
|
+
erbac!
|
110
|
+
rails (= 3.2.13)
|
111
|
+
rake
|
112
|
+
rspec (>= 2.0)
|
113
|
+
rspec-rails (>= 2.0)
|
114
|
+
sqlite3
|
data/Rakefile
ADDED
data/erbac.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "erbac/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'erbac'
|
7
|
+
s.version = Erbac::VERSION.dup
|
8
|
+
s.date = '2013-07-16'
|
9
|
+
s.summary = "Yii rbac transplatation!"
|
10
|
+
s.description = "A simple hello world gem"
|
11
|
+
|
12
|
+
s.authors = ["dx"]
|
13
|
+
s.email = 'bitcheap@gmail.com'
|
14
|
+
s.homepage = 'http://utocity.com'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency "sqlite3"
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'rspec', ">= 2.0"
|
23
|
+
s.add_development_dependency "rspec-rails", ">= 2.0"
|
24
|
+
s.add_development_dependency "bundler"
|
25
|
+
s.add_development_dependency "activerecord", ">= 3.0.0"
|
26
|
+
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
module Erbac
|
2
|
+
module ActiveRecordSupport
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def control_access
|
9
|
+
has_many Erbac.auth_assignment.pluralize.to_sym,
|
10
|
+
dependent: :delete_all,
|
11
|
+
class_name: Erbac.auth_assignment_class,
|
12
|
+
foreign_key: "user_id"
|
13
|
+
|
14
|
+
accepts_nested_attributes_for Erbac.auth_assignment.pluralize.to_sym, allow_destroy: true
|
15
|
+
attr_accessible (Erbac.auth_assignment.pluralize + "_attributes").to_sym
|
16
|
+
|
17
|
+
has_many Erbac.auth_item.pluralize.to_sym,
|
18
|
+
through: Erbac.auth_assignment.pluralize.to_sym,
|
19
|
+
source: Erbac.auth_item.pluralize.to_sym
|
20
|
+
|
21
|
+
define_method :check_access? do |*args|
|
22
|
+
unless (args.first.is_a? Erbac.auth_item_class.constantize)
|
23
|
+
raise TypeError, "Should pass in #{Erbac.auth_item_class} instance as the first parameter. Got #{args.first.to_s}", caller
|
24
|
+
end
|
25
|
+
|
26
|
+
check_access_recursive? args.first, args.extract_options!
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method :execute_biz_rule? do |*args|
|
30
|
+
item = args.first
|
31
|
+
params = args.extract_options!
|
32
|
+
if (item.is_a? Erbac.auth_item_class.constantize or item.is_a? Erbac.auth_assignment_class.constantize)
|
33
|
+
bizrule_sandbox(item.bizrule, params, item.data)
|
34
|
+
else
|
35
|
+
raise TypeError, "Argument type not supported, should pass in #{Erbac.auth_item_class} or #{Erbac.auth_assignment_class} instance as the first parameter. Got #{item.class.to_s}", caller
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
define_method :check_access_recursive? do |item, params={}|
|
40
|
+
if bizrule_sandbox(item.bizrule, params, item.data)
|
41
|
+
return true if Erbac.default_roles.include? item.name
|
42
|
+
assignment = Erbac.auth_assignment_class.constantize.where(user_id: self, item_id: item).first
|
43
|
+
if assignment
|
44
|
+
return true if bizrule_sandbox(item.bizrule, params, item.data)
|
45
|
+
end
|
46
|
+
item.parents.each do |p|
|
47
|
+
return true if check_access_recursive?(p, params)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
define_method :bizrule_sandbox do |bizrule, params={}, data=nil|
|
54
|
+
if (bizrule.nil? or bizrule.empty?)
|
55
|
+
true
|
56
|
+
else
|
57
|
+
proc do
|
58
|
+
# $SAFE = 3 how to change this ?
|
59
|
+
Erbac.strict_check_mode ? (self.instance_eval bizrule) == true : (self.instance_eval bizrule) != false
|
60
|
+
end.call
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
send :protected, :check_access_recursive?, :bizrule_sandbox
|
65
|
+
|
66
|
+
# add an anonymous singleton instance
|
67
|
+
@anonymous = self.new
|
68
|
+
class << self
|
69
|
+
define_method :anonymous do
|
70
|
+
@anonymous
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
define_method :anonymous_should_not_be_saved do
|
75
|
+
raise NoMethodError, "Anonymous object should not be saved!", caller if self.equal? self.class.anonymous
|
76
|
+
end
|
77
|
+
|
78
|
+
before_save :anonymous_should_not_be_saved
|
79
|
+
|
80
|
+
define_method :anonymous? do
|
81
|
+
self.equal? @anonymous
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def control_auth_item
|
87
|
+
attr_accessible :name, :auth_type, :description, :bizrule, :data
|
88
|
+
validates :name, uniqueness: true
|
89
|
+
|
90
|
+
has_many Erbac.auth_assignment.pluralize.to_sym,
|
91
|
+
dependent: :delete_all,
|
92
|
+
class_name: Erbac.auth_assignment_class,
|
93
|
+
foreign_key: "item_id"
|
94
|
+
|
95
|
+
accepts_nested_attributes_for Erbac.auth_assignment.pluralize.to_sym, allow_destroy: true
|
96
|
+
attr_accessible (Erbac.auth_assignment.pluralize + "_attributes").to_sym
|
97
|
+
|
98
|
+
has_many Erbac.user_class.underscore.pluralize.to_sym,
|
99
|
+
through: Erbac.auth_assignment.pluralize.to_sym,
|
100
|
+
source: Erbac.user_class.underscore.pluralize.to_sym
|
101
|
+
|
102
|
+
has_and_belongs_to_many :parents,
|
103
|
+
join_table: Erbac.auth_item_child_table,
|
104
|
+
class_name: Erbac.auth_item_class,
|
105
|
+
foreign_key: "child_id", association_foreign_key: "parent_id"
|
106
|
+
|
107
|
+
has_and_belongs_to_many :children,
|
108
|
+
join_table: Erbac.auth_item_child_table,
|
109
|
+
class_name: Erbac.auth_item_class,
|
110
|
+
foreign_key: "parent_id", association_foreign_key: "child_id"
|
111
|
+
|
112
|
+
const_set("TYPE_OPERATION", 0)
|
113
|
+
const_set("TYPE_TASK", 1)
|
114
|
+
const_set("TYPE_ROLE", 2)
|
115
|
+
|
116
|
+
|
117
|
+
define_method :add_child do |item|
|
118
|
+
if self.name == item.name
|
119
|
+
raise AuthItemRelationError, "Cannot add #{self.name} as a child of itself.", caller
|
120
|
+
end
|
121
|
+
|
122
|
+
check_item_child_type(item)
|
123
|
+
|
124
|
+
if detect_loop?(item)
|
125
|
+
raise AuthItemRelationError, "Cannot add #{child.name} as a child of #{self.name}. A loop has been detected.", caller
|
126
|
+
end
|
127
|
+
|
128
|
+
self.children << item
|
129
|
+
end
|
130
|
+
|
131
|
+
define_method :remove_child do |item|
|
132
|
+
self.children.delete item
|
133
|
+
end
|
134
|
+
|
135
|
+
define_method :has_child? do |item|
|
136
|
+
self.children.include? child
|
137
|
+
end
|
138
|
+
|
139
|
+
define_method :assign do |user, bizrule=nil, data=nil|
|
140
|
+
assignment = Erbac.auth_assignment_class.constantize.where({item_id: self, user_id: user}).first_or_create
|
141
|
+
assignment.bizrule = bizrule
|
142
|
+
assignment.data = data
|
143
|
+
assignment
|
144
|
+
end
|
145
|
+
|
146
|
+
define_method :revoke do |user|
|
147
|
+
Erbac.auth_assignment_class.constantize.delete({item_id: self, user_id: user})
|
148
|
+
end
|
149
|
+
|
150
|
+
define_method :is_assigned? do |user|
|
151
|
+
Erbac.auth_assignment_class.constantize.where({item_id: self, user_id: user}).any?
|
152
|
+
end
|
153
|
+
|
154
|
+
define_method :get_auth_assignment? do |user|
|
155
|
+
Erbac.auth_assignment_class.constantize.where({item_id: self, user_id: user}).first
|
156
|
+
end
|
157
|
+
|
158
|
+
define_method :detect_loop? do |child|
|
159
|
+
return true if (self.name == child.name)
|
160
|
+
child.children.each do |g|
|
161
|
+
return true if self.detect_loop?(g)
|
162
|
+
end
|
163
|
+
false
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
define_method :check_item_child_type do |child|
|
168
|
+
unless (self.is_a? Erbac.auth_item_class.constantize)
|
169
|
+
raise TypeError, "Should pass in #{Erbac.auth_item_class} instance as the first parameter. Got #{self.to_s}", caller
|
170
|
+
end
|
171
|
+
|
172
|
+
unless (child.is_a? Erbac.auth_item_class.constantize)
|
173
|
+
raise TypeError, "Should pass in #{Erbac.auth_item_class} instance as the second parameter. Got #{child.to_s}", caller
|
174
|
+
end
|
175
|
+
|
176
|
+
if self.auth_type < child.auth_type
|
177
|
+
raise Erbac::AuthItemRelationError, "Cannot add an itme of type #{AUTH_TYPES[child.auth_type]} to an item of type #{AUTH_TYPES[self.auth_type]}.", caller
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# should marshal the data when it comes to database
|
182
|
+
# make it transparent with the user
|
183
|
+
define_method :marshal_around_save do |*args, &block|
|
184
|
+
data = self.data
|
185
|
+
self.data = Marshal.dump data
|
186
|
+
block.call # it is a trick, search it!
|
187
|
+
self.data = data
|
188
|
+
end
|
189
|
+
around_save :marshal_around_save
|
190
|
+
|
191
|
+
define_method :marshal_after_find do
|
192
|
+
begin # rescue marshal error and replace with nil
|
193
|
+
self.data = Marshal.restore self.data
|
194
|
+
rescue ArgumentError
|
195
|
+
self.data = nil
|
196
|
+
end
|
197
|
+
end
|
198
|
+
after_find :marshal_after_find
|
199
|
+
|
200
|
+
send :protected, :detect_loop?, :check_item_child_type
|
201
|
+
send :private, :marshal_around_save, :marshal_after_find
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
def control_auth_assignment
|
206
|
+
attr_accessible :item_id, :user_id, :bizrule, :data
|
207
|
+
|
208
|
+
belongs_to Erbac.user_class.underscore.pluralize.to_sym,
|
209
|
+
class_name: Erbac.user_class,
|
210
|
+
foreign_key: "user_id"
|
211
|
+
|
212
|
+
belongs_to Erbac.auth_item.pluralize.to_sym,
|
213
|
+
class_name: Erbac.auth_item_class,
|
214
|
+
foreign_key: "item_id"
|
215
|
+
|
216
|
+
protected
|
217
|
+
|
218
|
+
# should marshal the data when it comes to database
|
219
|
+
# make it transparent with the user
|
220
|
+
define_method :marshal_around_save do |*args, &block|
|
221
|
+
data = self.data
|
222
|
+
self.data = Marshal.dump data
|
223
|
+
block.call # it is a trick, search it!
|
224
|
+
self.data = data
|
225
|
+
end
|
226
|
+
around_save :marshal_around_save
|
227
|
+
|
228
|
+
define_method :marshal_after_find do
|
229
|
+
begin # rescue marshal error and replace with nil
|
230
|
+
self.data = Marshal.restore self.data
|
231
|
+
rescue ArgumentError
|
232
|
+
self.data = nil
|
233
|
+
end
|
234
|
+
end
|
235
|
+
after_find :marshal_after_find
|
236
|
+
|
237
|
+
send :private, :marshal_around_save, :marshal_after_find
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Erbac
|
2
|
+
module AuthManage
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
AUTH_TYPES = ["operation", "task", "role"]
|
9
|
+
|
10
|
+
AUTH_TYPES.each do |type|
|
11
|
+
define_method ("create_" + type) do |name, options={}|
|
12
|
+
options[:name] = name
|
13
|
+
options[:auth_type] = Erbac.auth_item_class.constantize.const_get("TYPE_" + type.upcase)
|
14
|
+
Erbac.auth_item_class.constantize.create options
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method ("get_" + type.pluralize) do |user|
|
18
|
+
user.send Erbac.auth_item.pluralize.to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_access(*args)
|
23
|
+
user = args[1].is_a? Erbac.user_class.constantize ? args[1] : Erbac.user_class.constantize.anonymous
|
24
|
+
user.check_access? args.first, args.extract_options!
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_item_child(parent, child)
|
28
|
+
parent.add_child child
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_item_child(parent, child)
|
32
|
+
parent.remove_child child
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_item_child?(parent, child)
|
36
|
+
parent.has_child? child
|
37
|
+
end
|
38
|
+
|
39
|
+
def assign(item, user, bizrule=nil, data=nil)
|
40
|
+
item.assign user, bizrule, data
|
41
|
+
end
|
42
|
+
|
43
|
+
def revoke(item, user)
|
44
|
+
item.revoke user
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_assigned?(item, user)
|
48
|
+
item.is_assigned? user
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_auth_assignment(item, user)
|
52
|
+
item.get_auth_assignment user
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_all
|
56
|
+
Erbac.auth_item_class.constantize.delete_all
|
57
|
+
end
|
58
|
+
|
59
|
+
def clear_auth_assignments(*args)
|
60
|
+
Erbac.auth_assignment_class.constantize.delete_all args
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_biz_rule?(*args)
|
64
|
+
item = args.first
|
65
|
+
params = args.extract_options!
|
66
|
+
unless (item.is_a? Erbac.auth_item_class.constantize or item.is_a? Erbac.auth_assignment_class.constantize)
|
67
|
+
raise TypeError, "Argument type not supported, should pass in #{Erbac.auth_item_class} or #{Erbac.auth_assignment_class} instance as the first parameter. Got #{item.class.to_s}", caller
|
68
|
+
end
|
69
|
+
|
70
|
+
if (args[1].is_a? Erbac.user_class.constantize)
|
71
|
+
args[1].execute_biz_rule? item, params
|
72
|
+
else
|
73
|
+
bizrule_sandbox(item.bizrule, params, item.data)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_access?(*args)
|
78
|
+
unless (args.first.is_a? Erbac.auth_item_class.constantize)
|
79
|
+
raise TypeError, "Should pass in #{Erbac.auth_item_class} instance as the first parameter. Got #{args.first.to_s}", caller
|
80
|
+
end
|
81
|
+
|
82
|
+
if (args[1].is_a? Erbac.user_class.constantize)
|
83
|
+
args[1].check_access? args.first, args.extract_options!
|
84
|
+
else
|
85
|
+
bizrule_sandbox args.first.bizrule, args.extract_options!, args.first.data
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
def bizrule_sandbox(bizrule, params={}, data=nil)
|
91
|
+
if (bizrule.nil? or bizrule.empty?)
|
92
|
+
true
|
93
|
+
else
|
94
|
+
proc do
|
95
|
+
$SAFE = 4 # here is a military area
|
96
|
+
Erbac.restrict_check_mode ? (eval bizrule) == true : (eval bizrule) != false
|
97
|
+
end.call
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Erbac
|
2
|
+
module Configure
|
3
|
+
attr_accessor :strict_check_mode
|
4
|
+
attr_accessor :default_roles
|
5
|
+
|
6
|
+
USER_CLASS = "User"
|
7
|
+
attr_accessor :user_class
|
8
|
+
|
9
|
+
AUTH_ITEM = "auth_item"
|
10
|
+
AUTH_ITEM_CLASS = AUTH_ITEM.dup.classify
|
11
|
+
AUTH_ITEM_TABLE = AUTH_ITEM.dup.tableize
|
12
|
+
attr_accessor :auth_item, :auth_item_class, :auth_item_table
|
13
|
+
|
14
|
+
AUTH_ITEM_CHILD = "auth_item_child"
|
15
|
+
AUTH_ITEM_CHILD_TABLE = AUTH_ITEM_CHILD.dup.tableize
|
16
|
+
attr_accessor :auth_item_child, :auth_item_child_table
|
17
|
+
|
18
|
+
AUTH_ASSIGNMENT = "auth_assignment"
|
19
|
+
AUTH_ASSIGNMENT_CLASS = AUTH_ASSIGNMENT.dup.classify
|
20
|
+
AUTH_ASSIGNMENT_TABLE = AUTH_ASSIGNMENT.dup.tableize
|
21
|
+
attr_accessor :auth_assignment, :auth_assignment_class, :auth_assignment_table
|
22
|
+
|
23
|
+
def configure
|
24
|
+
yield self if block_given?
|
25
|
+
|
26
|
+
self.strict_check_mode ||= true
|
27
|
+
self.default_roles ||= []
|
28
|
+
|
29
|
+
self.user_class ||= USER_CLASS
|
30
|
+
|
31
|
+
self.auth_item ||= AUTH_ITEM
|
32
|
+
self.auth_item_class ||= self.auth_item.classify
|
33
|
+
self.auth_item_table ||= self.auth_item.tableize
|
34
|
+
|
35
|
+
self.auth_item_child ||= AUTH_ITEM_CHILD
|
36
|
+
self.auth_item_child_table ||= self.auth_item_child.tableize
|
37
|
+
|
38
|
+
self.auth_assignment ||= AUTH_ASSIGNMENT
|
39
|
+
self.auth_assignment_class ||= self.auth_assignment.classify
|
40
|
+
self.auth_assignment_table ||= self.auth_assignment.tableize
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/erbac/errors.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'erbac'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module Erbac
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
initializer 'erbac.initialize' do
|
7
|
+
ActiveSupport.on_load(:active_record) do
|
8
|
+
ActiveRecord::Base.send :include, Erbac::ActiveRecordSupport
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveSupport.on_load(:action_controller) do
|
12
|
+
ActionController::Base.send :include, Erbac::ActionControllerSupport
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/erbac/utils.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Erbac
|
2
|
+
module Utils
|
3
|
+
def deprecate(old_method, new_method)
|
4
|
+
define_method(old_method) do |*args|
|
5
|
+
warn "[DEPRECATION] #{caller.first}: `#{old_method}` is deprecated. Please use `#{new_method}` instead."
|
6
|
+
send(new_method, *args)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/erbac.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'erbac/railtie' if defined?(Rails)
|
2
|
+
require 'erbac/configure'
|
3
|
+
require 'erbac/auth_manage'
|
4
|
+
require 'erbac/errors'
|
5
|
+
require 'erbac/active_record_support'
|
6
|
+
require 'erbac/action_controller_support'
|
7
|
+
|
8
|
+
module Erbac
|
9
|
+
include AuthManage
|
10
|
+
include Error
|
11
|
+
extend Configure
|
12
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'erbac/configure'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Generators
|
7
|
+
class ErbacGenerator < ActiveRecord::Generators::Base
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
class_option :auth_item, type: :array, aliases: "-i",
|
11
|
+
default: [Erbac::Configure::AUTH_ITEM, Erbac::Configure::AUTH_ITEM_CLASS, Erbac::Configure::AUTH_ITEM_TABLE]
|
12
|
+
class_option :auth_item_child, type: :array, aliases: "-c",
|
13
|
+
default: [Erbac::Configure::AUTH_ITEM_CHILD, Erbac::Configure::AUTH_ITEM_CHILD_TABLE]
|
14
|
+
class_option :auth_assignment, type: :array, aliases: "-a",
|
15
|
+
default: [Erbac::Configure::AUTH_ASSIGNMENT, Erbac::Configure::AUTH_ASSIGNMENT_CLASS, Erbac::Configure::AUTH_ASSIGNMENT_TABLE]
|
16
|
+
|
17
|
+
def init_erbac_options
|
18
|
+
# assert options.auth_item[0]
|
19
|
+
options.auth_item[1] ||= options.auth_item[0].classify || Erbac::Configure::AUTH_ITEM_CLASS
|
20
|
+
options.auth_item[2] ||= options.auth_item[0].tableize || Erbac::Configure::AUTH_ITEM_TABLE
|
21
|
+
|
22
|
+
# assert options.auth_item_child[0]
|
23
|
+
options.auth_item_child[1] || options.auth_item_child[0].tableize || Erbac::Configure::AUTH_ITEM_CHILD_TABLE
|
24
|
+
|
25
|
+
# assert options.auth_assignment[0]
|
26
|
+
options.auth_assignment[1] || options.auth_assignment[0].classify || Erbac::Configure::AUTH_ASSIGNMENT_CLASS
|
27
|
+
options.auth_assignment[2] || options.auth_assignment[0].tableize || Erbac::Configure::AUTH_ASSIGNMENT_TABLE
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_auth_item_model
|
31
|
+
Rails::Generators.invoke("active_record:model", [options.auth_item[0], "--no-migration"], behavior: behavior)
|
32
|
+
Rails::Generators.invoke("active_record:model", [options.auth_assignment[0], "--no-migration"], behavior: behavior)
|
33
|
+
end
|
34
|
+
|
35
|
+
def inject_auth_item_model
|
36
|
+
if behavior == :invoke
|
37
|
+
inject_into_class(model_path(options.auth_item[0]), options.auth_item[1], " control_auth_item\n")
|
38
|
+
inject_into_class(model_path(options.auth_assignment[0]), options.auth_assignment[1], " control_auth_assignment\n")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy_erbac_migration
|
43
|
+
migration_template "migration.rb", "db/migrate/erbac_create_auth"
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def model_path(filename)
|
49
|
+
File.join("app", "models", "#{filename}.rb")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ErbacCreateAuth < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table(:<%= options.auth_item[2] %>) do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.integer :auth_type
|
6
|
+
t.text :description
|
7
|
+
t.text :bizrule
|
8
|
+
t.text :data
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table(:<%= options.auth_item_child[1] %>, id: false) do |t|
|
12
|
+
t.integer :parent_id, null: false
|
13
|
+
t.integer :child_id, null: false
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table(:<%= options.auth_assignment[2] %>) do |t|
|
17
|
+
t.integer :item_id, null: false
|
18
|
+
t.integer :user_id, null: false
|
19
|
+
t.text :bizrule
|
20
|
+
t.text :data
|
21
|
+
end
|
22
|
+
|
23
|
+
add_index(:<%= options.auth_item[2] %>, :name, unique: true)
|
24
|
+
add_index(:<%= options.auth_item_child[1] %>, [:parent_id, :child_id], unique: true)
|
25
|
+
add_index(:<%= options.auth_assignment[2] %>, [:item_id, :user_id], unique: true)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'erbac/configure'
|
2
|
+
|
3
|
+
module Erbac
|
4
|
+
module Generators
|
5
|
+
class ErbacGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
argument :user_class, type: :string, banner: "User", desc: "User class for the system, maybe User, Account or Admin etc. in your case."
|
8
|
+
|
9
|
+
class_option :auth_item, type: :array,
|
10
|
+
default: [Configure::AUTH_ITEM, Configure::AUTH_ITEM_CLASS, Configure::AUTH_ITEM_TABLE],
|
11
|
+
banner: "#{Configure::AUTH_ITEM} #{Configure::AUTH_ITEM_CLASS} #{Configure::AUTH_ITEM_TABLE}",
|
12
|
+
aliases: "-i", desc: "auth item, model and table"
|
13
|
+
class_option :auth_item_child, type: :array,
|
14
|
+
default: [Configure::AUTH_ITEM_CHILD, Configure::AUTH_ITEM_CHILD_TABLE],
|
15
|
+
banner: "#{Configure::AUTH_ITEM_CHILD} #{Configure::AUTH_ITEM_CHILD_TABLE}",
|
16
|
+
aliases: "-c", desc: "auth item child and the join table"
|
17
|
+
class_option :auth_assignment, type: :array,
|
18
|
+
default: [Configure::AUTH_ASSIGNMENT, Configure::AUTH_ASSIGNMENT_CLASS, Configure::AUTH_ASSIGNMENT_TABLE],
|
19
|
+
banner: "#{Configure::AUTH_ASSIGNMENT} #{Configure::AUTH_ASSIGNMENT_CLASS} #{Configure::AUTH_ASSIGNMENT_TABLE}",
|
20
|
+
aliases: "-a", desc: "auth assignment and the join table"
|
21
|
+
|
22
|
+
hook_for :orm
|
23
|
+
namespace :erbac
|
24
|
+
|
25
|
+
desc "Generates RBAC(role base access control) models and migration files according to the USER"
|
26
|
+
# def show_parameters
|
27
|
+
# puts "user: " + self.user
|
28
|
+
# puts "auth_item: " + self.options[:auth_item].inspect
|
29
|
+
# puts "auth_item_child: " + self.options[:auth_item_child].inspect
|
30
|
+
# puts "auth_assignment: " + self.options[:auth_assignment].inspect
|
31
|
+
# puts "options: " + options.inspect
|
32
|
+
# end
|
33
|
+
|
34
|
+
def init_erbac_options
|
35
|
+
# TODO: check the validity of the name
|
36
|
+
|
37
|
+
# assert options.auth_item[0]
|
38
|
+
options.auth_item[1] ||= options.auth_item[0].classify || Configure::AUTH_ITEM_CLASS
|
39
|
+
options.auth_item[2] ||= options.auth_item[0].tableize || Configure::AUTH_ITEM_TABLE
|
40
|
+
|
41
|
+
# assert options.auth_item_child[0]
|
42
|
+
options.auth_item_child[1] || options.auth_item_child[0].tableize || Configure::AUTH_ITEM_CHILD_TABLE
|
43
|
+
|
44
|
+
# assert options.auth_assignment[0]
|
45
|
+
options.auth_assignment[1] || options.auth_assignment[0].classify || Configure::AUTH_ASSIGNMENT_CLASS
|
46
|
+
options.auth_assignment[2] || options.auth_assignment[0].tableize || Configure::AUTH_ASSIGNMENT_TABLE
|
47
|
+
end
|
48
|
+
|
49
|
+
def copy_initializer_file
|
50
|
+
template "initializer.rb", "config/initializers/erbac.rb"
|
51
|
+
end
|
52
|
+
|
53
|
+
def inject_user_class
|
54
|
+
invoke "erbac:user", [ self.user_class ]
|
55
|
+
end
|
56
|
+
|
57
|
+
def show_readme
|
58
|
+
if behavior == :invoke
|
59
|
+
readme "README"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
===============================================================================
|
2
|
+
|
3
|
+
An initializer file has been created here: config/initializers/erbac.rb, you
|
4
|
+
can change erbac settings to match your needs.
|
5
|
+
Defaults values are commented out.
|
6
|
+
|
7
|
+
A Role class has been created in app/models (with the name you gave as
|
8
|
+
argument otherwise the default is auth_item.rb), you can add your own business logic
|
9
|
+
inside.
|
10
|
+
|
11
|
+
Inside your User class (or the name you gave as argument otherwise the default
|
12
|
+
is user.rb), erbac method has been inserted to provide erbac methods.
|
13
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Erbac.configure do |config|
|
2
|
+
# For the time being, maybe in the long term, it only support ActiveRecord...
|
3
|
+
|
4
|
+
# Uncomment the item to override the default
|
5
|
+
|
6
|
+
# if this is set to true, bizrule should return true restrictly
|
7
|
+
# else bizrule will pass if they don't return false
|
8
|
+
# NB in ruby 0 != false and 1 != true
|
9
|
+
# config.strict_check_mode = true
|
10
|
+
|
11
|
+
# check these roles first
|
12
|
+
# config.default_roles = []
|
13
|
+
|
14
|
+
# models and database tables
|
15
|
+
<%= "# " if user_class == Erbac::Configure::USER_CLASS %>config.user_class = <%= %Q("#{user_class}") %>
|
16
|
+
|
17
|
+
<%= "# " if options.auth_item[0] == Erbac::Configure::AUTH_ITEM %>config.auth_item = <%= %Q("#{options.auth_item[0]}") %>
|
18
|
+
<%= "# " if options.auth_item[1] == Erbac::Configure::AUTH_ITEM_CLASS %>config.auth_item_class = <%= %Q("#{options.auth_item[1]}") %>
|
19
|
+
<%= "# " if options.auth_item[2] == Erbac::Configure::AUTH_ITEM_TABLE %>config.auth_item_table = <%= %Q("#{options.auth_item[2]}") %>
|
20
|
+
|
21
|
+
<%= "# " if options.auth_item_child[0] == Erbac::Configure::AUTH_ITEM_CHILD %>config.auth_item_child = <%= %Q("#{options.auth_item_child[0]}") %>
|
22
|
+
<%= "# " if options.auth_item_child[1] == Erbac::Configure::AUTH_ITEM_CHILD_TABLE %>config.auth_item_child_table = <%= %Q("#{options.auth_item_child[1]}") %>
|
23
|
+
|
24
|
+
<%= "# " if options.auth_assignment[0] == Erbac::Configure::AUTH_ASSIGNMENT %>config.auth_assignment = <%= %Q("#{options.auth_assignment[0]}") %>
|
25
|
+
<%= "# " if options.auth_assignment[1] == Erbac::Configure::AUTH_ASSIGNMENT_CLASS %>config.auth_assignment = <%= %Q("#{options.auth_assignment[1]}") %>
|
26
|
+
<%= "# " if options.auth_assignment[2] == Erbac::Configure::AUTH_ASSIGNMENT_TABLE %>config.auth_assignment_table = <%= %Q("#{options.auth_assignment[2]}") %>
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
module Erbac
|
5
|
+
module Generators
|
6
|
+
class UserGenerator < Rails::Generators::Base
|
7
|
+
argument :user_class, type: :string, default: "User"
|
8
|
+
|
9
|
+
desc "Inject erbac method in the User|Account|Admin|* class."
|
10
|
+
|
11
|
+
def inject_user_content
|
12
|
+
inject_into_file(model_path, " control_access\n", :after => inject_erbac_method)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def inject_erbac_method
|
18
|
+
/class[ |\t]+#{user_class}\n|class[ |\t]+#{user_class}.*\n|class[ |\t]+#{user_class.demodulize}\n|class[ |\t]+#{user_class.demodulize}.*\n/
|
19
|
+
end
|
20
|
+
|
21
|
+
def model_path
|
22
|
+
File.join("app", "models", "#{self.user_class.underscore}.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Erbac do
|
4
|
+
before(:all) do
|
5
|
+
@authorA = User.where(name: "authorA").first
|
6
|
+
@authorB = User.where(name: "authorB").first
|
7
|
+
@editor = User.where(name: "editor").first
|
8
|
+
@admin = User.where(name: "administrator").first
|
9
|
+
|
10
|
+
@first_post = Post.create! header: "first post", author_id: @authorA.id
|
11
|
+
@second_post = Post.create! header: "second post", author_id: @authorB.id
|
12
|
+
|
13
|
+
@role_reader = AuthItem.where(name: "reader").first
|
14
|
+
@role_author = AuthItem.where(name: "author").first
|
15
|
+
@role_editor = AuthItem.where(name: "editor").first
|
16
|
+
@role_admin = AuthItem.where(name: "admin").first
|
17
|
+
|
18
|
+
@task_update_own_post = AuthItem.where(name: "updateOwnPost").first
|
19
|
+
|
20
|
+
@oper_update_post = AuthItem.where(name: "updatePost").first
|
21
|
+
@oper_delete_post = AuthItem.where(name: "deletePost").first
|
22
|
+
end
|
23
|
+
|
24
|
+
describe User do
|
25
|
+
subject {nil}
|
26
|
+
|
27
|
+
it "can not read" do
|
28
|
+
Erbac.check_access?(@role_reader, subject).should be_false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe User do
|
33
|
+
subject { User.where(name: "visitor").first }
|
34
|
+
|
35
|
+
it "can not read" do
|
36
|
+
subject.check_access?(@role_reader).should be_false
|
37
|
+
Erbac.check_access?(@role_reader, subject).should be_false
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can not edit as author" do
|
41
|
+
subject.check_access?(@role_author).should be_false
|
42
|
+
Erbac.check_access?(@role_author, subject).should be_false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "can not edit as editor" do
|
46
|
+
subject.check_access?(@role_editor).should be_false
|
47
|
+
Erbac.check_access?(@role_editor, subject).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can not delete" do
|
51
|
+
subject.check_access?(@oper_delete_post).should be_false
|
52
|
+
Erbac.check_access?(@oper_delete_post, subject).should be_false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
describe User do
|
58
|
+
subject { User.where(name: "reader").first }
|
59
|
+
|
60
|
+
it "can read" do
|
61
|
+
subject.check_access?(@role_reader).should be_true
|
62
|
+
Erbac.check_access?(@role_reader, subject).should be_true
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can not edit as author" do
|
66
|
+
subject.check_access?(@role_author).should be_false
|
67
|
+
Erbac.check_access?(@role_author, subject).should be_false
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can not edit as editor" do
|
71
|
+
subject.check_access?(@role_editor).should be_false
|
72
|
+
Erbac.check_access?(@role_editor, subject).should be_false
|
73
|
+
end
|
74
|
+
|
75
|
+
it "can not delete" do
|
76
|
+
subject.check_access?(@oper_delete_post).should be_false
|
77
|
+
Erbac.check_access?(@oper_delete_post, subject).should be_false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe User do
|
82
|
+
subject { User.where(name: "editor").first }
|
83
|
+
|
84
|
+
it "can read" do
|
85
|
+
subject.check_access?(@role_reader).should be_true
|
86
|
+
Erbac.check_access?(@role_reader, subject).should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "can not edit as author" do
|
90
|
+
subject.check_access?(@role_author).should be_false
|
91
|
+
Erbac.check_access?(@role_author, subject).should be_false
|
92
|
+
subject.check_access?(@task_update_own_post, post: @first_post).should be_false
|
93
|
+
Erbac.check_access?(@task_update_own_post, subject, post: @first_post).should be_false
|
94
|
+
end
|
95
|
+
|
96
|
+
it "can edit as editor" do
|
97
|
+
subject.check_access?(@role_editor).should be_true
|
98
|
+
Erbac.check_access?(@role_editor, subject).should be_false
|
99
|
+
subject.check_access?(@oper_update_post, post: @first_post).should be_true
|
100
|
+
Erbac.check_access?(@oper_update_post, subject, post: @first_post).should be_false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can not delete" do
|
104
|
+
subject.check_access?(@oper_delete_post).should be_false
|
105
|
+
Erbac.check_access?(@oper_delete_post, subject).should be_false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe User do
|
110
|
+
subject { User.where(name: "authorA").first }
|
111
|
+
|
112
|
+
it "can read" do
|
113
|
+
subject.check_access?(@role_reader).should be_true
|
114
|
+
end
|
115
|
+
|
116
|
+
it "can edit self post as task" do
|
117
|
+
subject.check_access?(@task_update_own_post, post: @first_post).should be_true
|
118
|
+
subject.check_access?(@task_update_own_post, post: @second_post).should be_false
|
119
|
+
end
|
120
|
+
|
121
|
+
it "can edit self post as update operation" do
|
122
|
+
subject.check_access?(@oper_update_post, post: @first_post).should be_true
|
123
|
+
subject.check_access?(@oper_update_post, post: @second_post).should be_false
|
124
|
+
end
|
125
|
+
|
126
|
+
it "can not edit as editor" do
|
127
|
+
subject.check_access?(@role_editor).should be_false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "can not delete" do
|
131
|
+
subject.check_access?(@role_admin).should be_false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe User do
|
136
|
+
subject { User.where(name: "administrator").first }
|
137
|
+
|
138
|
+
it "can read" do
|
139
|
+
subject.check_access?(@role_reader).should be_true
|
140
|
+
end
|
141
|
+
|
142
|
+
it "can edit self post as task" do
|
143
|
+
subject.check_access?(@task_update_own_post, post: @first_post).should be_false
|
144
|
+
subject.check_access?(@task_update_own_post, post: @second_post).should be_false
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can edit self post as update operation" do
|
148
|
+
subject.check_access?(@oper_update_post, post: @first_post).should be_true
|
149
|
+
subject.check_access?(@oper_update_post, post: @second_post).should be_true
|
150
|
+
end
|
151
|
+
|
152
|
+
it "can not edit as editor" do
|
153
|
+
subject.check_access?(@role_editor).should be_true
|
154
|
+
end
|
155
|
+
|
156
|
+
it "can not delete" do
|
157
|
+
subject.check_access?(@oper_delete_post).should be_true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
RSpec::Matchers::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::BuiltIn::MatchArray)
|
4
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
5
|
+
ActiveRecord::Base.send :include, Erbac::ActiveRecordSupport
|
6
|
+
|
7
|
+
load File.dirname(__FILE__) + '/../schema.rb'
|
8
|
+
|
9
|
+
# ActiveRecord models
|
10
|
+
class User < ActiveRecord::Base
|
11
|
+
attr_accessible :name
|
12
|
+
control_access
|
13
|
+
|
14
|
+
has_many :my_posts, class_name: "Post", inverse_of: :author
|
15
|
+
has_many :co_posts, class_name: "Post", inverse_of: :co_workers
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class AuthItem < ActiveRecord::Base
|
20
|
+
attr_accessible :name, :auth_type, :description, :bizrule, :data
|
21
|
+
control_auth_item
|
22
|
+
end
|
23
|
+
|
24
|
+
class AuthAssignment < ActiveRecord::Base
|
25
|
+
attr_accessible :item_id, :user_id, :bizrule, :data
|
26
|
+
control_auth_assignment
|
27
|
+
end
|
28
|
+
|
29
|
+
class Post < ActiveRecord::Base
|
30
|
+
attr_accessible :header, :author_id
|
31
|
+
belongs_to :author, class_name: "User", inverse_of: :my_posts
|
32
|
+
belongs_to :co_workers, class_name: "User", inverse_of: :co_posts
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
User.destroy_all
|
2
|
+
AuthItem.destroy_all
|
3
|
+
|
4
|
+
# users
|
5
|
+
visitor = User.create! name: "visitor"
|
6
|
+
reader = User.create! name: "reader"
|
7
|
+
authorA = User.create! name: "authorA"
|
8
|
+
authorB = User.create! name: "authorB"
|
9
|
+
editor = User.create! name: "editor"
|
10
|
+
admin = User.create! name: "administrator"
|
11
|
+
|
12
|
+
oper_create_post = Erbac.create_operation "createPost", description: "create a post"
|
13
|
+
oper_read_post = Erbac.create_operation 'readPost', description: 'read a post'
|
14
|
+
oper_update_post = Erbac.create_operation 'updatePost', description: 'update a post'
|
15
|
+
oper_delete_post = Erbac.create_operation 'deletePost', description: 'delete a post'
|
16
|
+
|
17
|
+
task_update_own_post = Erbac.create_task "updateOwnPost", description: "update a post by author himself", bizrule: "self == params[:post].author"
|
18
|
+
task_update_own_post.add_child oper_update_post
|
19
|
+
|
20
|
+
role_reader = Erbac.create_role "reader"
|
21
|
+
role_reader.add_child oper_read_post
|
22
|
+
|
23
|
+
role_author = Erbac.create_role "author"
|
24
|
+
role_author.add_child role_reader
|
25
|
+
role_author.add_child oper_create_post
|
26
|
+
role_author.add_child task_update_own_post
|
27
|
+
|
28
|
+
role_editor = Erbac.create_role "editor"
|
29
|
+
role_editor.add_child role_reader
|
30
|
+
role_editor.add_child oper_update_post
|
31
|
+
|
32
|
+
role_admin = Erbac.create_role "admin"
|
33
|
+
role_admin.add_child role_editor
|
34
|
+
role_admin.add_child role_author
|
35
|
+
role_admin.add_child oper_delete_post
|
36
|
+
|
37
|
+
Erbac.assign role_reader, reader
|
38
|
+
Erbac.assign role_author, authorA
|
39
|
+
Erbac.assign role_author, authorB
|
40
|
+
Erbac.assign role_editor, editor
|
41
|
+
Erbac.assign role_admin, admin
|
42
|
+
|
43
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
self.verbose = false
|
3
|
+
|
4
|
+
create_table(:users) do |t|
|
5
|
+
t.string :name, null: false
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table(:auth_items) do |t|
|
9
|
+
t.string :name, null: false
|
10
|
+
t.integer :auth_type
|
11
|
+
t.text :description
|
12
|
+
t.text :bizrule
|
13
|
+
t.text :data
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table(:auth_item_children, id: false) do |t|
|
17
|
+
t.integer :parent_id, null: false
|
18
|
+
t.integer :child_id, null: false
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table(:auth_assignments) do |t|
|
22
|
+
t.integer :item_id, null: false
|
23
|
+
t.integer :user_id, null: false
|
24
|
+
t.text :bizrule
|
25
|
+
t.text :data
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table(:posts) do |t|
|
29
|
+
t.string :header
|
30
|
+
t.integer :author_id
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table(:posts_users, id: false) do |t|
|
34
|
+
t.integer :user_id, null: false
|
35
|
+
t.integer :post_id, null: false
|
36
|
+
end
|
37
|
+
|
38
|
+
add_index(:auth_items, :name, unique: true)
|
39
|
+
add_index(:auth_item_children, [:parent_id, :child_id], unique: true)
|
40
|
+
add_index(:auth_assignments, [:item_id, :user_id], unique: true)
|
41
|
+
add_index(:posts, :author_id)
|
42
|
+
add_index(:posts_users, [:user_id, :post_id], unique: true)
|
43
|
+
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: erbac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- dx
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sqlite3
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec-rails
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: activerecord
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 3.0.0
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 3.0.0
|
110
|
+
description: A simple hello world gem
|
111
|
+
email: bitcheap@gmail.com
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files: []
|
115
|
+
files:
|
116
|
+
- .gitignore
|
117
|
+
- Gemfile
|
118
|
+
- Gemfile.lock
|
119
|
+
- Rakefile
|
120
|
+
- erbac.gemspec
|
121
|
+
- lib/erbac.rb
|
122
|
+
- lib/erbac/action_controller_support.rb
|
123
|
+
- lib/erbac/active_record_support.rb
|
124
|
+
- lib/erbac/auth_manage.rb
|
125
|
+
- lib/erbac/configure.rb
|
126
|
+
- lib/erbac/errors.rb
|
127
|
+
- lib/erbac/railtie.rb
|
128
|
+
- lib/erbac/utils.rb
|
129
|
+
- lib/erbac/version.rb
|
130
|
+
- lib/generators/active_record/erbac_generator.rb
|
131
|
+
- lib/generators/active_record/templates/README
|
132
|
+
- lib/generators/active_record/templates/migration.rb
|
133
|
+
- lib/generators/erbac/erbac_generator.rb
|
134
|
+
- lib/generators/erbac/templates/README
|
135
|
+
- lib/generators/erbac/templates/initializer.rb
|
136
|
+
- lib/generators/erbac/user_generator.rb
|
137
|
+
- spec/erbac/user_instance_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
139
|
+
- spec/support/adapters/active_record.rb
|
140
|
+
- spec/support/data.rb
|
141
|
+
- spec/support/schema.rb
|
142
|
+
homepage: http://utocity.com
|
143
|
+
licenses: []
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
requirements: []
|
161
|
+
rubyforge_project:
|
162
|
+
rubygems_version: 1.8.24
|
163
|
+
signing_key:
|
164
|
+
specification_version: 3
|
165
|
+
summary: Yii rbac transplatation!
|
166
|
+
test_files: []
|