uhees-declarative_authorization 0.3.1 → 0.3.2.2.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.
@@ -117,7 +117,36 @@ module Authorization
117
117
  end
118
118
  end
119
119
  end
120
-
120
+
121
+ def load_controller_object (context) # :nodoc:
122
+ instance_var = :"@#{context.to_s.singularize}"
123
+ model = context.to_s.classify.constantize
124
+ instance_variable_set(instance_var, model.find(params[:id]))
125
+ end
126
+
127
+ def load_parent_controller_object (parent_context) # :nodoc:
128
+ instance_var = :"@#{parent_context.to_s.singularize}"
129
+ model = parent_context.to_s.classify.constantize
130
+ instance_variable_set(instance_var, model.find(params[:"#{parent_context.to_s.singularize}_id"]))
131
+ end
132
+
133
+ def new_controller_object_from_params (context, parent_context) # :nodoc:
134
+ model_or_proxy = parent_context ?
135
+ instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
136
+ context.to_s.classify.constantize
137
+ instance_var = :"@#{context.to_s.singularize}"
138
+ instance_variable_set(instance_var,
139
+ model_or_proxy.new(params[context.to_s.singularize]))
140
+ end
141
+
142
+ def new_controller_object_for_collection (context, parent_context) # :nodoc:
143
+ model_or_proxy = parent_context ?
144
+ instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
145
+ context.to_s.classify.constantize
146
+ instance_var = :"@#{context.to_s.singularize}"
147
+ instance_variable_set(instance_var, model_or_proxy.new)
148
+ end
149
+
121
150
  module ClassMethods
122
151
  #
123
152
  # Defines a filter to be applied according to the authorization of the
@@ -270,6 +299,202 @@ module Authorization
270
299
  end
271
300
  end
272
301
  end
