stffn-declarative_authorization 0.2.1 → 0.2.3

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.
@@ -0,0 +1,14 @@
1
+ # Groups methods and additions that were used but are not readily available in
2
+ # all supported Rails versions.
3
+ class Hash
4
+ unless {}.respond_to?(:deep_merge)
5
+ # Imported from Rails 2.2
6
+ def deep_merge(other_hash)
7
+ self.merge(other_hash) do |key, oldval, newval|
8
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
9
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
10
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/reader.rb ADDED
@@ -0,0 +1,391 @@
1
+ # Authorization::Reader
2
+
3
+ require File.dirname(__FILE__) + '/authorization.rb'
4
+
5
+ module Authorization
6
+ # Parses an authorization configuration file in the authorization DSL and
7
+ # constructs a data model of its contents.
8
+ #
9
+ # For examples and the modelled data model, see the
10
+ # README[link:files/README_rdoc.html].
11
+ #
12
+ # Also, see role definition methods
13
+ # * AuthorizationRulesReader#role,
14
+ # * AuthorizationRulesReader#includes,
15
+ # * AuthorizationRulesReader#title,
16
+ # * AuthorizationRulesReader#description
17
+ #
18
+ # Methods for rule definition in roles
19
+ # * AuthorizationRulesReader#has_permission_on,
20
+ # * AuthorizationRulesReader#to,
21
+ # * AuthorizationRulesReader#if_attribute,
22
+ # * AuthorizationRulesReader#if_permitted_to
23
+ #
24
+ # Methods to be used in if_attribute statements
25
+ # * AuthorizationRulesReader#contains,
26
+ # * AuthorizationRulesReader#does_not_contain,
27
+ # * AuthorizationRulesReader#is,
28
+ # * AuthorizationRulesReader#is_not,
29
+ # * AuthorizationRulesReader#is_in,
30
+ # * AuthorizationRulesReader#is_not_in
31
+ #
32
+ # And privilege definition methods
33
+ # * PrivilegesReader#privilege,
34
+ # * PrivilegesReader#includes
35
+ #
36
+ module Reader
37
+ # Signals errors that occur while reading and parsing an authorization DSL
38
+ class DSLError < Exception; end
39
+ # Signals errors in the syntax of an authorization DSL.
40
+ class DSLSyntaxError < DSLError; end
41
+
42
+ # Top-level reader, parses the methods +privileges+ and +authorization+.
43
+ # +authorization+ takes a block with authorization rules as described in
44
+ # AuthorizationRulesReader. The block to +privileges+ defines privilege
45
+ # hierarchies, as described in PrivilegesReader.
46
+ #
47
+ class DSLReader
48
+ attr_reader :privileges_reader, :auth_rules_reader # :nodoc:
49
+
50
+ def initialize ()
51
+ @privileges_reader = PrivilegesReader.new
52
+ @auth_rules_reader = AuthorizationRulesReader.new
53
+ end
54
+
55
+ # Parses a authorization DSL specification from the string given
56
+ # in +dsl_data+. Raises DSLSyntaxError if errors occur on parsing.
57
+ def parse (dsl_data, file_name = nil)
58
+ if file_name
59
+ DSLMethods.new(self).instance_eval(dsl_data, file_name)
60
+ else
61
+ DSLMethods.new(self).instance_eval(dsl_data)
62
+ end
63
+ rescue SyntaxError, NoMethodError, NameError => e
64
+ raise DSLSyntaxError, "Illegal DSL syntax: #{e}"
65
+ end
66
+
67
+ # Loads and parses a DSL from the given file name.
68
+ def self.load (dsl_file)
69
+ # TODO cache reader in production mode?
70
+ reader = new
71
+ reader.parse(File.read(dsl_file), dsl_file)
72
+ reader
73
+ end
74
+
75
+ # DSL methods
76
+ class DSLMethods # :nodoc:
77
+ def initialize (parent)
78
+ @parent = parent
79
+ end
80
+
81
+ def privileges (&block)
82
+ @parent.privileges_reader.instance_eval(&block)
83
+ end
84
+
85
+ def contexts (&block)
86
+ # Not implemented
87
+ end
88
+
89
+ def authorization (&block)
90
+ @parent.auth_rules_reader.instance_eval(&block)
91
+ end
92
+ end
93
+ end
94
+
95
+ # The PrivilegeReader handles the part of the authorization DSL in
96
+ # a +privileges+ block. Here, privilege hierarchies are defined.
97
+ class PrivilegesReader
98
+ # TODO handle privileges with separated context
99
+ attr_reader :privileges, :privilege_hierarchy # :nodoc:
100
+
101
+ def initialize # :nodoc:
102
+ @current_priv = nil
103
+ @current_context = nil
104
+ @privileges = []
105
+ # {priv => [[priv,ctx], ...]}
106
+ @privilege_hierarchy = {}
107
+ end
108
+
109
+ def append_privilege (priv) # :nodoc:
110
+ @privileges << priv unless @privileges.include?(priv)
111
+ end
112
+
113
+ # Defines part of a privilege hierarchy. For the given +privilege+,
114
+ # included privileges may be defined in the block (through includes)
115
+ # or as option :+includes+. If the optional context is given,
116
+ # the privilege hierarchy is limited to that context.
117
+ #
118
+ def privilege (privilege, context = nil, options = {}, &block)
119
+ if context.is_a?(Hash)
120
+ options = context
121
+ context = nil
122
+ end
123
+ @current_priv = privilege
124
+ @current_context = context
125
+ append_privilege privilege
126
+ instance_eval(&block) if block
127
+ includes(*options[:includes]) if options[:includes]
128
+ ensure
129
+ @current_priv = nil
130
+ @current_context = nil
131
+ end
132
+
133
+ # Specifies +privileges+ that are to be assigned as lower ones. Only to
134
+ # be used inside a privilege block.
135
+ def includes (*privileges)
136
+ raise DSLError, "includes only in privilege block" if @current_priv.nil?
137
+ privileges.each do |priv|
138
+ append_privilege priv
139
+ @privilege_hierarchy[@current_priv] ||= []
140
+ @privilege_hierarchy[@current_priv] << [priv, @current_context]
141
+ end
142
+ end
143
+ end
144
+
145
+ class AuthorizationRulesReader
146
+ attr_reader :roles, :role_hierarchy, :auth_rules,
147
+ :role_descriptions, :role_titles # :nodoc:
148
+
149
+ def initialize # :nodoc:
150
+ @current_role = nil
151
+ @current_rule = nil
152
+ @roles = []
153
+ # higher_role => [lower_roles]
154
+ @role_hierarchy = {}
155
+ @role_titles = {}
156
+ @role_descriptions = {}
157
+ @auth_rules = []
158
+ end
159
+
160
+ def append_role (role, options = {}) # :nodoc:
161
+ @roles << role unless @roles.include? role
162
+ @role_titles[role] = options[:title] if options[:title]
163
+ @role_descriptions[role] = options[:description] if options[:description]
164
+ end
165
+
166
+ # Defines the authorization rules for the given +role+ in the
167
+ # following block.
168
+ # role :admin do
169
+ # has_permissions_on ...
170
+ # end
171
+ #
172
+ def role (role, options = {}, &block)
173
+ append_role role, options
174
+ @current_role = role
175
+ yield
176
+ ensure
177
+ @current_role = nil
178
+ end
179
+
180
+ # Roles may inherit all the rights from subroles. The given +roles+
181
+ # become subroles of the current block's role.
182
+ # role :admin do
183
+ # includes :user
184
+ # has_permission_on :employees, :to => [:update, :create]
185
+ # end
186
+ # role :user do
187
+ # has_permission_on :employees, :to => :read
188
+ # end
189
+ #
190
+ def includes (*roles)
191
+ raise DSLError, "includes only in role blocks" if @current_role.nil?
192
+ @role_hierarchy[@current_role] ||= []
193
+ @role_hierarchy[@current_role] += roles.flatten
194
+ end
195
+
196
+ # Allows the definition of privileges to be allowed for the current role,
197
+ # either in a has_permission_on block or directly in one call.
198
+ # role :admin
199
+ # has_permission_on :employees, :to => :read
200
+ # has_permission_on [:employees, :orders], :to => :read
201
+ # has_permission_on :employees do
202
+ # to :create
203
+ # if_attribute ...
204
+ # end
205
+ # has_permission_on :employees, :to => :delete do
206
+ # if_attribute ...
207
+ # end
208
+ # end
209
+ # The block form allows to describe restrictions on the permissions
210
+ # using if_attribute. Multiple has_permission_on statements are
211
+ # OR'ed when evaluating the permissions. Also, multiple if_attribute
212
+ # statements in one block are OR'ed.
213
+ #
214
+ # Available options
215
+ # [:+to+]
216
+ # A symbol or an array of symbols representing the privileges that
217
+ # should be granted in this statement.
218
+ #
219
+ def has_permission_on (context, options = {}, &block)
220
+ raise DSLError, "has_permission_on only allowed in role blocks" if @current_role.nil?
221
+ options = {:to => []}.merge(options)
222
+
223
+ privs = options[:to]
224
+ privs = [privs] unless privs.is_a?(Array)
225
+ raise DSLError, "has_permission_on either needs a block or :to option" if !block_given? and privs.empty?
226
+
227
+ rule = AuthorizationRule.new(@current_role, privs, context)
228
+ @auth_rules << rule
229
+ if block_given?
230
+ @current_rule = rule
231
+ yield
232
+ raise DSLError, "has_permission_on block content specifies no privileges" if rule.privileges.empty?
233
+ # TODO ensure?
234
+ @current_rule = nil
235
+ end
236
+ end
237
+
238
+ # Sets a description for the current role. E.g.
239
+ # role :admin
240
+ # description "To be assigned to administrative personnel"
241
+ # has_permission_on ...
242
+ # end
243
+ def description (text)
244
+ raise DSLError, "description only allowed in role blocks" if @current_role.nil?
245
+ role_descriptions[@current_role] = text
246
+ end
247
+
248
+ # Sets a human-readable title for the current role. E.g.
249
+ # role :admin
250
+ # title "Administrator"
251
+ # has_permission_on ...
252
+ # end
253
+ def title (text)
254
+ raise DSLError, "title only allowed in role blocks" if @current_role.nil?
255
+ role_titles[@current_role] = text
256
+ end
257
+
258
+ # Used in a has_permission_on block, to may be used to specify privileges
259
+ # to be assigned to the current role under the conditions specified in
260
+ # the current block.
261
+ # role :admin
262
+ # has_permission_on :employees do
263
+ # to :create, :read, :update, :delete
264
+ # end
265
+ # end
266
+ def to (*privs)
267
+ raise DSLError, "to only allowed in has_permission_on blocks" if @current_rule.nil?
268
+ @current_rule.append_privileges(privs)
269
+ end
270
+
271
+ # In a has_permission_on block, if_attribute specifies conditions
272
+ # of dynamic parameters that have to be met for the user to meet the
273
+ # privileges in this block. Conditions are evaluated on the context
274
+ # object. Thus, the following allows CRUD for branch admins only on
275
+ # employees that belong to the same branch as the current user.
276
+ # role :branch_admin
277
+ # has_permission_on :employees do
278
+ # to :create, :read, :update, :delete
279
+ # if_attribute :branch => is { user.branch }
280
+ # end
281
+ # end
282
+ # In this case, is is the operator for evaluating the condition. Another
283
+ # operator is contains for collections. In the block supplied to the
284
+ # operator, +user+ specifies the current user for whom the condition
285
+ # is evaluated.
286
+ #
287
+ # Conditions may be nested:
288
+ # role :company_admin
289
+ # has_permission_on :employees do
290
+ # to :create, :read, :update, :delete
291
+ # if_attribute :branch => { :company => is {user.branch.company} }
292
+ # end
293
+ # end
294
+ #
295
+ # Multiple if_attribute statements are OR'ed.
296
+ #
297
+ # Arrays and fixed values may be used directly as hash values:
298
+ # if_attribute :id => 1
299
+ # if_attribute :id => [1,2]
300
+ #
301
+ def if_attribute (attr_conditions_hash)
302
+ raise DSLError, "if_attribute only in has_permission blocks" if @current_rule.nil?
303
+ parse_attribute_conditions_hash!(attr_conditions_hash)
304
+ @current_rule.append_attribute Attribute.new(attr_conditions_hash)
305
+ end
306
+
307
+ # if_permitted_to allows the has_permission_on block to depend on
308
+ # permissions on associated objects. By using it, the authorization
309
+ # rules may be a lot DRYer. E.g.:
310
+ #
311
+ # role :branch_manager
312
+ # has_permission_on :branches, :to => :manage do
313
+ # if_attribute :employees => includes { user }
314
+ # end
315
+ # has_permission_on :employees, :to => :read do
316
+ # if_permitted_to :read, :branch
317
+ # # instead of
318
+ # # if_attribute :branch => { :employees => includes { user } }
319
+ # end
320
+ # end
321
+ #
322
+ # if_permitted_to associations may be nested as well:
323
+ # if_permitted_to :read, :branch => :company
324
+ #
325
+ # Options:
326
+ # [:+context+]
327
+ # If the context of the refered object may not be infered from the
328
+ # associations name, the context may be given explicitly:
329
+ # if_permitted_to :read, :home_branch, :context => :branches
330
+ # if_permitted_to :read, :branch => :main_company, :context => :companies
331
+ #
332
+ def if_permitted_to (privilege, attr_or_hash, options = {})
333
+ raise DSLError, "if_permitted_to only in has_permission blocks" if @current_rule.nil?
334
+ options[:context] ||= attr_or_hash.delete(:context) if attr_or_hash.is_a?(Hash)
335
+ @current_rule.append_attribute AttributeWithPermission.new(privilege,
336
+ attr_or_hash, options[:context])
337
+ end
338
+
339
+ # In an if_attribute statement, is says that the value has to be
340
+ # met exactly by the if_attribute attribute. For information on the block
341
+ # argument, see if_attribute.
342
+ def is (&block)
343
+ [:is, block]
344
+ end
345
+
346
+ # The negation of is.
347
+ def is_not (&block)
348
+ [:is_not, block]
349
+ end
350
+
351
+ # In an if_attribute statement, contains says that the value has to be
352
+ # part of the collection specified by the if_attribute attribute.
353
+ # For information on the block argument, see if_attribute.
354
+ def contains (&block)
355
+ [:contains, block]
356
+ end
357
+
358
+ # The negation of contains.
359
+ def does_not_contain (&block)
360
+ [:does_not_contain, block]
361
+ end
362
+
363
+ # In an if_attribute statement, is_in says that the value has to
364
+ # contain the attribute value.
365
+ # For information on the block argument, see if_attribute.
366
+ def is_in (&block)
367
+ [:is_in, block]
368
+ end
369
+
370
+ # The negation of is_in.
371
+ def is_not_in (&block)
372
+ [:is_not_in, block]
373
+ end
374
+
375
+ private
376
+ def parse_attribute_conditions_hash! (hash)
377
+ merge_hash = {}
378
+ hash.each do |key, value|
379
+ if value.is_a?(Hash)
380
+ parse_attribute_conditions_hash!(value)
381
+ elsif !value.is_a?(Array)
382
+ merge_hash[key] = [:is, lambda { value }]
383
+ elsif value.is_a?(Array) and !value[0].is_a?(Symbol)
384
+ merge_hash[key] = [:is_in, value]
385
+ end
386
+ end
387
+ hash.merge!(merge_hash)
388
+ end
389
+ end
390
+ end
391
+ end
@@ -0,0 +1,576 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class AuthorizationTest < Test::Unit::TestCase
4
+
5
+ def test_permit
6
+ reader = Authorization::Reader::DSLReader.new
7
+ reader.parse %{
8
+ authorization do
9
+ role :test_role do
10
+ has_permission_on :permissions, :to => :test
11
+ end
12
+ end
13
+ }
14
+ engine = Authorization::Engine.new(reader)
15
+ assert engine.permit?(:test, :context => :permissions,
16
+ :user => MockUser.new(:test_role, :test_role_2))
17
+ assert !engine.permit?(:test_2, :context => :permissions_2,
18
+ :user => MockUser.new(:test_role))
19
+ assert !engine.permit?(:test, :context => :permissions,
20
+ :user => MockUser.new(:test_role_2))
21
+ end
22
+
23
+ def test_permit_context_people
24
+ reader = Authorization::Reader::DSLReader.new
25
+ reader.parse %{
26
+ authorization do
27
+ role :test_role do
28
+ has_permission_on :people, :to => :test
29
+ end
30
+ end
31
+ }
32
+ engine = Authorization::Engine.new(reader)
33
+ assert engine.permit?(:test, :context => :people,
34
+ :user => MockUser.new(:test_role))
35
+ end
36
+
37
+ def test_permit_multiple_contexts
38
+ reader = Authorization::Reader::DSLReader.new
39
+ reader.parse %{
40
+ authorization do
41
+ role :test_role do
42
+ has_permission_on [:permissions, :permissions_2], :to => :test
43
+ end
44
+ end
45
+ }
46
+ engine = Authorization::Engine.new(reader)
47
+ assert engine.permit?(:test, :context => :permissions,
48
+ :user => MockUser.new(:test_role))
49
+ assert engine.permit?(:test, :context => :permissions_2,
50
+ :user => MockUser.new(:test_role))
51
+ assert !engine.permit?(:test, :context => :permissions_3,
52
+ :user => MockUser.new(:test_role))
53
+ end
54
+
55
+ def test_obligations_without_conditions
56
+ reader = Authorization::Reader::DSLReader.new
57
+ reader.parse %{
58
+ authorization do
59
+ role :test_role do
60
+ has_permission_on :permissions, :to => :test
61
+ end
62
+ end
63
+ }
64
+ engine = Authorization::Engine.new(reader)
65
+ assert_equal [{}], engine.obligations(:test, :context => :permissions,
66
+ :user => MockUser.new(:test_role))
67
+ end
68
+
69
+ def test_obligations_with_conditions
70
+ reader = Authorization::Reader::DSLReader.new
71
+ reader.parse %{
72
+ authorization do
73
+ role :test_role do
74
+ has_permission_on :permissions, :to => :test do
75
+ if_attribute :attr => is { user.attr }
76
+ end
77
+ end
78
+ end
79
+ }
80
+ engine = Authorization::Engine.new(reader)
81
+ assert_equal [{:attr => [:is, 1]}],
82
+ engine.obligations(:test, :context => :permissions,
83
+ :user => MockUser.new(:test_role, :attr => 1))
84
+ end
85
+
86
+ def test_obligations_with_conditions_and_empty
87
+ reader = Authorization::Reader::DSLReader.new
88
+ reader.parse %{
89
+ authorization do
90
+ role :test_role do
91
+ has_permission_on :permissions, :to => :test
92
+ has_permission_on :permissions, :to => :test do
93
+ if_attribute :attr => is { user.attr }
94
+ end
95
+ end
96
+ end
97
+ }
98
+ engine = Authorization::Engine.new(reader)
99
+ assert_equal [{}, {:attr => [:is, 1]}],
100
+ engine.obligations(:test, :context => :permissions,
101
+ :user => MockUser.new(:test_role, :attr => 1))
102
+ end
103
+
104
+ def test_obligations_with_permissions
105
+ reader = Authorization::Reader::DSLReader.new
106
+ reader.parse %{
107
+ authorization do
108
+ role :test_role do
109
+ has_permission_on :permissions, :to => :test do
110
+ if_attribute :attr => is { user.attr }
111
+ end
112
+ has_permission_on :permission_children, :to => :test do
113
+ if_permitted_to :test, :permission, :context => :permissions
114
+ end
115
+ has_permission_on :permission_children_2, :to => :test do
116
+ if_permitted_to :test, :permission
117
+ end
118
+ has_permission_on :permission_children_children, :to => :test do
119
+ if_permitted_to :test, :permission_child => :permission,
120
+ :context => :permissions
121
+ end
122
+ end
123
+ end
124
+ }
125
+ engine = Authorization::Engine.new(reader)
126
+ assert_equal [{:permission => {:attr => [:is, 1]}}],
127
+ engine.obligations(:test, :context => :permission_children,
128
+ :user => MockUser.new(:test_role, :attr => 1))
129
+ assert_equal [{:permission => {:attr => [:is, 1]}}],
130
+ engine.obligations(:test, :context => :permission_children_2,
131
+ :user => MockUser.new(:test_role, :attr => 1))
132
+ assert_equal [{:permission_child => {:permission => {:attr => [:is, 1]}}}],
133
+ engine.obligations(:test, :context => :permission_children_children,
134
+ :user => MockUser.new(:test_role, :attr => 1))
135
+ end
136
+
137
+ def test_obligations_with_permissions_multiple
138
+ reader = Authorization::Reader::DSLReader.new
139
+ reader.parse %{
140
+ authorization do
141
+ role :test_role do
142
+ has_permission_on :permissions, :to => :test do
143
+ if_attribute :attr => is { 1 }
144
+ if_attribute :attr => is { 2 }
145
+ end
146
+ has_permission_on :permission_children_children, :to => :test do
147
+ if_permitted_to :test, :permission_child => :permission
148
+ end
149
+ end
150
+ end
151
+ }
152
+ engine = Authorization::Engine.new(reader)
153
+ assert_equal [{:permission_child => {:permission => {:attr => [:is, 1]}}},
154
+ {:permission_child => {:permission => {:attr => [:is, 2]}}}],
155
+ engine.obligations(:test, :context => :permission_children_children,
156
+ :user => MockUser.new(:test_role))
157
+ end
158
+
159
+ def test_guest_user
160
+ reader = Authorization::Reader::DSLReader.new
161
+ reader.parse %{
162
+ authorization do
163
+ role :guest do
164
+ has_permission_on :permissions, :to => :test
165
+ end
166
+ end
167
+ }
168
+ engine = Authorization::Engine.new(reader)
169
+ assert engine.permit?(:test, :context => :permissions)
170
+ assert !engine.permit?(:test, :context => :permissions_2)
171
+ end
172
+
173
+ def test_invalid_user_model
174
+ reader = Authorization::Reader::DSLReader.new
175
+ reader.parse %{
176
+ authorization do
177
+ role :guest do
178
+ has_permission_on :permissions, :to => :test
179
+ end
180
+ end
181
+ }
182
+ engine = Authorization::Engine.new(reader)
183
+ assert_raise(Authorization::AuthorizationUsageError) do
184
+ engine.permit?(:test, :context => :permissions, :user => MockUser.new(1, 2))
185
+ end
186
+ assert_raise(Authorization::AuthorizationUsageError) do
187
+ engine.permit?(:test, :context => :permissions, :user => MockDataObject.new)
188
+ end
189
+ end
190
+
191
+ def test_role_hierarchy
192
+ reader = Authorization::Reader::DSLReader.new
193
+ reader.parse %{
194
+ authorization do
195
+ role :test_role do
196
+ includes :lower_role
197
+ has_permission_on :permissions, :to => :test
198
+ end
199
+ role :lower_role do
200
+ has_permission_on :permissions, :to => :lower
201
+ end
202
+ end
203
+ }
204
+ engine = Authorization::Engine.new(reader)
205
+ assert engine.permit?(:lower, :context => :permissions,
206
+ :user => MockUser.new(:test_role))
207
+ end
208
+
209
+ def test_role_hierarchy_infinity
210
+ reader = Authorization::Reader::DSLReader.new
211
+ reader.parse %{
212
+ authorization do
213
+ role :test_role do
214
+ includes :lower_role
215
+ has_permission_on :permissions, :to => :test
216
+ end
217
+ role :lower_role do
218
+ includes :higher_role
219
+ has_permission_on :permissions, :to => :lower
220
+ end
221
+ end
222
+ }
223
+ engine = Authorization::Engine.new(reader)
224
+ assert engine.permit?(:lower, :context => :permissions,
225
+ :user => MockUser.new(:test_role))
226
+ end
227
+
228
+ def test_privilege_hierarchy
229
+ reader = Authorization::Reader::DSLReader.new
230
+ reader.parse %{
231
+ privileges do
232
+ privilege :test, :permissions do
233
+ includes :lower
234
+ end
235
+ end
236
+ authorization do
237
+ role :test_role do
238
+ has_permission_on :permissions, :to => :test
239
+ end
240
+ end
241
+ }
242
+ engine = Authorization::Engine.new(reader)
243
+ assert engine.permit?(:lower, :context => :permissions,
244
+ :user => MockUser.new(:test_role))
245
+ end
246
+
247
+ def test_privilege_hierarchy_without_context
248
+ reader = Authorization::Reader::DSLReader.new
249
+ reader.parse %{
250
+ privileges do
251
+ privilege :read do
252
+ includes :list, :show
253
+ end
254
+ end
255
+ authorization do
256
+ role :test_role do
257
+ has_permission_on :permissions, :to => :read
258
+ end
259
+ end
260
+ }
261
+ engine = Authorization::Engine.new(reader)
262
+ assert engine.permit?(:list, :context => :permissions,
263
+ :user => MockUser.new(:test_role))
264
+ end
265
+
266
+ def test_attribute_is
267
+ reader = Authorization::Reader::DSLReader.new
268
+ reader.parse %|
269
+ authorization do
270
+ role :test_role do
271
+ has_permission_on :permissions, :to => :test do
272
+ if_attribute :test_attr => is { user.test_attr }
273
+ if_attribute :test_attr => 3
274
+ end
275
+ end
276
+ end
277
+ |
278
+ engine = Authorization::Engine.new(reader)
279
+ assert engine.permit?(:test, :context => :permissions,
280
+ :user => MockUser.new(:test_role, :test_attr => 1),
281
+ :object => MockDataObject.new(:test_attr => 1))
282
+ assert engine.permit?(:test, :context => :permissions,
283
+ :user => MockUser.new(:test_role, :test_attr => 2),
284
+ :object => MockDataObject.new(:test_attr => 3))
285
+ assert((not(engine.permit?(:test, :context => :permissions,
286
+ :user => MockUser.new(:test_role, :test_attr => 2),
287
+ :object => MockDataObject.new(:test_attr => 1)))))
288
+ end
289
+
290
+ def test_attribute_is_not
291
+ reader = Authorization::Reader::DSLReader.new
292
+ reader.parse %|
293
+ authorization do
294
+ role :test_role do
295
+ has_permission_on :permissions, :to => :test do
296
+ if_attribute :test_attr => is_not { user.test_attr }
297
+ end
298
+ end
299
+ end
300
+ |
301
+ engine = Authorization::Engine.new(reader)
302
+ assert !engine.permit?(:test, :context => :permissions,
303
+ :user => MockUser.new(:test_role, :test_attr => 1),
304
+ :object => MockDataObject.new(:test_attr => 1))
305
+ assert engine.permit?(:test, :context => :permissions,
306
+ :user => MockUser.new(:test_role, :test_attr => 2),
307
+ :object => MockDataObject.new(:test_attr => 1))
308
+ end
309
+
310
+ def test_attribute_contains
311
+ reader = Authorization::Reader::DSLReader.new
312
+ reader.parse %|
313
+ authorization do
314
+ role :test_role do
315
+ has_permission_on :permissions, :to => :test do
316
+ if_attribute :test_attr => contains { user.test_attr }
317
+ end
318
+ end
319
+ end
320
+ |
321
+ engine = Authorization::Engine.new(reader)
322
+ assert engine.permit?(:test, :context => :permissions,
323
+ :user => MockUser.new(:test_role, :test_attr => 1),
324
+ :object => MockDataObject.new(:test_attr => [1,2]))
325
+ assert !engine.permit?(:test, :context => :permissions,
326
+ :user => MockUser.new(:test_role, :test_attr => 3),
327
+ :object => MockDataObject.new(:test_attr => [1,2]))
328
+ end
329
+
330
+ def test_attribute_does_not_contain
331
+ reader = Authorization::Reader::DSLReader.new
332
+ reader.parse %|
333
+ authorization do
334
+ role :test_role do
335
+ has_permission_on :permissions, :to => :test do
336
+ if_attribute :test_attr => does_not_contain { user.test_attr }
337
+ end
338
+ end
339
+ end
340
+ |
341
+ engine = Authorization::Engine.new(reader)
342
+ assert !engine.permit?(:test, :context => :permissions,
343
+ :user => MockUser.new(:test_role, :test_attr => 1),
344
+ :object => MockDataObject.new(:test_attr => [1,2]))
345
+ assert engine.permit?(:test, :context => :permissions,
346
+ :user => MockUser.new(:test_role, :test_attr => 3),
347
+ :object => MockDataObject.new(:test_attr => [1,2]))
348
+ end
349
+
350
+ def test_attribute_in_array
351
+ reader = Authorization::Reader::DSLReader.new
352
+ reader.parse %|
353
+ authorization do
354
+ role :test_role do
355
+ has_permission_on :permissions, :to => :test do
356
+ if_attribute :test_attr => is_in { [1,2] }
357
+ if_attribute :test_attr => [2,3]
358
+ end
359
+ end
360
+ end
361
+ |
362
+ engine = Authorization::Engine.new(reader)
363
+ assert engine.permit?(:test, :context => :permissions,
364
+ :user => MockUser.new(:test_role),
365
+ :object => MockDataObject.new(:test_attr => 1))
366
+ assert engine.permit?(:test, :context => :permissions,
367
+ :user => MockUser.new(:test_role),
368
+ :object => MockDataObject.new(:test_attr => 3))
369
+ assert !engine.permit?(:test, :context => :permissions,
370
+ :user => MockUser.new(:test_role),
371
+ :object => MockDataObject.new(:test_attr => 4))
372
+ end
373
+
374
+ def test_attribute_not_in_array
375
+ reader = Authorization::Reader::DSLReader.new
376
+ reader.parse %|
377
+ authorization do
378
+ role :test_role do
379
+ has_permission_on :permissions, :to => :test do
380
+ if_attribute :test_attr => is_not_in { [1,2] }
381
+ end
382
+ end
383
+ end
384
+ |
385
+ engine = Authorization::Engine.new(reader)
386
+ assert !engine.permit?(:test, :context => :permissions,
387
+ :user => MockUser.new(:test_role),
388
+ :object => MockDataObject.new(:test_attr => 1))
389
+ assert engine.permit?(:test, :context => :permissions,
390
+ :user => MockUser.new(:test_role),
391
+ :object => MockDataObject.new(:test_attr => 4))
392
+ end
393
+
394
+ def test_attribute_deep
395
+ reader = Authorization::Reader::DSLReader.new
396
+ reader.parse %|
397
+ authorization do
398
+ role :test_role do
399
+ has_permission_on :permissions, :to => :test do
400
+ if_attribute :test_attr_1 => {:test_attr_2 => contains { 1 }}
401
+ end
402
+ end
403
+ end
404
+ |
405
+ engine = Authorization::Engine.new(reader)
406
+ attr_1_struct = Struct.new(:test_attr_2)
407
+ assert engine.permit?(:test, :context => :permissions,
408
+ :user => MockUser.new(:test_role),
409
+ :object => MockDataObject.new(:test_attr_1 => attr_1_struct.new([1,2])))
410
+ assert !engine.permit?(:test, :context => :permissions,
411
+ :user => MockUser.new(:test_role),
412
+ :object => MockDataObject.new(:test_attr_1 => attr_1_struct.new([3,4])))
413
+ assert_equal [{:test_attr_1 => {:test_attr_2 => [:contains, 1]}}],
414
+ engine.obligations(:test, :context => :permissions,
415
+ :user => MockUser.new(:test_role))
416
+ end
417
+
418
+ def test_attribute_non_block
419
+ reader = Authorization::Reader::DSLReader.new
420
+ reader.parse %|
421
+ authorization do
422
+ role :test_role do
423
+ has_permission_on :permissions, :to => :test do
424
+ if_attribute :test_attr => 1
425
+ end
426
+ end
427
+ end
428
+ |
429
+ engine = Authorization::Engine.new(reader)
430
+ assert engine.permit?(:test, :context => :permissions,
431
+ :user => MockUser.new(:test_role),
432
+ :object => MockDataObject.new(:test_attr => 1))
433
+ assert !engine.permit?(:test, :context => :permissions,
434
+ :user => MockUser.new(:test_role),
435
+ :object => MockDataObject.new(:test_attr => 2))
436
+ end
437
+
438
+ def test_attribute_multiple
439
+ reader = Authorization::Reader::DSLReader.new
440
+ reader.parse %{
441
+ authorization do
442
+ role :test_role do
443
+ has_permission_on :permissions, :to => :test do
444
+ if_attribute :test_attr => 1
445
+ if_attribute :test_attr => 2 # or
446
+ end
447
+ end
448
+ end
449
+ }
450
+ engine = Authorization::Engine.new(reader)
451
+ assert engine.permit?(:test, :context => :permissions,
452
+ :user => MockUser.new(:test_role),
453
+ :object => MockDataObject.new(:test_attr => 1))
454
+ assert engine.permit?(:test, :context => :permissions,
455
+ :user => MockUser.new(:test_role),
456
+ :object => MockDataObject.new(:test_attr => 2))
457
+ end
458
+
459
+ class PermissionMock < MockDataObject
460
+ def self.table_name
461
+ "permissions"
462
+ end
463
+ end
464
+ def test_attribute_with_permissions
465
+ reader = Authorization::Reader::DSLReader.new
466
+ reader.parse %{
467
+ authorization do
468
+ role :test_role do
469
+ has_permission_on :permissions, :to => :test do
470
+ if_attribute :test_attr => 1
471
+ end
472
+ has_permission_on :permission_children, :to => :test do
473
+ if_permitted_to :test, :permission
474
+ end
475
+ end
476
+ end
477
+ }
478
+ engine = Authorization::Engine.new(reader)
479
+
480
+ perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
481
+ perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
482
+ assert engine.permit?(:test, :context => :permission_children,
483
+ :user => MockUser.new(:test_role),
484
+ :object => MockDataObject.new(:permission => perm_data_attr_1))
485
+ assert !engine.permit?(:test, :context => :permission_children,
486
+ :user => MockUser.new(:test_role),
487
+ :object => MockDataObject.new(:permission => perm_data_attr_2))
488
+ end
489
+
490
+ def test_attribute_with_deep_permissions
491
+ reader = Authorization::Reader::DSLReader.new
492
+ reader.parse %{
493
+ authorization do
494
+ role :test_role do
495
+ has_permission_on :permissions, :to => :test do
496
+ if_attribute :test_attr => 1
497
+ end
498
+ has_permission_on :permission_children, :to => :test do
499
+ if_permitted_to :test, :shallow_permission => :permission
500
+ end
501
+ end
502
+ end
503
+ }
504
+ engine = Authorization::Engine.new(reader)
505
+
506
+ perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
507
+ perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
508
+ assert engine.permit?(:test, :context => :permission_children,
509
+ :user => MockUser.new(:test_role),
510
+ :object => MockDataObject.new(:shallow_permission =>
511
+ MockDataObject.new(:permission => perm_data_attr_1)))
512
+ assert !engine.permit?(:test, :context => :permission_children,
513
+ :user => MockUser.new(:test_role),
514
+ :object => MockDataObject.new(:shallow_permission =>
515
+ MockDataObject.new(:permission => perm_data_attr_2)))
516
+ end
517
+
518
+ def test_raise_on_if_attribute_hash_on_collection
519
+ reader = Authorization::Reader::DSLReader.new
520
+ reader.parse %{
521
+ authorization do
522
+ role :test_role do
523
+ has_permission_on :permissions, :to => :test do
524
+ if_attribute :test_attrs => {:attr => is {1}}
525
+ end
526
+ end
527
+ end
528
+ }
529
+ engine = Authorization::Engine.new(reader)
530
+ assert_raise Authorization::AuthorizationUsageError do
531
+ engine.permit?(:test, :context => :permissions,
532
+ :user => MockUser.new(:test_role),
533
+ :object => MockDataObject.new(:test_attrs => [1, 2, 3]))
534
+ end
535
+ end
536
+
537
+ def test_role_title_description
538
+ reader = Authorization::Reader::DSLReader.new
539
+ reader.parse %{
540
+ authorization do
541
+ role :test_role, :title => 'Test Role' do
542
+ description "Test Role Description"
543
+ end
544
+ end
545
+ }
546
+ engine = Authorization::Engine.new(reader)
547
+ assert engine.roles.include?(:test_role)
548
+ assert_equal "Test Role", engine.role_titles[:test_role]
549
+ assert_equal "Test Role", engine.title_for(:test_role)
550
+ assert_nil engine.title_for(:test_role_2)
551
+ assert_equal "Test Role Description", engine.role_descriptions[:test_role]
552
+ assert_equal "Test Role Description", engine.description_for(:test_role)
553
+ assert_nil engine.description_for(:test_role_2)
554
+ end
555
+
556
+ def test_multithread
557
+ reader = Authorization::Reader::DSLReader.new
558
+ reader.parse %{
559
+ authorization do
560
+ role :test_role do
561
+ has_permission_on :permissions, :to => :test
562
+ end
563
+ end
564
+ }
565
+
566
+ engine = Authorization::Engine.new(reader)
567
+ Authorization.current_user = MockUser.new(:test_role)
568
+ assert engine.permit?(:test, :context => :permissions)
569
+ Thread.new do
570
+ Authorization.current_user = MockUser.new(:test_role2)
571
+ assert !engine.permit?(:test, :context => :permissions)
572
+ end
573
+ assert engine.permit?(:test, :context => :permissions)
574
+ Authorization.current_user = nil
575
+ end
576
+ end