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.
- data/CHANGELOG +2 -0
- data/Rakefile +8 -0
- data/app/controllers/authorization_rules_controller.rb +103 -0
- data/app/controllers/authorization_usages_controller.rb +19 -0
- data/app/helpers/authorization_rules_helper.rb +84 -0
- data/app/views/authorization_rules/graph.dot.erb +49 -0
- data/app/views/authorization_rules/graph.html.erb +39 -0
- data/app/views/authorization_rules/index.html.erb +15 -0
- data/app/views/authorization_usages/index.html.erb +45 -0
- data/config/routes.rb +6 -0
- data/lib/authorization.rb +514 -0
- data/lib/helper.rb +51 -0
- data/lib/in_controller.rb +311 -0
- data/lib/in_model.rb +130 -0
- data/lib/maintenance.rb +174 -0
- data/lib/obligation_scope.rb +281 -0
- data/lib/rails_legacy.rb +14 -0
- data/lib/reader.rb +391 -0
- data/test/authorization_test.rb +576 -0
- data/test/controller_test.rb +361 -0
- data/test/dsl_reader_test.rb +157 -0
- data/test/helper_test.rb +96 -0
- data/test/maintenance_test.rb +15 -0
- data/test/model_test.rb +794 -0
- data/test/schema.sql +32 -0
- data/test/test_helper.rb +99 -0
- metadata +26 -2
data/lib/rails_legacy.rb
ADDED
@@ -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
|