302
+
303
+ # To DRY up the filter_access_to statements in restful controllers,
304
+ # filter_resource_access combines typical filter_access_to and
305
+ # before_filter calls, which set up the instance variables.
306
+ #
307
+ # The simplest case are top-level resource controllers with only the
308
+ # seven CRUD methods, e.g.
309
+ # class CompanyController < ApplicationController
310
+ # filter_resource_access
311
+ #
312
+ # def index...
313
+ # end
314
+ # Here, all CRUD actions are protected through a filter_access_to :all
315
+ # statement. :+attribute_check+ is enabled for all actions except for
316
+ # the collection action :+index+. To have an object for attribute checks
317
+ # available, filter_resource_access will set the instance variable
318
+ # @+company+ in before filters. For the member actions (:+show+, :+edit+,
319
+ # :+update+, :+destroy+) @company is set to Company.find(params[:id]).
320
+ # For +new+ actions (:+new+, :+create+), filter_resource_access creates
321
+ # a new object from company parameters: Company.new(params[:company].
322
+ #
323
+ # For nested resources, the parent object may be loaded automatically.
324
+ # class BranchController < ApplicationController
325
+ # filter_resource_access :nested_in => :companies
326
+ # end
327
+ # Again, the CRUD actions are protected. Now, for all CRUD actions,
328
+ # the parent object @company is loaded from params[:company_id]. It is
329
+ # also used when creating @branch for +new+ actions. Here, attribute_check
330
+ # is enabled for the collection :+index+ as well, checking attributes on a
331
+ # @company.branches.new method.
332
+ #
333
+ # In many cases, the default seven CRUD actions are not sufficient. As in
334
+ # the resource definition for routing you may thus give additional member,
335
+ # new and collection methods. The options allow you to specify the
336
+ # required privileges for each action by providing a hash or an array of
337
+ # pairs. By default, for each action the action name is taken as privilege
338
+ # (action search in the example below requires the privilege :index
339
+ # :companies). Any controller action that is not specified and does not
340
+ # belong to the seven CRUD actions is handled as a member method.
341
+ # class CompanyController < ApplicationController
342
+ # filter_resource_access :collection => [[:search, :index], :index],
343
+ # :additional_member => {:mark_as_key_company => :update}
344
+ # end
345
+ # The +additional_+* options add to the respective CRUD actions,
346
+ # the other options replace the respective CRUD actions.
347
+ #
348
+ # You can override the default object loading by implementing any of the
349
+ # following instance methods on the controller. Examples are given for the
350
+ # BranchController (with +nested_in+ set to :+companies+):
351
+ # [+new_branch_from_params+]
352
+ # Used for +new+ actions.
353
+ # [+new_branch_for_collection+]
354
+ # Used for +collection+ actions if the +nested_in+ option is set.
355
+ # [+load_branch+]
356
+ # Used for +member+ actions.
357
+ # [+load_company+]
358
+ # Used for all +new+, +member+, and +collection+ actions if the
359
+ # +nested_in+ option is set.
360
+ #
361
+ # All options:
362
+ # [:+member+]
363
+ # Member methods are actions like +show+, which have an params[:id] from
364
+ # which to load the controller object and assign it to @controller_name,
365
+ # e.g. @+branch+.
366
+ #
367
+ # By default, member actions are [:+show+, :+edit+, :+update+,
368
+ # :+destroy+]. Also, any action not belonging to the seven CRUD actions
369
+ # are handled as member actions.
370
+ #
371
+ # There are three different syntax to specify member, collection and
372
+ # new actions.
373
+ # * Hash: Lets you set the required privilege for each action:
374
+ # {:+show+ => :+show+, :+mark_as_important+ => :+update+}
375
+ # * Array of actions or pairs: [:+show+, [:+mark_as_important+, :+update+]],
376
+ # with single actions requiring the privilege of the same name as the method.
377
+ # * Single method symbol: :+show+
378
+ # [:+additional_member+]
379
+ # Allows to add additional member actions to the default resource +member+
380
+ # actions.
381
+ # [:+collection+]
382
+ # Collection actions are like :+index+, actions without any controller object
383
+ # to check attributes of. If +nested_in+ is given, a new object is
384
+ # created from the parent object, e.g. @company.branches.new. Without
385
+ # +nested_in+, attribute check is deactivated for these actions. By
386
+ # default, collection is set to :+index+.
387
+ # [:+additional_collection+]
388
+ # Allows to add additional collaction actions to the default resource +collection+
389
+ # actions.
390
+ # [:+new+]
391
+ # +new+ methods are actions such as +new+ and +create+, which don't
392
+ # receive a params[:id] to load an object from, but
393
+ # a params[:controller_name_singular] hash with attributes for a new
394
+ # object. The attributes will be used here to create a new object and
395
+ # check the object against the authorization rules. The object is
396
+ # assigned to @controller_name_singular, e.g. @branch.
397
+ #
398
+ # If +nested_in+ is given, the new object
399
+ # is created from the parent_object.controller_name
400
+ # proxy, e.g. company.branches.new(params[:branch]). By default,
401
+ # +new+ is set to [:new, :create].
402
+ # [:+additional_new+]
403
+ # Allows to add additional new actions to the default resource +new+ actions.
404
+ # [:+context+]
405
+ # The context is used to determine the model to load objects from for the
406
+ # before_filters and the context of privileges to use in authorization
407
+ # checks.
408
+ # [:+nested_in+]
409
+ # Specifies the parent controller if the resource is nested in another
410
+ # one. This is used to automatically load the parent object, e.g.
411
+ # @+company+ from params[:company_id] for a BranchController nested in
412
+ # a CompanyController.
413
+ # [:+no_attribute_check+]
414
+ # Allows to set actions for which no attribute check should be perfomed.
415
+ # See filter_access_to on details. By default, with no +nested_in+,
416
+ # +no_attribute_check+ is set to all collections. If +nested_in+ is given
417
+ # +no_attribute_check+ is empty by default.
418
+ #
419
+ def filter_resource_access(options = {})
420
+ options = {
421
+ :new => [:new, :create],
422
+ :additional_new => nil,
423
+ :member => [:show, :edit, :update, :destroy],
424
+ :additional_member => nil,
425
+ :collection => [:index],
426
+ :additional_collection => nil,
427
+ #:new_method_for_collection => nil, # only symbol method name
428
+ #:new_method => nil, # only symbol method name
429
+ #:load_method => nil, # only symbol method name
430
+ :no_attribute_check => nil,
431
+ :context => nil,
432
+ :nested_in => nil,
433
+ }.merge(options)
434
+
435
+ new_actions = actions_from_option(options[:new]).merge(
436
+ actions_from_option(options[:additional_new]))
437
+ members = actions_from_option(options[:member]).merge(
438
+ actions_from_option(options[:additional_member]))
439
+ collections = actions_from_option(options[:collection]).merge(
440
+ actions_from_option(options[:additional_collection]))
441
+
442
+ options[:no_attribute_check] ||= collections.keys unless options[:nested_in]
443
+
444
+ unless options[:nested_in].blank?
445
+ load_method = :"load_#{options[:nested_in].to_s.singularize}"
446
+ before_filter do |controller|
447
+ if controller.respond_to?(load_method)
448
+ controller.send(load_method)
449
+ else
450
+ controller.send(:load_parent_controller_object, options[:nested_in])
451
+ end
452
+ end
453
+
454
+ new_for_collection_method = :"new_#{controller_name.singularize}_for_collection"
455
+ before_filter :only => collections.keys do |controller|
456
+ # new_for_collection
457
+ if controller.respond_to?(new_for_collection_method)
458
+ controller.send(new_for_collection_method)
459
+ else
460
+ controller.send(:new_controller_object_for_collection,
461
+ options[:context] || controller_name, options[:nested_in])
462
+ end
463
+ end
464
+ end
465
+
466
+ new_from_params_method = :"new_#{controller_name.singularize}_from_params"
467
+ before_filter :only => new_actions.keys do |controller|
468
+ # new_from_params
469
+ if controller.respond_to?(new_from_params_method)
470
+ controller.send(new_from_params_method)
471
+ else
472
+ controller.send(:new_controller_object_from_params,
473
+ options[:context] || controller_name, options[:nested_in])
474
+ end
475
+ end
476
+ load_method = :"load_#{controller_name.singularize}"
477
+ before_filter :only => members.keys do |controller|
478
+ # load controller object
479
+ if controller.respond_to?(load_method)
480
+ controller.send(load_method)
481
+ else
482
+ controller.send(:load_controller_object, options[:context] || controller_name)
483
+ end
484
+ end
485
+ filter_access_to :all, :attribute_check => true, :context => options[:context]
486
+
487
+ members.merge(new_actions).merge(collections).each do |action, privilege|
488
+ if action != privilege or (options[:no_attribute_check] and options[:no_attribute_check].include?(action))
489
+ filter_options = {
490
+ :context => options[:context],
491
+ :attribute_check => !options[:no_attribute_check] || !options[:no_attribute_check].include?(action)
492
+ }
493
+ filter_options[:require] = privilege if action != privilege
494
+ filter_access_to(action, filter_options)
495
+ end
496
+ end
497
+ end
273
498
 
