stffn-declarative_authorization 0.2.4 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +16 -0
- data/README.rdoc +6 -1
- data/app/controllers/authorization_rules_controller.rb +5 -1
- data/app/controllers/authorization_usages_controller.rb +3 -1
- data/app/helpers/authorization_rules_helper.rb +16 -0
- data/app/views/authorization_rules/index.html.erb +2 -1
- data/lib/declarative_authorization/authorization.rb +77 -13
- data/lib/declarative_authorization/authorization_rules_analyzer.rb +138 -0
- data/lib/declarative_authorization/helper.rb +5 -0
- data/lib/declarative_authorization/in_controller.rb +11 -0
- data/lib/declarative_authorization/in_model.rb +12 -17
- data/lib/declarative_authorization/obligation_scope.rb +34 -23
- data/lib/declarative_authorization/reader.rb +48 -9
- data/test/authorization_rules_analyzer_test.rb +123 -0
- data/test/authorization_test.rb +203 -0
- data/test/helper_test.rb +38 -0
- data/test/model_test.rb +323 -1
- data/test/schema.sql +22 -1
- metadata +4 -2
@@ -76,6 +76,10 @@ module Authorization
|
|
76
76
|
#
|
77
77
|
# If an operation is not permitted, a Authorization::AuthorizationError
|
78
78
|
# is raised.
|
79
|
+
#
|
80
|
+
# To activate model security on all models, call using_access_control
|
81
|
+
# on ActiveRecord::Base
|
82
|
+
# ActiveRecord::Base.using_access_control
|
79
83
|
#
|
80
84
|
# Available options
|
81
85
|
# [:+context+] Specify context different from the models table name.
|
@@ -86,28 +90,19 @@ module Authorization
|
|
86
90
|
:context => nil,
|
87
91
|
:include_read => false
|
88
92
|
}.merge(options)
|
89
|
-
context = (options[:context] || self.table_name).to_sym
|
90
93
|
|
91
94
|
class_eval do
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
before_update do |object|
|
98
|
-
Authorization::Engine.instance.permit!(:update, :object => object,
|
99
|
-
:context => context)
|
95
|
+
[:create, :update, [:destroy, :delete]].each do |action, privilege|
|
96
|
+
send(:"before_#{action}") do |object|
|
97
|
+
Authorization::Engine.instance.permit!(privilege || action,
|
98
|
+
:object => object, :context => options[:context])
|
99
|
+
end
|
100
100
|
end
|
101
|
-
|
102
|
-
|
103
|
-
Authorization::Engine.instance.permit!(:delete, :object => object,
|
104
|
-
:context => context)
|
105
|
-
end
|
106
|
-
|
107
|
-
# only called if after_find is implemented
|
101
|
+
|
102
|
+
# after_find is only called if after_find is implemented
|
108
103
|
after_find do |object|
|
109
104
|
Authorization::Engine.instance.permit!(:read, :object => object,
|
110
|
-
:context => context)
|
105
|
+
:context => options[:context])
|
111
106
|
end
|
112
107
|
|
113
108
|
if options[:include_read]
|
@@ -48,7 +48,7 @@ module Authorization
|
|
48
48
|
@current_obligation = obligation
|
49
49
|
obligation_conditions[@current_obligation] ||= {}
|
50
50
|
follow_path( obligation )
|
51
|
-
|
51
|
+
|
52
52
|
rebuild_condition_options!
|
53
53
|
rebuild_join_options!
|
54
54
|
end
|
@@ -133,7 +133,7 @@ module Authorization
|
|
133
133
|
parent.reflect_on_association( path.last )
|
134
134
|
end
|
135
135
|
raise "invalid path #{path.inspect}" if reflection.nil?
|
136
|
-
|
136
|
+
|
137
137
|
reflections[path] = reflection
|
138
138
|
map_table_alias_for( path ) # Claim a table alias for the path.
|
139
139
|
|
@@ -208,17 +208,15 @@ module Authorization
|
|
208
208
|
end
|
209
209
|
bindvar = "#{attribute_table_alias}__#{attribute_name}_#{obligation_index}".to_sym
|
210
210
|
|
211
|
-
attribute_value = value.respond_to?( :descends_from_active_record? ) && value.descends_from_active_record? && value.id ||
|
212
|
-
value.is_a?( Array ) && value[0].respond_to?( :descends_from_active_record? ) && value[0].descends_from_active_record? && value.map( &:id ) ||
|
213
|
-
value
|
214
211
|
attribute_operator = case operator
|
215
212
|
when :contains, :is then "= :#{bindvar}"
|
216
213
|
when :does_not_contain, :is_not then "<> :#{bindvar}"
|
217
|
-
when :is_in
|
214
|
+
when :is_in, :intersects_with then "IN (:#{bindvar})"
|
218
215
|
when :is_not_in then "NOT IN (:#{bindvar})"
|
216
|
+
else raise AuthorizationUsageError, "Unknown operator: #{operator}"
|
219
217
|
end
|
220
218
|
obligation_conds << "#{connection.quote_table_name(attribute_table_alias)}.#{connection.quote_table_name(attribute_name)} #{attribute_operator}"
|
221
|
-
binds[bindvar] = attribute_value
|
219
|
+
binds[bindvar] = attribute_value(value)
|
222
220
|
end
|
223
221
|
end
|
224
222
|
obligation_conds << "1=1" if obligation_conds.empty?
|
@@ -227,36 +225,40 @@ module Authorization
|
|
227
225
|
(delete_paths - used_paths).each {|path| reflections.delete(path)}
|
228
226
|
@proxy_options[:conditions] = [ conds.join( " OR " ), binds ]
|
229
227
|
end
|
228
|
+
|
229
|
+
def attribute_value (value)
|
230
|
+
value.respond_to?( :descends_from_active_record? ) && value.descends_from_active_record? && value.id ||
|
231
|
+
value.is_a?( Array ) && value[0].respond_to?( :descends_from_active_record? ) && value[0].descends_from_active_record? && value.map( &:id ) ||
|
232
|
+
value
|
233
|
+
end
|
230
234
|
|
231
235
|
# Parses all of the defined obligation joins and defines the scope's :joins or :includes option.
|
232
236
|
# TODO: Support non-linear association paths. Right now, we just break down the longest path parsed.
|
233
237
|
def rebuild_join_options!
|
234
|
-
joins = @proxy_options[:joins] || []
|
238
|
+
joins = (@proxy_options[:joins] || []) + (@proxy_options[:includes] || [])
|
235
239
|
|
236
|
-
reflections.keys.
|
240
|
+
reflections.keys.each do |path|
|
237
241
|
next if path.empty?
|
238
|
-
|
242
|
+
|
239
243
|
existing_join = joins.find do |join|
|
240
|
-
|
244
|
+
existing_path = join_to_path(join)
|
245
|
+
min_length = [existing_path.length, path.length].min
|
246
|
+
existing_path.first(min_length) == path.first(min_length)
|
241
247
|
end
|
242
|
-
path_join = path_to_join(path)
|
243
248
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
joins
|
250
|
-
joins << path_join.deep_merge(existing_join)
|
251
|
-
when [NilClass, Hash], [NilClass, Symbol]
|
252
|
-
joins << path_join
|
249
|
+
if existing_join
|
250
|
+
if join_to_path(existing_join).length < path.length
|
251
|
+
joins[joins.index(existing_join)] = path_to_join(path)
|
252
|
+
end
|
253
|
+
else
|
254
|
+
joins << path_to_join(path)
|
253
255
|
end
|
254
256
|
end
|
255
257
|
|
256
258
|
case obligation_conditions.length
|
257
|
-
when 0
|
259
|
+
when 0 then
|
258
260
|
# No obligation conditions means we don't have to mess with joins or includes at all.
|
259
|
-
when 1
|
261
|
+
when 1 then
|
260
262
|
@proxy_options[:joins] = joins
|
261
263
|
@proxy_options.delete( :include )
|
262
264
|
else
|
@@ -277,5 +279,14 @@ module Authorization
|
|
277
279
|
hash
|
278
280
|
end
|
279
281
|
end
|
282
|
+
|
283
|
+
def join_to_path (join)
|
284
|
+
case join
|
285
|
+
when Symbol
|
286
|
+
[join]
|
287
|
+
when Hash
|
288
|
+
[join.keys.first] + join_to_path(join[join.keys.first])
|
289
|
+
end
|
290
|
+
end
|
280
291
|
end
|
281
292
|
end
|
@@ -24,6 +24,7 @@ module Authorization
|
|
24
24
|
# Methods to be used in if_attribute statements
|
25
25
|
# * AuthorizationRulesReader#contains,
|
26
26
|
# * AuthorizationRulesReader#does_not_contain,
|
27
|
+
# * AuthorizationRulesReader#intersects_with,
|
27
28
|
# * AuthorizationRulesReader#is,
|
28
29
|
# * AuthorizationRulesReader#is_not,
|
29
30
|
# * AuthorizationRulesReader#is_in,
|
@@ -209,22 +210,27 @@ module Authorization
|
|
209
210
|
# The block form allows to describe restrictions on the permissions
|
210
211
|
# using if_attribute. Multiple has_permission_on statements are
|
211
212
|
# OR'ed when evaluating the permissions. Also, multiple if_attribute
|
212
|
-
# statements in one block are OR'ed
|
213
|
+
# statements in one block are OR'ed if no :+join_by+ option is given
|
214
|
+
# (see below). To AND conditions, either set :+join_by+ to :and or place
|
215
|
+
# them in one if_attribute statement.
|
213
216
|
#
|
214
217
|
# Available options
|
215
218
|
# [:+to+]
|
216
219
|
# A symbol or an array of symbols representing the privileges that
|
217
220
|
# should be granted in this statement.
|
221
|
+
# [:+join_by+]
|
222
|
+
# Join operator to logically connect the constraint statements inside
|
223
|
+
# of the has_permission_on block. May be :+and+ or :+or+. Defaults to :+or+.
|
218
224
|
#
|
219
225
|
def has_permission_on (context, options = {}, &block)
|
220
226
|
raise DSLError, "has_permission_on only allowed in role blocks" if @current_role.nil?
|
221
|
-
options = {:to => []}.merge(options)
|
227
|
+
options = {:to => [], :join_by => :or}.merge(options)
|
222
228
|
|
223
229
|
privs = options[:to]
|
224
230
|
privs = [privs] unless privs.is_a?(Array)
|
225
231
|
raise DSLError, "has_permission_on either needs a block or :to option" if !block_given? and privs.empty?
|
226
232
|
|
227
|
-
rule = AuthorizationRule.new(@current_role, privs, context)
|
233
|
+
rule = AuthorizationRule.new(@current_role, privs, context, options[:join_by])
|
228
234
|
@auth_rules << rule
|
229
235
|
if block_given?
|
230
236
|
@current_rule = rule
|
@@ -292,11 +298,21 @@ module Authorization
|
|
292
298
|
# end
|
293
299
|
# end
|
294
300
|
#
|
295
|
-
# Multiple if_attribute
|
301
|
+
# Multiple attributes in one :if_attribute statement are AND'ed.
|
302
|
+
# Multiple if_attribute statements are OR'ed if the join operator for the
|
303
|
+
# has_permission_on block isn't explicitly set. Thus, the following would
|
304
|
+
# require the current user either to be of the same branch AND the employee
|
305
|
+
# to be "changeable_by_coworker". OR the current user has to be the
|
306
|
+
# employee in question.
|
307
|
+
# has_permission_on :employees, :to => :manage do
|
308
|
+
# if_attribute :branch => is {user.branch}, :changeable_by_coworker => true
|
309
|
+
# if_attribute :id => is {user.id}
|
310
|
+
# end
|
296
311
|
#
|
297
312
|
# Arrays and fixed values may be used directly as hash values:
|
298
|
-
# if_attribute :id
|
299
|
-
# if_attribute :
|
313
|
+
# if_attribute :id => 1
|
314
|
+
# if_attribute :type => "special"
|
315
|
+
# if_attribute :id => [1,2]
|
300
316
|
#
|
301
317
|
def if_attribute (attr_conditions_hash)
|
302
318
|
raise DSLError, "if_attribute only in has_permission blocks" if @current_rule.nil?
|
@@ -322,6 +338,18 @@ module Authorization
|
|
322
338
|
# if_permitted_to associations may be nested as well:
|
323
339
|
# if_permitted_to :read, :branch => :company
|
324
340
|
#
|
341
|
+
# To check permissions based on the current object, the attribute has to
|
342
|
+
# be left out:
|
343
|
+
# has_permission_on :branches, :to => :manage do
|
344
|
+
# if_attribute :employees => includes { user }
|
345
|
+
# end
|
346
|
+
# has_permission_on :branches, :to => :paint_green do
|
347
|
+
# if_permitted_to :update
|
348
|
+
# end
|
349
|
+
# Normally, one would merge those rules into one. Deviding makes sense
|
350
|
+
# if additional if_attribute are used in the second rule or those rules
|
351
|
+
# are applied to different roles.
|
352
|
+
#
|
325
353
|
# Options:
|
326
354
|
# [:+context+]
|
327
355
|
# If the context of the refered object may not be infered from the
|
@@ -329,9 +357,11 @@ module Authorization
|
|
329
357
|
# if_permitted_to :read, :home_branch, :context => :branches
|
330
358
|
# if_permitted_to :read, :branch => :main_company, :context => :companies
|
331
359
|
#
|
332
|
-
def if_permitted_to (privilege, attr_or_hash, options = {})
|
360
|
+
def if_permitted_to (privilege, attr_or_hash = nil, options = {})
|
333
361
|
raise DSLError, "if_permitted_to only in has_permission blocks" if @current_rule.nil?
|
334
362
|
options[:context] ||= attr_or_hash.delete(:context) if attr_or_hash.is_a?(Hash)
|
363
|
+
# only :context option in attr_or_hash:
|
364
|
+
attr_or_hash = nil if attr_or_hash.is_a?(Hash) and attr_or_hash.empty?
|
335
365
|
@current_rule.append_attribute AttributeWithPermission.new(privilege,
|
336
366
|
attr_or_hash, options[:context])
|
337
367
|
end
|
@@ -355,10 +385,19 @@ module Authorization
|
|
355
385
|
[:contains, block]
|
356
386
|
end
|
357
387
|
|
358
|
-
# The negation of contains.
|
388
|
+
# The negation of contains. Currently, query rewriting is disabled
|
389
|
+
# for does_not_contain.
|
359
390
|
def does_not_contain (&block)
|
360
391
|
[:does_not_contain, block]
|
361
392
|
end
|
393
|
+
|
394
|
+
# In an if_attribute statement, intersects_with requires that at least
|
395
|
+
# one of the values has to be part of the collection specified by the
|
396
|
+
# if_attribute attribute. The value block needs to evaluate to an
|
397
|
+
# Enumerable. For information on the block argument, see if_attribute.
|
398
|
+
def intersects_with (&block)
|
399
|
+
[:intersects_with, block]
|
400
|
+
end
|
362
401
|
|
363
402
|
# In an if_attribute statement, is_in says that the value has to
|
364
403
|
# contain the attribute value.
|
@@ -381,7 +420,7 @@ module Authorization
|
|
381
420
|
elsif !value.is_a?(Array)
|
382
421
|
merge_hash[key] = [:is, lambda { value }]
|
383
422
|
elsif value.is_a?(Array) and !value[0].is_a?(Symbol)
|
384
|
-
merge_hash[key] = [:is_in, value]
|
423
|
+
merge_hash[key] = [:is_in, lambda { value }]
|
385
424
|
end
|
386
425
|
end
|
387
426
|
hash.merge!(merge_hash)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), %w{.. lib declarative_authorization authorization_rules_analyzer})
|
3
|
+
|
4
|
+
class AuthorizationRulesAnalyzerTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_analyzing_complex_rules
|
7
|
+
assert_nothing_raised do
|
8
|
+
engine, analyzer = engine_analyzer_for %{
|
9
|
+
authorization do
|
10
|
+
role :guest do
|
11
|
+
has_permission_on :conferences, :to => :read do
|
12
|
+
if_attribute :published => true
|
13
|
+
end
|
14
|
+
has_permission_on :talks, :to => :read do
|
15
|
+
if_permitted_to :read, :conference
|
16
|
+
end
|
17
|
+
has_permission_on :users, :to => :create
|
18
|
+
has_permission_on :authorization_rules, :to => :read
|
19
|
+
has_permission_on :authorization_usages, :to => :read
|
20
|
+
end
|
21
|
+
|
22
|
+
role :user do
|
23
|
+
includes :guest
|
24
|
+
has_permission_on :conference_attendees, :to => :create do
|
25
|
+
if_attribute :user => is {user},
|
26
|
+
:conference => { :published => true }
|
27
|
+
end
|
28
|
+
has_permission_on :conference_attendees, :to => :delete do
|
29
|
+
if_attribute :user => is {user},
|
30
|
+
:conference => { :attendees => contains {user} }
|
31
|
+
end
|
32
|
+
has_permission_on :talk_attendees, :to => :create do
|
33
|
+
if_attribute :talk => { :conference => { :attendees => contains {user} }}
|
34
|
+
end
|
35
|
+
has_permission_on :talk_attendees, :to => :delete do
|
36
|
+
if_attribute :user => is {user}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
role :conference_organizer do
|
41
|
+
has_permission_on :conferences do
|
42
|
+
to :manage
|
43
|
+
# if...
|
44
|
+
end
|
45
|
+
has_permission_on [:conference_attendees, :talks, :talk_attendees], :to => :manage
|
46
|
+
end
|
47
|
+
|
48
|
+
role :admin do
|
49
|
+
has_permission_on [:conferences, :users, :talks], :to => :manage
|
50
|
+
has_permission_on :authorization_rules, :to => :read
|
51
|
+
has_permission_on :authorization_usages, :to => :read
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
privileges do
|
56
|
+
privilege :manage, :includes => [:create, :read, :update, :delete]
|
57
|
+
privilege :read, :includes => [:index, :show]
|
58
|
+
privilege :create, :includes => :new
|
59
|
+
privilege :update, :includes => :edit
|
60
|
+
privilege :delete, :includes => :destroy
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_mergeable_rules_without_constraints
|
67
|
+
engine, analyzer = engine_analyzer_for %{
|
68
|
+
authorization do
|
69
|
+
role :test_role do
|
70
|
+
has_permission_on :permissions, :to => :test
|
71
|
+
has_permission_on :permissions, :to => :test2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
}
|
75
|
+
|
76
|
+
report = analyzer.reports.find {|report| report.type == :mergeable_rules}
|
77
|
+
assert report
|
78
|
+
assert_equal 4, report.line
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_mergeable_rules_with_in_block_to
|
82
|
+
assert_nothing_raised do
|
83
|
+
engine, analyzer = engine_analyzer_for %{
|
84
|
+
authorization do
|
85
|
+
role :test_role do
|
86
|
+
has_permission_on :permissions do
|
87
|
+
to :test
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_no_mergeable_rules_with_constraints
|
96
|
+
engine, analyzer = engine_analyzer_for %{
|
97
|
+
authorization do
|
98
|
+
role :test_role do
|
99
|
+
has_permission_on :permissions, :to => :test do
|
100
|
+
if_attribute :some_attr => is {bla}
|
101
|
+
end
|
102
|
+
has_permission_on :permissions, :to => :test2 do
|
103
|
+
if_attribute :some_attr_2 => is {bla}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
}
|
108
|
+
|
109
|
+
assert !analyzer.reports.find {|report| report.type == :mergeable_rules}
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
def engine_analyzer_for (rules)
|
114
|
+
reader = Authorization::Reader::DSLReader.new
|
115
|
+
reader.parse rules
|
116
|
+
engine = Authorization::Engine.new(reader)
|
117
|
+
|
118
|
+
analyzer = Authorization::Analyzer.new(engine)
|
119
|
+
analyzer.analyze rules
|
120
|
+
|
121
|
+
[engine, analyzer]
|
122
|
+
end
|
123
|
+
end
|
data/test/authorization_test.rb
CHANGED
@@ -82,6 +82,42 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
82
82
|
engine.obligations(:test, :context => :permissions,
|
83
83
|
:user => MockUser.new(:test_role, :attr => 1))
|
84
84
|
end
|
85
|
+
|
86
|
+
def test_obligations_with_anded_conditions
|
87
|
+
reader = Authorization::Reader::DSLReader.new
|
88
|
+
reader.parse %{
|
89
|
+
authorization do
|
90
|
+
role :test_role do
|
91
|
+
has_permission_on :permissions, :to => :test, :join_by => :and do
|
92
|
+
if_attribute :attr => is { user.attr }
|
93
|
+
if_attribute :attr_2 => is { user.attr_2 }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
}
|
98
|
+
engine = Authorization::Engine.new(reader)
|
99
|
+
assert_equal [{:attr => [:is, 1], :attr_2 => [:is, 2]}],
|
100
|
+
engine.obligations(:test, :context => :permissions,
|
101
|
+
:user => MockUser.new(:test_role, :attr => 1, :attr_2 => 2))
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_obligations_with_deep_anded_conditions
|
105
|
+
reader = Authorization::Reader::DSLReader.new
|
106
|
+
reader.parse %{
|
107
|
+
authorization do
|
108
|
+
role :test_role do
|
109
|
+
has_permission_on :permissions, :to => :test, :join_by => :and do
|
110
|
+
if_attribute :attr => { :deeper_attr => is { user.deeper_attr }}
|
111
|
+
if_attribute :attr => { :deeper_attr_2 => is { user.deeper_attr_2 }}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
}
|
116
|
+
engine = Authorization::Engine.new(reader)
|
117
|
+
assert_equal [{:attr => { :deeper_attr => [:is, 1], :deeper_attr_2 => [:is, 2] } }],
|
118
|
+
engine.obligations(:test, :context => :permissions,
|
119
|
+
:user => MockUser.new(:test_role, :deeper_attr => 1, :deeper_attr_2 => 2))
|
120
|
+
end
|
85
121
|
|
86
122
|
def test_obligations_with_conditions_and_empty
|
87
123
|
reader = Authorization::Reader::DSLReader.new
|
@@ -390,6 +426,42 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
390
426
|
:user => MockUser.new(:test_role),
|
391
427
|
:object => MockDataObject.new(:test_attr => 4))
|
392
428
|
end
|
429
|
+
|
430
|
+
def test_attribute_intersects_with
|
431
|
+
reader = Authorization::Reader::DSLReader.new
|
432
|
+
reader.parse %{
|
433
|
+
authorization do
|
434
|
+
role :test_role do
|
435
|
+
has_permission_on :permissions, :to => :test do
|
436
|
+
if_attribute :test_attrs => intersects_with { [1,2] }
|
437
|
+
end
|
438
|
+
end
|
439
|
+
role :test_role_2 do
|
440
|
+
has_permission_on :permissions, :to => :test do
|
441
|
+
if_attribute :test_attrs => intersects_with { 1 }
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
}
|
446
|
+
|
447
|
+
engine = Authorization::Engine.new(reader)
|
448
|
+
assert_raise Authorization::AuthorizationUsageError do
|
449
|
+
engine.permit?(:test, :context => :permissions,
|
450
|
+
:user => MockUser.new(:test_role),
|
451
|
+
:object => MockDataObject.new(:test_attrs => 1 ))
|
452
|
+
end
|
453
|
+
assert_raise Authorization::AuthorizationUsageError do
|
454
|
+
engine.permit?(:test, :context => :permissions,
|
455
|
+
:user => MockUser.new(:test_role_2),
|
456
|
+
:object => MockDataObject.new(:test_attrs => [1, 2] ))
|
457
|
+
end
|
458
|
+
assert engine.permit?(:test, :context => :permissions,
|
459
|
+
:user => MockUser.new(:test_role),
|
460
|
+
:object => MockDataObject.new(:test_attrs => [1,3] ))
|
461
|
+
assert !engine.permit?(:test, :context => :permissions,
|
462
|
+
:user => MockUser.new(:test_role),
|
463
|
+
:object => MockDataObject.new(:test_attrs => [3,4] ))
|
464
|
+
end
|
393
465
|
|
394
466
|
def test_attribute_deep
|
395
467
|
reader = Authorization::Reader::DSLReader.new
|
@@ -514,6 +586,137 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
514
586
|
:object => MockDataObject.new(:shallow_permission =>
|
515
587
|
MockDataObject.new(:permission => perm_data_attr_2)))
|
516
588
|
end
|
589
|
+
|
590
|
+
def test_attribute_with_permissions_nil
|
591
|
+
reader = Authorization::Reader::DSLReader.new
|
592
|
+
reader.parse %{
|
593
|
+
authorization do
|
594
|
+
role :test_role do
|
595
|
+
has_permission_on :permissions, :to => :test do
|
596
|
+
if_attribute :test_attr => 1
|
597
|
+
end
|
598
|
+
has_permission_on :permission_children, :to => :test do
|
599
|
+
if_permitted_to :test, :permission
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
}
|
604
|
+
engine = Authorization::Engine.new(reader)
|
605
|
+
|
606
|
+
assert_nothing_raised do
|
607
|
+
engine.permit?(:test, :context => :permission_children,
|
608
|
+
:user => MockUser.new(:test_role),
|
609
|
+
:object => MockDataObject.new(:permission => nil))
|
610
|
+
end
|
611
|
+
|
612
|
+
assert !engine.permit?(:test, :context => :permission_children,
|
613
|
+
:user => MockUser.new(:test_role),
|
614
|
+
:object => MockDataObject.new(:permission => nil))
|
615
|
+
end
|
616
|
+
|
617
|
+
def test_attribute_with_permissions_on_self
|
618
|
+
reader = Authorization::Reader::DSLReader.new
|
619
|
+
reader.parse %{
|
620
|
+
authorization do
|
621
|
+
role :test_role do
|
622
|
+
has_permission_on :permissions, :to => :test do
|
623
|
+
if_attribute :test_attr => 1
|
624
|
+
end
|
625
|
+
has_permission_on :permissions, :to => :another_test do
|
626
|
+
if_permitted_to :test
|
627
|
+
end
|
628
|
+
end
|
629
|
+
end
|
630
|
+
}
|
631
|
+
engine = Authorization::Engine.new(reader)
|
632
|
+
|
633
|
+
perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
|
634
|
+
perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
|
635
|
+
assert engine.permit?(:another_test, :context => :permissions,
|
636
|
+
:user => MockUser.new(:test_role),
|
637
|
+
:object => perm_data_attr_1)
|
638
|
+
assert !engine.permit?(:another_test, :context => :permissions,
|
639
|
+
:user => MockUser.new(:test_role),
|
640
|
+
:object => perm_data_attr_2)
|
641
|
+
end
|
642
|
+
|
643
|
+
def test_attribute_with_permissions_on_self_with_context
|
644
|
+
reader = Authorization::Reader::DSLReader.new
|
645
|
+
reader.parse %{
|
646
|
+
authorization do
|
647
|
+
role :test_role do
|
648
|
+
has_permission_on :permissions, :to => :test do
|
649
|
+
if_attribute :test_attr => 1
|
650
|
+
end
|
651
|
+
has_permission_on :permissions, :to => :another_test do
|
652
|
+
if_permitted_to :test, :context => :permissions
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|
656
|
+
}
|
657
|
+
engine = Authorization::Engine.new(reader)
|
658
|
+
|
659
|
+
perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
|
660
|
+
perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
|
661
|
+
assert engine.permit?(:another_test, :context => :permissions,
|
662
|
+
:user => MockUser.new(:test_role),
|
663
|
+
:object => perm_data_attr_1)
|
664
|
+
assert !engine.permit?(:another_test, :context => :permissions,
|
665
|
+
:user => MockUser.new(:test_role),
|
666
|
+
:object => perm_data_attr_2)
|
667
|
+
end
|
668
|
+
|
669
|
+
def test_attribute_with_permissions_and_anded_rules
|
670
|
+
reader = Authorization::Reader::DSLReader.new
|
671
|
+
reader.parse %{
|
672
|
+
authorization do
|
673
|
+
role :test_role do
|
674
|
+
has_permission_on :permissions, :to => :test do
|
675
|
+
if_attribute :test_attr => 1
|
676
|
+
end
|
677
|
+
has_permission_on :permission_children, :to => :test, :join_by => :and do
|
678
|
+
if_permitted_to :test, :permission
|
679
|
+
if_attribute :test_attr => 1
|
680
|
+
end
|
681
|
+
end
|
682
|
+
end
|
683
|
+
}
|
684
|
+
engine = Authorization::Engine.new(reader)
|
685
|
+
|
686
|
+
perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
|
687
|
+
perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
|
688
|
+
assert engine.permit?(:test, :context => :permission_children,
|
689
|
+
:user => MockUser.new(:test_role),
|
690
|
+
:object => MockDataObject.new(:permission => perm_data_attr_1, :test_attr => 1))
|
691
|
+
assert !engine.permit?(:test, :context => :permission_children,
|
692
|
+
:user => MockUser.new(:test_role),
|
693
|
+
:object => MockDataObject.new(:permission => perm_data_attr_2, :test_attr => 1))
|
694
|
+
assert !engine.permit?(:test, :context => :permission_children,
|
695
|
+
:user => MockUser.new(:test_role),
|
696
|
+
:object => MockDataObject.new(:permission => perm_data_attr_1, :test_attr => 2))
|
697
|
+
end
|
698
|
+
|
699
|
+
def test_attribute_with_anded_rules
|
700
|
+
reader = Authorization::Reader::DSLReader.new
|
701
|
+
reader.parse %{
|
702
|
+
authorization do
|
703
|
+
role :test_role do
|
704
|
+
has_permission_on :permissions, :to => :test, :join_by => :and do
|
705
|
+
if_attribute :test_attr => 1
|
706
|
+
if_attribute :test_attr_2 => 2
|
707
|
+
end
|
708
|
+
end
|
709
|
+
end
|
710
|
+
}
|
711
|
+
engine = Authorization::Engine.new(reader)
|
712
|
+
|
713
|
+
assert engine.permit?(:test, :context => :permissions,
|
714
|
+
:user => MockUser.new(:test_role),
|
715
|
+
:object => MockDataObject.new(:test_attr => 1, :test_attr_2 => 2))
|
716
|
+
assert !engine.permit?(:test, :context => :permissions,
|
717
|
+
:user => MockUser.new(:test_role),
|
718
|
+
:object => MockDataObject.new(:test_attr => 1, :test_attr_2 => 3))
|
719
|
+
end
|
517
720
|
|
518
721
|
def test_raise_on_if_attribute_hash_on_collection
|
519
722
|
reader = Authorization::Reader::DSLReader.new
|