stffn-declarative_authorization 0.2.4 → 0.2.5
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 +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
|