274
499
  protected
275
500
  def filter_access_permissions # :nodoc:
@@ -285,6 +510,26 @@ module Authorization
285
510
  def filter_access_permissions? # :nodoc:
286
511
  class_variable_defined?(:@@declarative_authorization_permissions)
287
512
  end
513
+
514
+ def actions_from_option (option) # :nodoc:
515
+ case option
516
+ when nil
517
+ {}
518
+ when Symbol, String
519
+ {option.to_sym => option.to_sym}
520
+ when Hash
521
+ option
522
+ when Enumerable
523
+ option.each_with_object({}) do |action, hash|
524
+ if action.is_a?(Array)
525
+ raise "Unexpected option format: #{option.inspect}" if action.length != 2
526
+ hash[action.first] = action.last
527
+ else
528
+ hash[action.to_sym] = action.to_sym
529
+ end
530
+ end
531
+ end
532
+ end
288
533
  end
289
534
  end
290
535
 
@@ -356,8 +601,15 @@ module Authorization
356
601
  instance_var = :"@#{load_object_model.name.underscore}"
357
602
  object = contr.instance_variable_get(instance_var)
358
603
  unless object
359
- # catch ActiveRecord::RecordNotFound?
360
- object = load_object_model.find(contr.params[:id])
604
+ begin
605
+ object = load_object_model.find(contr.params[:id])
606
+ rescue ActiveRecord::RecordNotFound, RuntimeError
607
+ contr.logger.debug("filter_access_to tried to find " +
608
+ "#{load_object_model.inspect} from params[:id] " +
609
+ "(#{contr.params[:id].inspect}), because attribute_check is enabled " +
610
+ "and #{instance_var.to_s} isn't set.")
611
+ raise
612
+ end
361
613
  contr.instance_variable_set(instance_var, object)
