annotation_security 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. data/CHANGELOG +2 -0
  2. data/HOW-TO +261 -0
  3. data/MIT-LICENSE +18 -0
  4. data/README +39 -0
  5. data/Rakefile +56 -0
  6. data/assets/app/helpers/annotation_security_helper.rb +9 -0
  7. data/assets/config/initializers/annotation_security.rb +12 -0
  8. data/assets/config/security/relations.rb +20 -0
  9. data/assets/config/security/rights.yml +16 -0
  10. data/assets/vendor/plugins/annotation_security/init.rb +14 -0
  11. data/bin/annotation_security +8 -0
  12. data/lib/annotation_security/exceptions.rb +125 -0
  13. data/lib/annotation_security/exec.rb +189 -0
  14. data/lib/annotation_security/filters.rb +38 -0
  15. data/lib/annotation_security/includes/action_controller.rb +144 -0
  16. data/lib/annotation_security/includes/active_record.rb +28 -0
  17. data/lib/annotation_security/includes/helper.rb +215 -0
  18. data/lib/annotation_security/includes/resource.rb +85 -0
  19. data/lib/annotation_security/includes/role.rb +31 -0
  20. data/lib/annotation_security/includes/user.rb +27 -0
  21. data/lib/annotation_security/manager/policy_factory.rb +30 -0
  22. data/lib/annotation_security/manager/policy_manager.rb +80 -0
  23. data/lib/annotation_security/manager/relation_loader.rb +273 -0
  24. data/lib/annotation_security/manager/resource_manager.rb +36 -0
  25. data/lib/annotation_security/manager/right_loader.rb +88 -0
  26. data/lib/annotation_security/model_observer.rb +61 -0
  27. data/lib/annotation_security/policy/abstract_policy.rb +345 -0
  28. data/lib/annotation_security/policy/abstract_static_policy.rb +76 -0
  29. data/lib/annotation_security/policy/all_resources_policy.rb +21 -0
  30. data/lib/annotation_security/policy/rule.rb +340 -0
  31. data/lib/annotation_security/policy/rule_set.rb +139 -0
  32. data/lib/annotation_security/rails.rb +39 -0
  33. data/lib/annotation_security/user_wrapper.rb +74 -0
  34. data/lib/annotation_security/utils.rb +142 -0
  35. data/lib/annotation_security.rb +98 -0
  36. data/lib/extensions/action_controller.rb +33 -0
  37. data/lib/extensions/active_record.rb +35 -0
  38. data/lib/extensions/filter.rb +134 -0
  39. data/lib/extensions/object.rb +11 -0
  40. data/lib/security_context.rb +551 -0
  41. data/spec/annotation_security/exceptions_spec.rb +17 -0
  42. data/spec/annotation_security/includes/helper_spec.rb +82 -0
  43. data/spec/annotation_security/manager/policy_manager_spec.rb +15 -0
  44. data/spec/annotation_security/manager/resource_manager_spec.rb +17 -0
  45. data/spec/annotation_security/manager/right_loader_spec.rb +17 -0
  46. data/spec/annotation_security/policy/abstract_policy_spec.rb +17 -0
  47. data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -0
  48. data/spec/annotation_security/policy/rule_set_spec.rb +112 -0
  49. data/spec/annotation_security/policy/rule_spec.rb +78 -0
  50. data/spec/annotation_security/policy/test_policy_spec.rb +81 -0
  51. data/spec/annotation_security/security_context_spec.rb +78 -0
  52. data/spec/annotation_security/utils_spec.rb +74 -0
  53. data/spec/helper/test_controller.rb +66 -0
  54. data/spec/helper/test_helper.rb +5 -0
  55. data/spec/helper/test_relations.rb +7 -0
  56. data/spec/helper/test_resource.rb +39 -0
  57. data/spec/helper/test_rights.yml +5 -0
  58. data/spec/helper/test_role.rb +22 -0
  59. data/spec/helper/test_user.rb +32 -0
  60. data/spec/rails_stub.rb +38 -0
  61. data/spec/spec_helper.rb +43 -0
  62. metadata +157 -0
