stffn-declarative_authorization 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +2 -0
- data/Rakefile +8 -0
- data/app/controllers/authorization_rules_controller.rb +103 -0
- data/app/controllers/authorization_usages_controller.rb +19 -0
- data/app/helpers/authorization_rules_helper.rb +84 -0
- data/app/views/authorization_rules/graph.dot.erb +49 -0
- data/app/views/authorization_rules/graph.html.erb +39 -0
- data/app/views/authorization_rules/index.html.erb +15 -0
- data/app/views/authorization_usages/index.html.erb +45 -0
- data/config/routes.rb +6 -0
- data/lib/authorization.rb +514 -0
- data/lib/helper.rb +51 -0
- data/lib/in_controller.rb +311 -0
- data/lib/in_model.rb +130 -0
- data/lib/maintenance.rb +174 -0
- data/lib/obligation_scope.rb +281 -0
- data/lib/rails_legacy.rb +14 -0
- data/lib/reader.rb +391 -0
- data/test/authorization_test.rb +576 -0
- data/test/controller_test.rb +361 -0
- data/test/dsl_reader_test.rb +157 -0
- data/test/helper_test.rb +96 -0
- data/test/maintenance_test.rb +15 -0
- data/test/model_test.rb +794 -0
- data/test/schema.sql +32 -0
- data/test/test_helper.rb +99 -0
- metadata +26 -2
@@ -0,0 +1,361 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
|
3
|
+
|
4
|
+
class LoadMockObject < MockDataObject
|
5
|
+
def self.find(*args)
|
6
|
+
new :id => args[0]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
##################
|
12
|
+
class SpecificMocksController < MocksController
|
13
|
+
filter_access_to :test_action, :require => :test, :context => :permissions
|
14
|
+
filter_access_to :test_action_2, :require => :test, :context => :permissions_2
|
15
|
+
filter_access_to :show
|
16
|
+
filter_access_to :edit, :create, :require => :test, :context => :permissions
|
17
|
+
filter_access_to :edit_2, :require => :test, :context => :permissions,
|
18
|
+
:attribute_check => true, :model => LoadMockObject
|
19
|
+
filter_access_to :new, :require => :test, :context => :permissions
|
20
|
+
|
21
|
+
filter_access_to [:action_group_action_1, :action_group_action_2]
|
22
|
+
define_action_methods :test_action, :test_action_2, :show, :edit, :create,
|
23
|
+
:edit_2, :new, :unprotected_action, :action_group_action_1, :action_group_action_2
|
24
|
+
end
|
25
|
+
|
26
|
+
class BasicControllerTest < ActionController::TestCase
|
27
|
+
tests SpecificMocksController
|
28
|
+
|
29
|
+
|
30
|
+
def test_filter_access_to_receiving_an_explicit_array
|
31
|
+
reader = Authorization::Reader::DSLReader.new
|
32
|
+
|
33
|
+
reader.parse %{
|
34
|
+
authorization do
|
35
|
+
role :test_action_group_2 do
|
36
|
+
has_permission_on :specific_mocks, :to => :action_group_action_2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
request!(MockUser.new(:test_action_group_2), "action_group_action_2", reader)
|
42
|
+
assert @controller.authorized?
|
43
|
+
request!(MockUser.new(:test_action_group_2), "action_group_action_1", reader)
|
44
|
+
assert !@controller.authorized?
|
45
|
+
request!(nil, "action_group_action_2", reader)
|
46
|
+
assert !@controller.authorized?
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_filter_access
|
50
|
+
assert !@controller.class.before_filters.empty?
|
51
|
+
|
52
|
+
reader = Authorization::Reader::DSLReader.new
|
53
|
+
reader.parse %{
|
54
|
+
authorization do
|
55
|
+
role :test_role do
|
56
|
+
has_permission_on :permissions, :to => :test
|
57
|
+
has_permission_on :specific_mocks, :to => :show
|
58
|
+
end
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
request!(MockUser.new(:test_role), "test_action", reader)
|
63
|
+
assert @controller.authorized?
|
64
|
+
|
65
|
+
request!(MockUser.new(:test_role), "test_action_2", reader)
|
66
|
+
assert !@controller.authorized?
|
67
|
+
|
68
|
+
request!(MockUser.new(:test_role_2), "test_action", reader)
|
69
|
+
assert_response :forbidden
|
70
|
+
assert !@controller.authorized?
|
71
|
+
|
72
|
+
request!(MockUser.new(:test_role), "show", reader)
|
73
|
+
assert @controller.authorized?
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_filter_access_multi_actions
|
77
|
+
reader = Authorization::Reader::DSLReader.new
|
78
|
+
reader.parse %{
|
79
|
+
authorization do
|
80
|
+
role :test_role do
|
81
|
+
has_permission_on :permissions, :to => :test
|
82
|
+
end
|
83
|
+
end
|
84
|
+
}
|
85
|
+
request!(MockUser.new(:test_role), "create", reader)
|
86
|
+
assert @controller.authorized?
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_filter_access_unprotected_actions
|
90
|
+
reader = Authorization::Reader::DSLReader.new
|
91
|
+
reader.parse %{
|
92
|
+
authorization do
|
93
|
+
role :test_role do
|
94
|
+
end
|
95
|
+
end
|
96
|
+
}
|
97
|
+
request!(MockUser.new(:test_role), "unprotected_action", reader)
|
98
|
+
assert @controller.authorized?
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_filter_access_priv_hierarchy
|
102
|
+
reader = Authorization::Reader::DSLReader.new
|
103
|
+
reader.parse %{
|
104
|
+
privileges do
|
105
|
+
privilege :read do
|
106
|
+
includes :list, :show
|
107
|
+
end
|
108
|
+
end
|
109
|
+
authorization do
|
110
|
+
role :test_role do
|
111
|
+
has_permission_on :specific_mocks, :to => :read
|
112
|
+
end
|
113
|
+
end
|
114
|
+
}
|
115
|
+
request!(MockUser.new(:test_role), "show", reader)
|
116
|
+
assert @controller.authorized?
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_filter_access_skip_attribute_test
|
120
|
+
reader = Authorization::Reader::DSLReader.new
|
121
|
+
reader.parse %{
|
122
|
+
authorization do
|
123
|
+
role :test_role do
|
124
|
+
has_permission_on :permissions, :to => :test do
|
125
|
+
if_attribute :id => is { user }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
}
|
130
|
+
request!(MockUser.new(:test_role), "new", reader)
|
131
|
+
assert @controller.authorized?
|
132
|
+
|
133
|
+
request!(MockUser.new(:test_role), "edit_2", reader)
|
134
|
+
assert !@controller.authorized?
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_existing_instance_var_remains_unchanged
|
138
|
+
reader = Authorization::Reader::DSLReader.new
|
139
|
+
reader.parse %{
|
140
|
+
authorization do
|
141
|
+
role :test_role do
|
142
|
+
has_permission_on :permissions, :to => :test do
|
143
|
+
if_attribute :id => is { 5 }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}
|
148
|
+
mock_object = MockDataObject.new(:id => 5)
|
149
|
+
@controller.send(:instance_variable_set, :"@load_mock_object",
|
150
|
+
mock_object)
|
151
|
+
request!(MockUser.new(:test_role), "edit_2", reader)
|
152
|
+
assert_equal mock_object,
|
153
|
+
@controller.send(:instance_variable_get, :"@load_mock_object")
|
154
|
+
assert @controller.authorized?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
##################
|
160
|
+
class AllMocksController < MocksController
|
161
|
+
filter_access_to :all
|
162
|
+
filter_access_to :view, :require => :test, :context => :permissions
|
163
|
+
define_action_methods :show, :view
|
164
|
+
end
|
165
|
+
class AllActionsControllerTest < ActionController::TestCase
|
166
|
+
tests AllMocksController
|
167
|
+
def test_filter_access_all
|
168
|
+
reader = Authorization::Reader::DSLReader.new
|
169
|
+
reader.parse %{
|
170
|
+
authorization do
|
171
|
+
role :test_role do
|
172
|
+
has_permission_on :permissions, :to => :test
|
173
|
+
has_permission_on :all_mocks, :to => :show
|
174
|
+
end
|
175
|
+
end
|
176
|
+
}
|
177
|
+
|
178
|
+
request!(MockUser.new(:test_role), "show", reader)
|
179
|
+
assert @controller.authorized?
|
180
|
+
|
181
|
+
request!(MockUser.new(:test_role), "view", reader)
|
182
|
+
assert @controller.authorized?
|
183
|
+
|
184
|
+
request!(MockUser.new(:test_role_2), "show", reader)
|
185
|
+
assert !@controller.authorized?
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
##################
|
191
|
+
class LoadMockObjectsController < MocksController
|
192
|
+
filter_access_to :show, :attribute_check => true, :model => LoadMockObject
|
193
|
+
filter_access_to :edit, :attribute_check => true
|
194
|
+
filter_access_to :update, :delete, :attribute_check => true,
|
195
|
+
:load_method => lambda {MockDataObject.new(:test => 1)}
|
196
|
+
filter_access_to :create do
|
197
|
+
permitted_to! :edit, :load_mock_objects
|
198
|
+
end
|
199
|
+
filter_access_to :view, :attribute_check => true, :load_method => :load_method
|
200
|
+
def load_method
|
201
|
+
MockDataObject.new(:test => 2)
|
202
|
+
end
|
203
|
+
define_action_methods :show, :edit, :update, :delete, :create, :view
|
204
|
+
end
|
205
|
+
class LoadObjectControllerTest < ActionController::TestCase
|
206
|
+
tests LoadMockObjectsController
|
207
|
+
|
208
|
+
def test_filter_access_with_object_load
|
209
|
+
reader = Authorization::Reader::DSLReader.new
|
210
|
+
reader.parse %{
|
211
|
+
authorization do
|
212
|
+
role :test_role do
|
213
|
+
has_permission_on :load_mock_objects, :to => [:show, :edit] do
|
214
|
+
if_attribute :id => is {"1"}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
}
|
219
|
+
|
220
|
+
request!(MockUser.new(:test_role), "show", reader, :id => 2)
|
221
|
+
assert !@controller.authorized?
|
222
|
+
|
223
|
+
request!(MockUser.new(:test_role), "show", reader, :id => 1,
|
224
|
+
:clear => [:@load_mock_object])
|
225
|
+
assert @controller.authorized?
|
226
|
+
|
227
|
+
request!(MockUser.new(:test_role), "edit", reader, :id => 1,
|
228
|
+
:clear => [:@load_mock_object])
|
229
|
+
assert @controller.authorized?
|
230
|
+
assert @controller.instance_variable_defined?(:@load_mock_object)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_filter_access_with_object_load_custom
|
234
|
+
reader = Authorization::Reader::DSLReader.new
|
235
|
+
reader.parse %{
|
236
|
+
authorization do
|
237
|
+
role :test_role do
|
238
|
+
has_permission_on :load_mock_objects, :to => :view do
|
239
|
+
if_attribute :test => is {2}
|
240
|
+
end
|
241
|
+
has_permission_on :load_mock_objects, :to => :update do
|
242
|
+
if_attribute :test => is {1}
|
243
|
+
end
|
244
|
+
has_permission_on :load_mock_objects, :to => :delete do
|
245
|
+
if_attribute :test => is {2}
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
}
|
250
|
+
|
251
|
+
request!(MockUser.new(:test_role), "delete", reader)
|
252
|
+
assert !@controller.authorized?
|
253
|
+
|
254
|
+
request!(MockUser.new(:test_role), "view", reader)
|
255
|
+
assert @controller.authorized?
|
256
|
+
|
257
|
+
request!(MockUser.new(:test_role), "update", reader)
|
258
|
+
assert @controller.authorized?
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_filter_access_custom
|
262
|
+
reader = Authorization::Reader::DSLReader.new
|
263
|
+
reader.parse %{
|
264
|
+
authorization do
|
265
|
+
role :test_role do
|
266
|
+
has_permission_on :load_mock_objects, :to => :edit
|
267
|
+
end
|
268
|
+
role :test_role_2 do
|
269
|
+
has_permission_on :load_mock_objects, :to => :create
|
270
|
+
end
|
271
|
+
end
|
272
|
+
}
|
273
|
+
|
274
|
+
request!(MockUser.new(:test_role), "create", reader)
|
275
|
+
assert @controller.authorized?
|
276
|
+
|
277
|
+
request!(MockUser.new(:test_role_2), "create", reader)
|
278
|
+
assert !@controller.authorized?
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
##################
|
284
|
+
class AccessOverwritesController < MocksController
|
285
|
+
filter_access_to :test_action, :test_action_2,
|
286
|
+
:require => :test, :context => :permissions_2
|
287
|
+
filter_access_to :test_action, :require => :test, :context => :permissions
|
288
|
+
define_action_methods :test_action, :test_action_2
|
289
|
+
end
|
290
|
+
class AccessOverwritesControllerTest < ActionController::TestCase
|
291
|
+
def test_filter_access_overwrite
|
292
|
+
reader = Authorization::Reader::DSLReader.new
|
293
|
+
reader.parse %{
|
294
|
+
authorization do
|
295
|
+
role :test_role do
|
296
|
+
has_permission_on :permissions, :to => :test
|
297
|
+
end
|
298
|
+
end
|
299
|
+
}
|
300
|
+
request!(MockUser.new(:test_role), "test_action_2", reader)
|
301
|
+
assert !@controller.authorized?
|
302
|
+
|
303
|
+
request!(MockUser.new(:test_role), "test_action", reader)
|
304
|
+
assert @controller.authorized?
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
##################
|
310
|
+
class PeopleController < MocksController
|
311
|
+
filter_access_to :all
|
312
|
+
define_action_methods :show
|
313
|
+
end
|
314
|
+
class PluralizationControllerTest < ActionController::TestCase
|
315
|
+
tests PeopleController
|
316
|
+
|
317
|
+
def test_filter_access_people_controller
|
318
|
+
reader = Authorization::Reader::DSLReader.new
|
319
|
+
reader.parse %{
|
320
|
+
authorization do
|
321
|
+
role :test_role do
|
322
|
+
has_permission_on :people, :to => :show
|
323
|
+
end
|
324
|
+
end
|
325
|
+
}
|
326
|
+
request!(MockUser.new(:test_role), "show", reader)
|
327
|
+
assert @controller.authorized?
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
##################
|
333
|
+
class CommonController < MocksController
|
334
|
+
filter_access_to :delete, :context => :common
|
335
|
+
filter_access_to :all
|
336
|
+
end
|
337
|
+
class CommonChild1Controller < CommonController
|
338
|
+
filter_access_to :all, :context => :context_1
|
339
|
+
end
|
340
|
+
class CommonChild2Controller < CommonController
|
341
|
+
filter_access_to :delete
|
342
|
+
define_action_methods :show
|
343
|
+
end
|
344
|
+
class HierachicalControllerTest < ActionController::TestCase
|
345
|
+
tests CommonChild2Controller
|
346
|
+
def test_controller_hierarchy
|
347
|
+
reader = Authorization::Reader::DSLReader.new
|
348
|
+
reader.parse %{
|
349
|
+
authorization do
|
350
|
+
role :test_role do
|
351
|
+
has_permission_on :mocks, :to => [:delete, :show]
|
352
|
+
end
|
353
|
+
end
|
354
|
+
}
|
355
|
+
request!(MockUser.new(:test_role), "show", reader)
|
356
|
+
assert !@controller.authorized?
|
357
|
+
request!(MockUser.new(:test_role), "delete", reader)
|
358
|
+
assert !@controller.authorized?
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
|
3
|
+
class DSLReaderTest < Test::Unit::TestCase
|
4
|
+
def test_privileges
|
5
|
+
reader = Authorization::Reader::DSLReader.new
|
6
|
+
reader.parse %{
|
7
|
+
privileges do
|
8
|
+
privilege :test_priv do
|
9
|
+
includes :lower_priv
|
10
|
+
end
|
11
|
+
end
|
12
|
+
}
|
13
|
+
assert_equal 2, reader.privileges_reader.privileges.length
|
14
|
+
assert_equal [[:lower_priv, nil]],
|
15
|
+
reader.privileges_reader.privilege_hierarchy[:test_priv]
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_privileges_with_context
|
19
|
+
reader = Authorization::Reader::DSLReader.new
|
20
|
+
reader.parse %{
|
21
|
+
privileges do
|
22
|
+
privilege :test_priv, :test_context do
|
23
|
+
includes :lower_priv
|
24
|
+
end
|
25
|
+
end
|
26
|
+
}
|
27
|
+
assert_equal [[:lower_priv, :test_context]],
|
28
|
+
reader.privileges_reader.privilege_hierarchy[:test_priv]
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_privileges_one_line
|
32
|
+
reader = Authorization::Reader::DSLReader.new
|
33
|
+
reader.parse %{
|
34
|
+
privileges do
|
35
|
+
privilege :test_priv, :test_context, :includes => :lower_priv
|
36
|
+
privilege :test_priv_2, :test_context, :includes => [:lower_priv]
|
37
|
+
privilege :test_priv_3, :includes => [:lower_priv]
|
38
|
+
end
|
39
|
+
}
|
40
|
+
assert_equal [[:lower_priv, :test_context]],
|
41
|
+
reader.privileges_reader.privilege_hierarchy[:test_priv]
|
42
|
+
assert_equal [[:lower_priv, :test_context]],
|
43
|
+
reader.privileges_reader.privilege_hierarchy[:test_priv_2]
|
44
|
+
assert_equal [[:lower_priv, nil]],
|
45
|
+
reader.privileges_reader.privilege_hierarchy[:test_priv_3]
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_auth_role
|
49
|
+
reader = Authorization::Reader::DSLReader.new
|
50
|
+
reader.parse %{
|
51
|
+
authorization do
|
52
|
+
role :test_role do
|
53
|
+
includes :lesser_role
|
54
|
+
has_permission_on :items, :to => :read
|
55
|
+
end
|
56
|
+
end
|
57
|
+
}
|
58
|
+
assert_equal 1, reader.auth_rules_reader.roles.length
|
59
|
+
assert_equal [:lesser_role], reader.auth_rules_reader.role_hierarchy[:test_role]
|
60
|
+
assert_equal 1, reader.auth_rules_reader.auth_rules.length
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_auth_role_permit_on
|
64
|
+
reader = Authorization::Reader::DSLReader.new
|
65
|
+
reader.parse %|
|
66
|
+
authorization do
|
67
|
+
role :test_role do
|
68
|
+
has_permission_on :test_context do
|
69
|
+
to :test_perm, :manage
|
70
|
+
if_attribute :test_attr => is { user.test_attr }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
|
75
|
+
assert_equal 1, reader.auth_rules_reader.roles.length
|
76
|
+
assert_equal 1, reader.auth_rules_reader.auth_rules.length
|
77
|
+
assert reader.auth_rules_reader.auth_rules[0].matches?(:test_role, [:test_perm], :test_context)
|
78
|
+
assert reader.auth_rules_reader.auth_rules[0].matches?(:test_role, [:manage], :test_context)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_permit_block
|
82
|
+
reader = Authorization::Reader::DSLReader.new
|
83
|
+
reader.parse %|
|
84
|
+
authorization do
|
85
|
+
role :test_role do
|
86
|
+
has_permission_on :perms, :to => :test do
|
87
|
+
if_attribute :test_attr => is { user.test_attr }
|
88
|
+
if_attribute :test_attr_2 => is_not { user.test_attr }
|
89
|
+
if_attribute :test_attr_3 => contains { user.test_attr }
|
90
|
+
if_attribute :test_attr_4 => does_not_contain { user.test_attr }
|
91
|
+
if_attribute :test_attr_5 => is_in { user.test_attr }
|
92
|
+
if_attribute :test_attr_5 => is_not_in { user.test_attr }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
|
97
|
+
assert_equal 1, reader.auth_rules_reader.roles.length
|
98
|
+
assert_equal 1, reader.auth_rules_reader.auth_rules.length
|
99
|
+
assert reader.auth_rules_reader.auth_rules[0].matches?(:test_role, [:test], :perms)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_has_permission_to_with_context
|
103
|
+
reader = Authorization::Reader::DSLReader.new
|
104
|
+
reader.parse %|
|
105
|
+
authorization do
|
106
|
+
role :test_role do
|
107
|
+
has_permission_on :perms, :to => :test
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
|
111
|
+
assert_equal 1, reader.auth_rules_reader.roles.length
|
112
|
+
assert_equal 1, reader.auth_rules_reader.auth_rules.length
|
113
|
+
assert reader.auth_rules_reader.auth_rules[0].matches?(:test_role, [:test], :perms)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_context
|
117
|
+
reader = Authorization::Reader::DSLReader.new
|
118
|
+
reader.parse %{
|
119
|
+
contexts do
|
120
|
+
context :high_level_context do
|
121
|
+
includes :low_level_context_1, :low_level_context_2
|
122
|
+
end
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_dsl_error
|
128
|
+
reader = Authorization::Reader::DSLReader.new
|
129
|
+
assert_raise(Authorization::Reader::DSLError) do
|
130
|
+
reader.parse %{
|
131
|
+
authorization do
|
132
|
+
includes :lesser_role
|
133
|
+
end
|
134
|
+
}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_syntax_error
|
139
|
+
reader = Authorization::Reader::DSLReader.new
|
140
|
+
assert_raise(Authorization::Reader::DSLSyntaxError) do
|
141
|
+
reader.parse %{
|
142
|
+
authorizations do
|
143
|
+
end
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_syntax_error_2
|
149
|
+
reader = Authorization::Reader::DSLReader.new
|
150
|
+
assert_raise(Authorization::Reader::DSLSyntaxError) do
|
151
|
+
reader.parse %{
|
152
|
+
authorizations
|
153
|
+
end
|
154
|
+
}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|