362
614
  end
363
615
  object
@@ -8,7 +8,7 @@ module Authorization
8
8
 
9
9
  # If the user meets the given privilege, permitted_to? returns true
10
10
  # and yields to the optional block.
11
- def permitted_to? (privilege, options = {} )
11
+ def permitted_to? (privilege, options = {}, &block)
12
12
  options = {
13
13
  :user => Authorization.current_user,
14
14
  :object => self
@@ -38,7 +38,16 @@ module Authorization
38
38
  options = args.last.is_a?(Hash) ? args.pop : {}
39
39
  privilege = (args[0] || :read).to_sym
40
40
  privileges = [privilege]
41
- context = options[:context] || :"#{parent_scope.table_name}"
41
+ context =
42
+ if options[:context]
43
+ options[:context]
44
+ elsif parent_scope.respond_to?(:proxy_reflection)
45
+ parent_scope.proxy_reflection.klass.name.tableize.to_sym
46
+ elsif parent_scope.respond_to?(:decl_auth_context)
47
+ parent_scope.decl_auth_context
48
+ else
49
+ parent_scope.name.tableize.to_sym
50
+ end
42
51
 
43
52
  user = options[:user] || Authorization.current_user
44
53
 
@@ -2,12 +2,6 @@
2
2
  require File.dirname(__FILE__) + '/authorization.rb'
3
3
 
4
4
  module Authorization
5
-
6
- def self.ignore_access_control (state = nil) # :nodoc:
7
- Thread.current["ignore_access_control"] = state unless state.nil?
8
- Thread.current["ignore_access_control"] || false
9
- end
10
-
11
5
  # Provides a few maintenance methods for modifying data without enforcing
12
6
  # authorization.
13
7
  module Maintenance
@@ -21,8 +15,8 @@ module Authorization
21
15
  # without_access_control do
22
16
  # SomeModel.find(:first).save
23
17
  # end
24
- def without_access_control
25
- self.class.without_access_control
18
+ def without_access_control (&block)
19
+ Authorization::Maintenance.without_access_control(&block)
26
20
  end
27
21
 
28
22
  # A class method variant of without_access_control. Thus, one can call
@@ -107,9 +107,16 @@ module Authorization
107
107
  end
108
108
 
109
109
  # Returns the model associated with the given path.
110
- def model_for( path )
111
- reflection = reflection_for( path )
112
- reflection.respond_to?( :klass ) ? reflection.klass : reflection
110
+ def model_for (path)
111
+ reflection = reflection_for(path)
112
+
113
+ if reflection.respond_to?(:proxy_reflection)
114
+ reflection.proxy_reflection.klass
115
+ elsif reflection.respond_to?(:klass)
116
+ reflection.klass
117
+ else
118
+ reflection
119
+ end
113
120
  end
114
121
 
115
122
  # Returns the reflection corresponding to the given path.
@@ -125,10 +132,14 @@ module Authorization
125
132
  # Attempts to map a reflection for the given path. Raises if already defined.
126
133
  def map_reflection_for( path )
127
134
  raise "reflection for #{path.inspect} already exists" unless reflections[path].nil?
128
-
135
+
129
136
  reflection = path.empty? ? @proxy_scope : begin
130
137
  parent = reflection_for( path[0..-2] )
131
- parent.klass.reflect_on_association( path.last )
138
+ if !parent.respond_to?(:proxy_reflection) and parent.respond_to?(:klass)
139
+ parent.klass.reflect_on_association( path.last )
140
+ else
141
+ parent.reflect_on_association( path.last )
142
+ end
132
143
  rescue
133
144
  parent.reflect_on_association( path.last )
134
145
  end
@@ -232,8 +243,8 @@ module Authorization
232
243
  end
233
244
 
234
245
  def attribute_value (value)
235
- value.respond_to?( :descends_from_active_record? ) && value.descends_from_active_record? && value.id ||
236
- value.is_a?( Array ) && value[0].respond_to?( :descends_from_active_record? ) && value[0].descends_from_active_record? && value.map( &:id ) ||
246
+ value.class.respond_to?(:descends_from_active_record?) && value.class.descends_from_active_record? && value.id ||
247
+ value.is_a?(Array) && value[0].class.respond_to?(:descends_from_active_record?) && value[0].class.descends_from_active_record? && value.map( &:id ) ||
237
248
  value
238
249
  end
239
250
 
@@ -222,7 +222,10 @@ module Authorization
222
222
  # Join operator to logically connect the constraint statements inside
223
223
  # of the has_permission_on block. May be :+and+ or :+or+. Defaults to :+or+.
224
224
  #
225
- def has_permission_on (context, options = {}, &block)
225
+ def has_permission_on (*args, &block)
226
+ options = args.extract_options!
227
+ context = args.flatten
228
+
226
229
  raise DSLError, "has_permission_on only allowed in role blocks" if @current_role.nil?
227
230
  options = {:to => [], :join_by => :or}.merge(options)
228
231
 
@@ -40,6 +40,7 @@ class AuthorizationTest < Test::Unit::TestCase
40
40
  authorization do
41
41
  role :test_role do
42
42
  has_permission_on [:permissions, :permissions_2], :to => :test
43
+ has_permission_on :permissions_4, :permissions_5, :to => :test
43
44
  end
44
45
  end
45
46
  }
