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.
- data/CHANGELOG +6 -0
- data/README.rdoc +24 -7
- data/app/controllers/authorization_rules_controller.rb +29 -5
- data/app/helpers/authorization_rules_helper.rb +7 -3
- data/app/views/authorization_rules/_change.erb +12 -3
- data/app/views/authorization_rules/_show_graph.erb +2 -2
- data/app/views/authorization_rules/_suggestions.erb +38 -14
- data/app/views/authorization_rules/change.html.erb +37 -9
- data/lib/declarative_authorization/authorization.rb +10 -7
- data/lib/declarative_authorization/development_support/analyzer.rb +2 -2
- data/lib/declarative_authorization/development_support/change_supporter.rb +46 -4
- data/lib/declarative_authorization/in_controller.rb +255 -3
- data/lib/declarative_authorization/in_model.rb +11 -2
- data/lib/declarative_authorization/maintenance.rb +2 -8
- data/lib/declarative_authorization/obligation_scope.rb +18 -7
- data/lib/declarative_authorization/reader.rb +4 -1
- data/test/authorization_test.rb +6 -2
- data/test/controller_filter_resource_access_test.rb +394 -0
- data/test/controller_test.rb +19 -8
- data/test/maintenance_test.rb +5 -0
- data/test/model_test.rb +137 -0
- data/test/schema.sql +1 -0
- data/test/test_helper.rb +18 -3
- metadata +5 -4
- data/app/views/authorization_rules/_suggestion.erb +0 -9
@@ -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
|
-
|
360
|
-
|
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 =
|
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
|
-
|
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(
|
111
|
-
reflection = reflection_for(
|
112
|
-
|
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.
|
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?(
|
236
|
-
value.is_a?(
|
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 (
|
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
|
|
data/test/authorization_test.rb
CHANGED
@@ -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.
|
555
|
-
"
|
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
|