@@ -0,0 +1,551 @@
1
+ #
2
+ # = lib/security_context.rb
3
+ #
4
+ # Contains the SecurityContext
5
+ require 'active_support'
6
+
7
+ # = SecurityContext
8
+ #
9
+ # The SecurityContext provides methods for all security concerns of
10
+ # the current request.
11
+ #
12
+ # For every request, it has to be initialized using #current_user=. It is
13
+ # recommended to do this in a security filter, which can be used to catch
14
+ # AnnotationSecurityExceptions as well.
15
+ #
16
+ # The SecurityContext is implemented as a singleton for the current thread.
17
+ # Thus, all instance methods can be send to the class as well.
18
+ #
19
+ class SecurityContext
20
+
21
+ # Returns current security context
22
+ #
23
+ def self.current
24
+ Thread.current[:security_context]
25
+ end
26
+
27
+ # At the begin of a request, the security context will be initialized for the
28
+ # current controller.
29
+ #
30
+ def self.initialize(controller) # :nodoc:
31
+ load(new(controller))
32
+ end
33
+
34
+ # As the security context is a singleton bound to the current thread,
35
+ # it will not be available in other threads. The following example shows
36
+ # how to use the security context inside of a spawn block
37
+ #
38
+ # copy = SecurityContext.copy
39
+ # spawn do
40
+ # SecurityContext.load(copy)
41
+ # begin
42
+ # # ...
43
+ # rescue SecurityViolationError
44
+ # puts 'Security was violated'
45
+ # end
46
+ # end
47
+ #
48
+ def self.load(sec_context)
49
+ Thread.current[:security_context] = sec_context
50
+ end
51
+
52
+ if RAILS_ENV == 'development'
53
+ # Disables all security checkings.
54
+ # Is only available in development mode.
55
+ def self.ignore_security!
56
+ security_methods.each do |method|
57
+ class_eval "def self.#{method}(*args); true; end"
58
+ end
59
+ end
60
+ end
61
+
62
+ ## ===========================================================================
63
+ ## Instance
64
+
65
+ # Initialize context for the given controller
66
+ #
67
+ def initialize(controller) # :nodoc:
68
+ super()
69
+
70
+ @controller = controller
71
+
72
+ # initialize rule
73
+
74
+ # rules that are not bound to any source,
75
+ # will be triggered by model observer
76
+ @context_rules = new_rules_hash
77
+ @valid_objects = new_valid_objects_hash
78
+
79
+ # rules bound to request param
80
+ @param_rules = new_bound_rules_hash
81
+ @param_valid_objects = new_bound_valid_objects_hash
82
+
83
+ # rules bound to variable
84
+ @var_rules = new_bound_rules_hash
85
+ @var_valid_objects = new_bound_valid_objects_hash
86
+
87
+ # Hash with all required policies
88
+ @policies = new_policy_hash
89
+ end
90
+
91
+ # Sets the current user. This has to be done in a before or around filter,
92
+ # *before* entering the action. Elsewise, the user will be interpreted as
93
+ # not being logged in. Once set, the current user cannot be changed.
94
+ #
95
+ def credential=(user)
96
+ if @cred_set
97
+ raise AnnotationSecurity::AnnotationSecurityError,
98
+ "Credential already set for this request"
99
+ end
100
+ @cred_set = true
101
+ @credential = user
102
+ end
103
+
104
+ # Get the current credential
105
+ def credential
106
+ @credential
107
+ end
108
+
109
+ alias current_credential= credential=
110
+ alias current_credential credential
111
+
112
+ # Creates a copy of the current security context.
113
+ # See #load for more information.
114
+ def copy
115
+ returning self.class.new(@controller) { |sc| sc.credential = credential }
116
+ end
117
+
118
+ # Will be set if an security exception was catched by the security filter
119
+ def security_exception=(ex) # :nodoc:
120
+ @security_exception = ex
121
+ @controller.security_exception = ex
122
+ end
123
+
124
+ # If the action was aborted due to a security exception, this returns the
125
+ # exception that was raised. Returns nil if no exception occurred.
126
+ #
127
+ def security_exception
128
+ @security_exception
129
+ end
130
+
131
+ # See eval_with_security.
132
+ def send_with_security(rules, obj, msg, *args, &proc)
133
+ eval_with_security(rules) { obj.send(msg, *args, &proc) }
134
+ end
135
+
136
+ # Evaluates the given block, additionally using the given rules.
137
+ # rules == [ { :action => action, :resource => res_type, :source => binding }, ...]
138
+ # action and res_type should be symbols, binding is optional
139
+ #
140
+ def eval_with_security(rules)
141
+ install_rules(rules)
142
+
143
+ apply_rules_before_action
144
+
145
+ result = yield
146
+
147
+ apply_rules_after_action
148
+
149
+ result
150
+ rescue AnnotationSecurity::SecurityError
151
+ SecurityContext.security_exception = $!
152
+ raise $!
153
+ ensure
154
+ uninstall_rules(rules)
155
+ result
156
+ end
157
+
158
+ def apply_rules_before_action # :nodoc:
159
+ # apply static rules before entering the action
160
+ apply_static_rules
161
+ # bindings may apply to parameters, try to check them too
162
+ apply_param_rules
163
+ apply_var_rules
164
+ end
165
+
166
+ def apply_rules_after_action # :nodoc:
167
+ # check again, bindings may have been changed
168
+ apply_var_rules
169
+ end
170
+
171
+ # Returns true iif the operation defined by +policy_args+ is allowed.
172
+ #
173
+ # The following calls to \#allowed? are allowed:
174
+ #
175
+ # allowed? :show, :resource, @resource
176
+ # # => true if the current user has the right to show @resource,
177
+ # # which belongs to the :resource resource-class
178
+ #
179
+ # In case of model objects or other classes which implement a #resource_type
180
+ # method the the second argument may be ommited
181
+ #
182
+ # allowed? :show, @resource
183
+ # # equivalent to the above call if @resource.resource_type == :resource
184
+ #
185
+ # A policy description used as a controller annotation may also be to check
186
+ # a right
187
+ #
188
+ # allowed? "show resource", @resource
189
+ # # => true if the current user has the right "show resource" for @resource
190
+ #
191
+ # A policy may also be applied without an object representing the context:
192
+ #
193
+ # allowed? :show, :resource
194
+ # # => true if the current may show resources.
195
+ #
196
+ # This will only check system and pretest rules. The result +true+ does not
197
+ # mean that the user may show all resources. However, a +false+ indicates
198
+ # that the user is not allowed to show any resources.
199
+ #
200
+ # If the resource type is omitted as well, only rules defined for all
201
+ # resources can be tested. See RelationLoader#all_resources for details.
202
+ #
203
+ # allowed? :administrate
204
+ # # => true if the user is allowed to administrate all resources.
205
+ #
206
+ def allowed?(*policy_args)
207
+ policy_args = AnnotationSecurity::Utils.parse_policy_arguments(policy_args)
208
+ __allowed?(*policy_args)
209
+ end
210
+
211
+ # Equivalent to allowed?; is? is provided for better readability.
212
+ #
213
+ # SecurityContext.allowed? :logged_in
214
+ # vs
215
+ # SecurityContext.is? :logged_in
216
+ #
217
+ alias is? allowed?
218
+
219
+ # Raises a SecurityViolationError if the rule defined by +policy_args+ is not
220
+ # allowed. See allowed? for details.
221
+ #
222
+ def apply_rule(*args)
223
+ self.class.raise_access_denied(*args) unless allowed?(*args)
224
+ end
225
+
226
+ # Checks the rules of an other action. Note that rules that are bound to a
227
+ # variable can not be checked.
228
+ #
229
+ # ==== Parameters
230
+ # * +controller+ Symbol representing the controller, like :resource
231
+ # * +action+ The called action, like :update
232
+ # * +objects+ (optional) List of objects that will be relevant for that action.
233
+ # * +params+ (optional) Hash of the passed parameters, like :id => 1.
234
+ #
235
+ # ==== Examples
236
+ #
237
+ # Checks static and pretest rules.
238
+ # allow_action? :resource, :create
239
+ # # => true if the current user may execute ResourcesController#create
240
+ #
241
+ # Checks static, pretest and context rules
242
+ # allow_action? :resource, :edit, [@resource]
243
+ # # => true if the current user may execute ResourcesController#edit,
244
+ # # assuming that @resource will be used in that action
245
+ #
246
+ # Checks static, pretest and context rules and all rules that are bound
247
+ # to :id.
248
+ # allow_action? :resource, :edit, [@resource], {:id => 4}
249
+ # # => true if the current user may execute ResourcesController#edit,
250
+ # # assuming that @resource will be used in that action
251
+ #
252
+ def allow_action?(*args) # :nodoc:
253
+
254
+ controller, action, objects, params =
255
+ AnnotationSecurity::Utils.parse_action_args(args)
256
+
257
+ # var rules are ignored here
258
+ context_rules, param_rules, _ = get_rule_set(controller, action)
259
+
260
+ # check static rules
261
+ evaluate_statically(context_rules)
262
+
263
+ # check context rules for all objects
264
+ objects.each do |o|
265
+ res_type = o.resource_type
266
+ evaluate_context_rules(context_rules, res_type, o)
267
+ end
268
+
269
+ evaluate_bound_rules_for_params(param_rules, params)
270
+
271
+ true
272
+ rescue SecurityViolationError
273
+ return false
274
+ end
275
+
276
+ # Applies all system and pretest rules of the current action.
277
+ # Raises a SecurityViolationError if a rule is violated.
278
+ #
279
+ def apply_static_rules # :nodoc:
280
+ evaluate_statically(@context_rules)
281
+ end
282
+
283
+ def apply_param_rules # :nodoc:
284
+ evaluate_bound_rules(@param_rules, @param_valid_objects)
285
+ end
286
+
287
+ def apply_var_rules # :nodoc:
288
+ evaluate_bound_rules(@var_rules, @var_valid_objects)
289
+ end
290
+
291
+ # Applies all rules of the current action to the resource defined by
292
+ # +resource_args+. Raises a SecurityViolationError if a rule is
293
+ # violated.
294
+ #
295
+ def apply_context_rules(*res_args) # :nodoc:
296
+ restype, res = AnnotationSecurity::Utils.parse_resource_arguments(res_args)
297
+ evaluate_context_rules(@context_rules, restype, res)
298
+ end
299
+
300
+ alias apply_rules apply_context_rules # :nodoc:
301
+
302
+ # Call if a resource object was touched during an action. Will be called
303
+ # automatically for model objects.
304
+ #
305
+ # Applies all rules that are currently active to the resource defined by
306
+ # +resource_args+. Raises a SecurityViolationError if a rule is
307
+ # violated.
308
+ #
309
+ # ==== Usage
310
+ # observe :resource, @resource
311
+ # where <tt>:resource</tt> is the resource type @resource belongs to, or
312
+ # observe @resource
313
+ # which is equivalent if <tt>@resource.resource_name == :resource</tt>
314
+ #
315
+ def observe(*resource_args)
316
+ apply_context_rules(*resource_args)
317
+ end
318
+
319
+ # Raise a SecurityViolationError.
320
+ # See allowed? for details on +policy_args+.
321
+ #
322
+ def self.raise_access_denied(*policy_args)
323
+ log_access_denied(policy_args)
324
+ raise SecurityViolationError.access_denied(credential,*policy_args)
325
+ end
326
+
327
+ # Activates access logging for the current request.
328
+ #
329
+ def log!(&proc)
330
+ @enable_logging = true
331
+ @log = proc || Proc.new do |result, action, res_type, resource|
332
+ result = result ? 'ALLOWED' : 'REFUSED' unless result.is_a? String
333
+ msg = "%-8s %-10s %-16s %s" % [result, action, res_type, resource]
334
+ puts msg
335
+ end
336
+ end
337
+
338
+ def log_access_denied(policy_args) # :nodoc:
339
+ @log.call('DENIED!', *policy_args) if @enable_logging
340
+ end
341
+
342
+ protected
343
+
344
+ def log_access_check(*policy_args)
345
+ @log.call(*policy_args) if @enable_logging
346
+ end
347
+
348
+ private
349
+
350
+ # data =========================================================================
351
+
352
+ # { binding => { :res_type1 => [:action1, ...], ... }, ... }
353
+ def new_bound_rules_hash() # :nodoc:
354
+ Hash.new { |h,k| h[k] = new_rules_hash }
355
+ end
356
+
357
+ # { :res_type1 => [:action1, ...], ... }
358
+ def new_rules_hash() # :nodoc:
359
+ Hash.new { |h,k| h[k] = [] }
360
+ end
361
+
362
+ # { binding => [object1, ...], ...}
363
+ def new_bound_valid_objects_hash() # :nodoc:
364
+ Hash.new { |h,k| h[k] = [] }
365
+ end
366
+
367
+ # { :res_type1 => { :action1 => [object1, ...], ...}, ...}
368
+ def new_valid_objects_hash() # :nodoc:
369
+ Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
370
+ end
371
+
372
+ # {:res_type1 => policy1, ...}
373
+ def new_policy_hash() # :nodoc:
374
+ Hash.new { |h,k| h[k] = new_policy(k) }
375
+ end
376
+
377
+ def new_policy(resource_type) # :nodoc:
378
+ AnnotationSecurity::PolicyManager.create_policy(resource_type, credential)
379
+ end
380
+
381
+ # Get the policy for a resource type from the cache
382
+ def policy(res_type) # :nodoc:
383
+ @policies[res_type]
384
+ end
385
+
386
+ # rules management =============================================================
387
+
388
+ def install_rules(rules, rule_set=nil, controller=@controller.class)
389
+ rules.each { |rule| install_rule rule, rule_set, controller }
390
+ end
391
+
392
+ def install_rule(rule, rule_set, controller)
393
+ rule_list(rule, rule_set, controller) << rule[:action]
394
+ end
395
+
396
+ def uninstall_rules(rules, rule_set=nil, controller=@controller.class)
397
+ rules.each { |rule| uninstall_rule rule, rule_set, controller }
398
+ end
399
+
400
+ def uninstall_rule(rule, rule_set, controller)
401
+ list = rule_list(rule, rule_set, controller)
402
+ i = list.index(rule[:action])
403
+ list.delete_at(i) if i
404
+ end
405
+
406
+ def rule_list(rule, rule_set, controller)
407
+ rule_set ||= [@context_rules, @param_rules, @var_rules]
408
+ resource = rule[:resource] || controller.default_resource
409
+ source = rule[:source]
410
+ if source.nil?
411
+ list = rule_set.first[resource]
412
+ elsif source.is_a? Symbol
413
+ list = rule_set.second[source][resource]
414
+ else
415
+ list = rule_set.third[source][resource]
416
+ end
417
+ list
418
+ end
419
+
420
+ # returns rule set for other controller actions
421
+ def get_rule_set(controller, action) # :nodoc:
422
+ @rule_sets ||= Hash.new { |h,k| h[k] = {} }
423
+ rule_set = @rule_sets[controller][action]
424
+ unless rule_set
425
+ rule_set = [new_rules_hash, new_bound_rules_hash, new_bound_rules_hash]
426
+ rules = controller.descriptions_of action
427
+ install_rules rules, rule_set, controller
428
+ @rule_sets[controller][action] = rule_set
429
+ end
430
+ rule_set
431
+ end
432
+
433
+ # rule evaluation ==============================================================
434
+
435
+ # Evaluate the rules statically, skips all rules that are static only.
436
+ # * +rules+ a Hash like {:resource_type => [:right1, :right2]}
437
+ def evaluate_statically(rules) # :nodoc:
438
+ # rules == { :resource1 => [:right1, ...], ... }
439
+ rules.each_pair do |resource_type,rights|
440
+ policy(resource_type).evaluate_statically(rights)
441
+ end
442
+ end
443
+
444
+ # Checks bound rules. Evaluates the bindings, on success adds objects
445
+ # to valid objects.
446
+ # rules == { binding1 => { :res_type1 => [:action1, ...], ... }, ... }
447
+ # valid_objects == { binding1 => [object1, ...], ... }
448
+ def evaluate_bound_rules(rules, valid_objects) # :nodoc:
449
+ evaluate_bound_rules_with_binding(rules, valid_objects) do |binding|
450
+ @controller.values_of_source(binding)
451
+ end
452
+ end
453
+
454
+ # Checks bound rules using the values from params
455
+ # rules == { binding1 => { :res_type1 => [:action1, ...], ... }, ... }
456
+ # params == { binding1 => object1, ... }
457
+ def evaluate_bound_rules_for_params(rules, params) # :nodoc:
458
+ valid_objects = new_bound_valid_objects_hash
459
+ evaluate_bound_rules_with_binding(rules, valid_objects) do |binding|
460
+ values = params[binding]
461
+ values.is_a?(Array) ? values : [values]
462
+ end
463
+ end
464
+
465
+ def evaluate_bound_rules_with_binding(rules, valid_objects, &proc) # :nodoc:
466
+ rules.each_key do |binding|
467
+ value_ids = proc.call(binding)
468
+ rules[binding].each_key do |res_type|
469
+ values = value_ids.collect do |id|
470
+ AnnotationSecurity::ResourceManager.get_resource res_type, id
471
+ end
472
+ values.compact!
473
+ values_of_res_type = values - valid_objects[binding]
474
+ values_of_res_type.each do |resource|
475
+ evaluate_rules(rules[binding][res_type],
476
+ res_type,
477
+ resource)
478
+ valid_objects[binding] << resource
479
+ end
480
+ end
481
+ end
482
+ end
483
+
484
+ # Checks context rules for given resource
485
+ # rules == { :res_type1 => [:action1, ...], ... }
486
+ def evaluate_context_rules(rules, res_type, res) # :nodoc:
487
+ evaluate_rules(rules[res_type], res_type, res)
488
+ end
489
+
490
+ # Checks if actions on resource are allowed. If true, adds to valid objects.
491
+ # Returns true
492
+ # actions == [:action1, ...]
493
+ # valid_objects == { :action1 => [object1, ...], ... }
494
+ def evaluate_rules(actions, res_type, resource) # :nodoc:
495
+ valid_objects = @valid_objects[res_type]
496
+ actions.each do |action|
497
+ unless valid_objects[action].index(resource)
498
+ __apply_rule(action, res_type, resource)
499
+ valid_objects[action] << resource
500
+ end
501
+ end
502
+ true
503
+ end
504
+
505
+ # Usage:
506
+ # __allowed? :show, :assignment, an_assignment
507
+ def __allowed?(action, res_type, resource=nil) # :nodoc:
508
+
509
+ block = lambda do
510
+ if resource
511
+ policy(res_type).allowed?(action, resource)
512
+ else
513
+ policy(res_type).static_policy.allowed?(action, nil)
514
+ end
515
+ end
516
+
517
+ returning block.call do |r|
518
+ log_access_check r, action, res_type, resource
519
+ end
520
+ end
521
+
522
+ # Raises a SecurityViolationError if the rule defined by +policy_args+ is not
523
+ # allowed. See __allowed? for details.
524
+ #
525
+ def __apply_rule(*args) # :nodoc:
526
+ self.class.raise_access_denied(*args) unless __allowed?(*args)
527
+ end
528
+
529
+ #=============================================================================
530
+ # Singleton
531
+
532
+ def self.security_methods
533
+ instance_methods(false)
534
+ end
535
+
536
+ # create singleton methods
537
+ security_methods.each do |method|
538
+ if method.to_s.ends_with? '='
539
+ # setters need a different handling
540
+ class_eval %{
541
+ def self.#{method}(value)
542
+ current.#{method} value
543
+ end }
544
+ else
545
+ class_eval %{
546
+ def self.#{method}(*args,&proc)
547
+ current.#{method}(*args,&proc)
548
+ end }
549
+ end
550
+ end
551
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AnnotationSecurity::RuleExecutionError do
4
+
5
+ before(:all) do
6
+ AnnotationSecurity.define_relations(:rule_ex_error_test) do
7
+ broken_relation { 1/0 }
8
+ end
9
+ end
10
+
11
+ it 'should be raised if a relation throws an error' do
12
+ lambda {
13
+ RuleExErrorTestPolicy.new(:user,:res).broken_relation?
14
+ }.should raise_error(AnnotationSecurity::RuleExecutionError)
15
+ end
16
+
17
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe AnnotationSecurity::Helper do
4
+
5
+ before(:each) do
6
+ SecurityContext.initialize(TestController.new)
7
+ SecurityContext.credential = TestUser.new 'theuser'
8
+ @helper = TestHelper.new
9
+ @res = TestResource.new 'theuser'
10
+ end
11
+
12
+ it "should understand options hash" do
13
+ options = { :action => :edit, :controller => :test, :id => @res }
14
+ expect(:test, :edit, [], {:id => @res})
15
+ @helper.action_allowed?(options).should be_true
16
+ end
17
+
18
+ it "should understand path strings" do
19
+ path = 'test/theuser/edit'
20
+ with_path_info path
21
+ expect :test, :edit, [], {:id => 'theuser'}
22
+ @helper.action_allowed?(path).should be_true
23
+ end
24
+
25
+ it "should understand resource objects" do
26
+ with_path_info 'test/theuser', :get, {:action => :show}
27
+ expect :test, :show, [], {:id => 'theuser'}
28
+ @helper.expects(:url_for).with(@res).returns('test/theuser')
29
+ @helper.action_allowed?(@res).should be_true
30
+ end
31
+
32
+ it "should take html options into account" do
33
+ with_path_info 'test/theuser', :delete, {:action => :destroy}
34
+ expect :test, :destroy, [], {:id => 'theuser'}
35
+ @helper.expects(:url_for).with(@res).returns('test/theuser')
36
+ @helper.action_allowed?(@res, { :method => :delete}).should be_true
37
+ end
38
+
39
+ it "should call named routes" do
40
+ with_path_info 'test/theuser/edit'
41
+ expect :test, :edit, [@res], {}
42
+ @helper.expects(:edit_test_path).with(@res, {}).returns('test/theuser/edit')
43
+ @helper.action_allowed?(:edit_test_path, @res).should be_true
44
+ end
45
+
46
+ it "should support defining all parameters explicitly" do
47
+ expect :test, :edit, [@res], {:option => true}
48
+ params = { :action => :edit, :controller => :test, :option => true }
49
+ @helper.action_allowed?('path/to/something', @res, params).should be_true
50
+ end
51
+
52
+ it "should create links if allowed" do
53
+ options = { :action => :edit, :controller => :test, :id => @res }
54
+ expect(:test, :edit, [], {:id => @res})
55
+ @helper.expects(:link_to_if).with(true, "Edit", options, {}).returns("<a>success</a>")
56
+ @helper.link_to_if_allowed("Edit", options){'no access'}.should == "<a>success</a>"
57
+ end
58
+
59
+ it "should not create links if forbidden" do
60
+ options = { :action => :edit, :controller => :test, :id => @res }
61
+ expect(:test, :edit, [], {:id => @res}, false)
62
+ @helper.expects(:link_to_if).with(false, "Edit", options, {}).returns("no access")
63
+ @helper.link_to_if_allowed("Edit", options){"no access"}.should == "no access"
64
+ end
65
+
66
+ def expect(ctrl, action, obj, param, result=true)
67
+ SecurityContext.expects(:allow_action?).with(ctrl, action, obj, param).returns(result)
68
+ end
69
+
70
+ # prepares #recognize_path to resolve the request path
71
+ def with_path_info(path, env = nil, result={})
72
+ env = { :method => env } if env.is_a? Symbol
73
+ env ||= { :method => :get }
74
+ parts = path.split('/')
75
+ result[:controller] ||= parts.first.to_sym
76
+ result[:id] ||= parts.second
77
+ result[:action] ||= parts.third.to_sym
78
+ ActionController::Routing::Routes.expects(:recognize_path).with(path, env).returns(result)
79
+ end
80
+
81
+ end
82
+
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe AnnotationSecurity::PolicyManager do
4
+
5
+ it "should provide policy factories" do
6
+ AnnotationSecurity::PolicyManager.policy_factory(:policy_manager)
7
+ (defined? PolicyManagerPolicy).should_not be_nil
8
+ end
9
+
10
+ it "should return the policy class for a resource" do
11
+ AnnotationSecurity::PolicyManager.policy_class(:policy_manager_2).
12
+ should == PolicyManager2Policy
13
+ end
14
+
15
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe AnnotationSecurity::ResourceManager do
4
+
5
+ it "should provide resource classes" do
6
+ klass = AnnotationSecurity::ResourceManager.get_resource_class :test_resource
7
+ klass.should == TestResource
8
+ end
9
+
10
+ it "should find resource instances" do
11
+ res = AnnotationSecurity::ResourceManager.get_resource :test_resource, 'xy'
12
+ res.should be_instance_of(TestResource)
13
+ res.name.should == 'xy'
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe AnnotationSecurity::RightLoader do
4
+
5
+ it "should allow right definitions by hash" do
6
+ AnnotationSecurity::RightLoader.define_rights({
7
+ :right_loader => {
8
+ :right1 => 'if logged_in',
9
+ :right2 => 'if may_right1',
10
+ }})
11
+ (defined? RightLoaderPolicy).should_not be_nil
12
+ RightLoaderPolicy.has_rule?(:right1).should be_true
13
+ RightLoaderPolicy.has_rule?(:right2).should be_true
14
+ end
15
+
16
+ end
17
+