stffn-declarative_authorization 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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