@@ -50,6 +51,9 @@ class AuthorizationTest < Test::Unit::TestCase
50
51
  :user => MockUser.new(:test_role))
51
52
  assert !engine.permit?(:test, :context => :permissions_3,
52
53
  :user => MockUser.new(:test_role))
54
+
55
+ assert engine.permit?(:test, :context => :permissions_4, :user => MockUser.new(:test_role))
56
+ assert engine.permit?(:test, :context => :permissions_5, :user => MockUser.new(:test_role))
53
57
  end
54
58
 
55
59
  def test_obligations_without_conditions
@@ -551,8 +555,8 @@ class AuthorizationTest < Test::Unit::TestCase
551
555
  end
552
556
 
553
557
  class PermissionMock < MockDataObject
554
- def self.table_name
555
- "permissions"
558
+ def self.name
559
+ "Permission"
556
560
  end
557
561
  end
558
562
  def test_attribute_with_permissions
@@ -0,0 +1,394 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class BasicResource < MockDataObject
4
+ def self.name
5
+ "BasicResource"
6
+ end
7
+ end
8
+ class BasicResourcesController < MocksController
9
+ filter_resource_access
10
+ define_resource_actions
11
+ end
12
+ class BasicResourcesControllerTest < ActionController::TestCase
13
+ def test_basic_filter_index
14
+ reader = Authorization::Reader::DSLReader.new
15
+ reader.parse %{
16
+ authorization do
17
+ role :allowed_role do
18
+ has_permission_on :basic_resources, :to => :index do
19
+ if_attribute :id => is {"1"}
20
+ end
21
+ end
22
+ end
23
+ }
24
+
25
+ allowed_user = MockUser.new(:allowed_role)
26
+ request!(MockUser.new(:another_role), :index, reader)
27
+ assert !@controller.authorized?
28
+ request!(allowed_user, :index, reader)
29
+ assert @controller.authorized?
30
+ end
31
+
32
+ def test_basic_filter_show_with_id
33
+ reader = Authorization::Reader::DSLReader.new
34
+ reader.parse %{
35
+ authorization do
36
+ role :allowed_role do
37
+ has_permission_on :basic_resources, :to => :show do
38
+ if_attribute :id => is {"1"}
39
+ end
40
+ end
41
+ end
42
+ }
43
+
44
+ allowed_user = MockUser.new(:allowed_role)
45
+ request!(allowed_user, :show, reader, :id => "2")
46
+ assert !@controller.authorized?
47
+ request!(allowed_user, :show, reader, :id => "1", :clear => [:@basic_resource])
48
+ assert @controller.authorized?
49
+ end
50
+
51
+ def test_basic_filter_new_with_params
52
+ reader = Authorization::Reader::DSLReader.new
53
+ reader.parse %{
54
+ authorization do
55
+ role :allowed_role do
56
+ has_permission_on :basic_resources, :to => :new do
57
+ if_attribute :id => is {"1"}
58
+ end
59
+ end
60
+ end
61
+ }
62
+
63
+ allowed_user = MockUser.new(:allowed_role)
64
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "2"})
65
+ assert !@controller.authorized?
66
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "1"},
67
+ :clear => [:@basic_resource])
68
+ assert @controller.authorized?
69
+ end
70
+ end
71
+
72
+
73
+ class NestedResource < MockDataObject
74
+ def initialize (attributes = {})
75
+ if attributes[:id]
76
+ attributes[:parent_mock] ||= ParentMock.new(:id => attributes[:id])
77
+ end
78
+ super(attributes)
79
+ end
80
+ def self.name
81
+ "NestedResource"
82
+ end
83
+ end
84
+ class ParentMock < MockDataObject
85
+ def nested_resources
86
+ Class.new do
87
+ def initialize (parent_mock)
88
+ @parent_mock = parent_mock
89
+ end
90
+ def new (attributes = {})
91
+ NestedResource.new(attributes.merge(:parent_mock => @parent_mock))
92
+ end
93
+ end.new(self)
94
+ end
95
+
96
+ def == (other)
97
+ id == other.id
98
+ end
99
+ def self.name
100
+ "ParentMock"
101
+ end
102
+ end
103
+ class NestedResourcesController < MocksController
104
+ filter_resource_access :nested_in => :parent_mocks
105
+ define_resource_actions
106
+ end
107
+ class NestedResourcesControllerTest < ActionController::TestCase
108
+ def test_nested_filter_index
109
+ reader = Authorization::Reader::DSLReader.new
110
+ reader.parse %{
111
+ authorization do
112
+ role :allowed_role do
113
+ has_permission_on :nested_resources, :to => :index do
114
+ if_attribute :parent_mock => is {ParentMock.find("1")}
115
+ end
116
+ end
117
+ end
118
+ }
119
+
120
+ allowed_user = MockUser.new(:allowed_role)
121
+ request!(MockUser.new(:another_role), :index, reader, :parent_mock_id => "2")
122
+ assert !@controller.authorized?
123
+ request!(allowed_user, :index, reader, :parent_mock_id => "2",
124
+ :clear => [:@nested_resource, :@parent_mock])
125
+ assert !@controller.authorized?
126
+ request!(allowed_user, :index, reader, :parent_mock_id => "1",
127
+ :clear => [:@nested_resource, :@parent_mock])
128
+ assert @controller.authorized?
129
+ end
130
+
131
+ def test_nested_filter_show_with_id
132
+ reader = Authorization::Reader::DSLReader.new
133
+ reader.parse %{
134
+ authorization do
135
+ role :allowed_role do
136
+ has_permission_on :nested_resources, :to => :show do
137
+ if_attribute :parent_mock => is {ParentMock.find("1")}
138
+ end
139
+ end
140
+ end
141
+ }
142
+
143
+ allowed_user = MockUser.new(:allowed_role)
144
+ request!(allowed_user, :show, reader, :id => "2", :parent_mock_id => "2")
145
+ assert !@controller.authorized?
146
+ request!(allowed_user, :show, reader, :id => "1", :parent_mock_id => "1",
147
+ :clear => [:@nested_resource, :@parent_mock])
148
+ assert @controller.authorized?
149
+ end
150
+
151
+ def test_nested_filter_new_with_params
152
+ reader = Authorization::Reader::DSLReader.new
153
+ reader.parse %{
154
+ authorization do
155
+ role :allowed_role do
156
+ has_permission_on :nested_resources, :to => :new do
157
+ if_attribute :parent_mock => is {ParentMock.find("1")}
158
+ end
159
+ end
160
+ end
161
+ }
162
+
163
+ allowed_user = MockUser.new(:allowed_role)
164
+ request!(allowed_user, :new, reader, :parent_mock_id => "2",
165
+ :nested_resource => {:id => "2"})
166
+ assert !@controller.authorized?
167
+ request!(allowed_user, :new, reader, :parent_mock_id => "1",
168
+ :nested_resource => {:id => "1"},
169
+ :clear => [:@nested_resource, :@parent_mock])
170
+ assert @controller.authorized?
171
+ end
172
+ end
173
+
174
+
175
+ class CustomMembersCollectionsResourceController < MocksController
176
+ def self.controller_name
177
+ "basic_resources"
178
+ end
179
+ filter_resource_access :member => [[:other_show, :read]],
180
+ :collection => {:search => :read}, :new => [:other_new]
181
+ define_action_methods :other_new, :search, :other_show
182
+ end
183
+ class CustomMembersCollectionsResourceControllerTest < ActionController::TestCase
184
+ def test_custom_members_filter_search
185
+ reader = Authorization::Reader::DSLReader.new
186
+ reader.parse %{
187
+ authorization do
188
+ role :allowed_role do
189
+ has_permission_on :basic_resources, :to => :read do
190
+ if_attribute :id => is {"1"}
191
+ end
192
+ end
193
+ end
194
+ }
195
+
196
+ request!(MockUser.new(:another_role), :search, reader)
197
+ assert !@controller.authorized?
198
+ request!(MockUser.new(:allowed_role), :search, reader)
199
+ assert @controller.authorized?
200
+ end
201
+
202
+ def test_custom_members_filter_other_show
203
+ reader = Authorization::Reader::DSLReader.new
204
+ reader.parse %{
205
+ authorization do
206
+ role :allowed_role do
207
+ has_permission_on :basic_resources, :to => :read do
208
+ if_attribute :id => is {"1"}
209
+ end
210
+ end
211
+ end
212
+ }
213
+
214
+ allowed_user = MockUser.new(:allowed_role)
215
+ request!(allowed_user, :other_show, reader, :id => "2")
216
+ assert !@controller.authorized?
217
+ request!(allowed_user, :other_show, reader, :id => "1", :clear => [:@basic_resource])
218
+ assert @controller.authorized?
219
+ end
220
+
221
+ def test_custom_members_filter_other_new
222
+ reader = Authorization::Reader::DSLReader.new
223
+ reader.parse %{
224
+ authorization do
225
+ role :allowed_role do
226
+ has_permission_on :basic_resources, :to => :other_new do
227
+ if_attribute :id => is {"1"}
228
+ end
229
+ end
230
+ end
231
+ }
232
+
233
+ allowed_user = MockUser.new(:allowed_role)
234
+ request!(allowed_user, :other_new, reader, :basic_resource => {:id => "2"})
235
+ assert !@controller.authorized?
236
+ request!(allowed_user, :other_new, reader, :basic_resource => {:id => "1"},
237
+ :clear => [:@basic_resource])
238
+ assert @controller.authorized?
239
+ end
240
+ end
241
+
242
+
243
+ class AdditionalMembersCollectionsResourceController < MocksController
244
+ def self.controller_name
245
+ "basic_resources"
246
+ end
247
+ filter_resource_access :additional_member => :other_show,
248
+ :additional_collection => [:search], :additional_new => {:other_new => :new}
249
+ define_resource_actions
250
+ define_action_methods :other_new, :search, :other_show
251
+ end
252
+ class AdditionalMembersCollectionsResourceControllerTest < ActionController::TestCase
253
+ def test_additional_members_filter_search_index
254
+ reader = Authorization::Reader::DSLReader.new
255
+ reader.parse %{
256
+ authorization do
257
+ role :allowed_role do
258
+ has_permission_on :basic_resources, :to => [:search, :index] do
259
+ if_attribute :id => is {"1"}
260
+ end
261
+ end
262
+ end
263
+ }
264
+
265
+ request!(MockUser.new(:another_role), :search, reader)
266
+ assert !@controller.authorized?
267
+ request!(MockUser.new(:another_role), :index, reader)
268
+ assert !@controller.authorized?
269
+ request!(MockUser.new(:allowed_role), :search, reader)
270
+ assert @controller.authorized?
271
+ request!(MockUser.new(:allowed_role), :index, reader)
272
+ assert @controller.authorized?
273
+ end
274
+
275
+ def test_additional_members_filter_other_show
276
+ reader = Authorization::Reader::DSLReader.new
277
+ reader.parse %{
278
+ authorization do
279
+ role :allowed_role do
280
+ has_permission_on :basic_resources, :to => [:show, :other_show] do
281
+ if_attribute :id => is {"1"}
282
+ end
283
+ end
284
+ end
285
+ }
286
+
287
+ allowed_user = MockUser.new(:allowed_role)
288
+ request!(allowed_user, :other_show, reader, :id => "2")
289
+ assert !@controller.authorized?
290
+ request!(allowed_user, :show, reader, :id => "2", :clear => [:@basic_resource])
291
+ assert !@controller.authorized?
292
+ request!(allowed_user, :other_show, reader, :id => "1", :clear => [:@basic_resource])
293
+ assert @controller.authorized?
294
+ request!(allowed_user, :show, reader, :id => "1", :clear => [:@basic_resource])
295
+ assert @controller.authorized?
296
+ end
297
+
298
+ def test_additional_members_filter_other_new
299
+ reader = Authorization::Reader::DSLReader.new
300
+ reader.parse %{
301
+ authorization do
302
+ role :allowed_role do
303
+ has_permission_on :basic_resources, :to => :new do
304
+ if_attribute :id => is {"1"}
305
+ end
306
+ end
307
+ end
308
+ }
309
+
310
+ allowed_user = MockUser.new(:allowed_role)
311
+ request!(allowed_user, :other_new, reader, :basic_resource => {:id => "2"})
312
+ assert !@controller.authorized?
313
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "2"},
314
+ :clear => [:@basic_resource])
315
+ assert !@controller.authorized?
316
+
317
+ request!(allowed_user, :other_new, reader, :basic_resource => {:id => "1"},
318
+ :clear => [:@basic_resource])
319
+ assert @controller.authorized?
320
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "1"},
321
+ :clear => [:@basic_resource])
322
+ assert @controller.authorized?
323
+ end
324
+ end
325
+
326
+
327
+ class CustomMethodsResourceController < MocksController
328
+ # not implemented yet
329
+ end
330
+
331
+
332
+ class ExplicitContextResourceController < MocksController
333
+ filter_resource_access :context => :basic_resources
334
+ define_resource_actions
335
+ end
336
+ class ExplicitContextResourceControllerTest < ActionController::TestCase
337
+ def test_explicit_context_filter_index
338
+ reader = Authorization::Reader::DSLReader.new
339
+ reader.parse %{
340
+ authorization do
341
+ role :allowed_role do
342
+ has_permission_on :basic_resources, :to => :index do
343
+ if_attribute :id => is {"1"}
344
+ end
345
+ end
346
+ end
347
+ }
348
+
349
+ allowed_user = MockUser.new(:allowed_role)
350
+ request!(MockUser.new(:another_role), :index, reader)
351
+ assert !@controller.authorized?
352
+ request!(allowed_user, :index, reader)
353
+ assert @controller.authorized?
354
+ end
355
+
356
+ def test_explicit_context_filter_show_with_id
357
+ reader = Authorization::Reader::DSLReader.new
358
+ reader.parse %{
359
+ authorization do
360
+ role :allowed_role do
361
+ has_permission_on :basic_resources, :to => :show do
362
+ if_attribute :id => is {"1"}
363
+ end
364
+ end
365
+ end
366
+ }
367
+
368
+ allowed_user = MockUser.new(:allowed_role)
369
+ request!(allowed_user, :show, reader, :id => "2")
370
+ assert !@controller.authorized?
371
+ request!(allowed_user, :show, reader, :id => "1", :clear => [:@basic_resource])
372
+ assert @controller.authorized?
373
+ end
374
+
375
+ def test_explicit_context_filter_new_with_params
376
+ reader = Authorization::Reader::DSLReader.new
377
+ reader.parse %{
378
+ authorization do
379
+ role :allowed_role do
380
+ has_permission_on :basic_resources, :to => :new do
381
+ if_attribute :id => is {"1"}
382
+ end
383
+ end
384
+ end
385
+ }
386
+
387
+ allowed_user = MockUser.new(:allowed_role)
388
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "2"})
389
+ assert !@controller.authorized?
390
+ request!(allowed_user, :new, reader, :basic_resource => {:id => "1"},
391
+ :clear => [:@basic_resource])
392
+ assert @controller.authorized?
393
+ end
394
+ end