annotation_security 1.0.1
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/HOW-TO +261 -0
- data/MIT-LICENSE +18 -0
- data/README +39 -0
- data/Rakefile +56 -0
- data/assets/app/helpers/annotation_security_helper.rb +9 -0
- data/assets/config/initializers/annotation_security.rb +12 -0
- data/assets/config/security/relations.rb +20 -0
- data/assets/config/security/rights.yml +16 -0
- data/assets/vendor/plugins/annotation_security/init.rb +14 -0
- data/bin/annotation_security +8 -0
- data/lib/annotation_security/exceptions.rb +125 -0
- data/lib/annotation_security/exec.rb +189 -0
- data/lib/annotation_security/filters.rb +38 -0
- data/lib/annotation_security/includes/action_controller.rb +144 -0
- data/lib/annotation_security/includes/active_record.rb +28 -0
- data/lib/annotation_security/includes/helper.rb +215 -0
- data/lib/annotation_security/includes/resource.rb +85 -0
- data/lib/annotation_security/includes/role.rb +31 -0
- data/lib/annotation_security/includes/user.rb +27 -0
- data/lib/annotation_security/manager/policy_factory.rb +30 -0
- data/lib/annotation_security/manager/policy_manager.rb +80 -0
- data/lib/annotation_security/manager/relation_loader.rb +273 -0
- data/lib/annotation_security/manager/resource_manager.rb +36 -0
- data/lib/annotation_security/manager/right_loader.rb +88 -0
- data/lib/annotation_security/model_observer.rb +61 -0
- data/lib/annotation_security/policy/abstract_policy.rb +345 -0
- data/lib/annotation_security/policy/abstract_static_policy.rb +76 -0
- data/lib/annotation_security/policy/all_resources_policy.rb +21 -0
- data/lib/annotation_security/policy/rule.rb +340 -0
- data/lib/annotation_security/policy/rule_set.rb +139 -0
- data/lib/annotation_security/rails.rb +39 -0
- data/lib/annotation_security/user_wrapper.rb +74 -0
- data/lib/annotation_security/utils.rb +142 -0
- data/lib/annotation_security.rb +98 -0
- data/lib/extensions/action_controller.rb +33 -0
- data/lib/extensions/active_record.rb +35 -0
- data/lib/extensions/filter.rb +134 -0
- data/lib/extensions/object.rb +11 -0
- data/lib/security_context.rb +551 -0
- data/spec/annotation_security/exceptions_spec.rb +17 -0
- data/spec/annotation_security/includes/helper_spec.rb +82 -0
- data/spec/annotation_security/manager/policy_manager_spec.rb +15 -0
- data/spec/annotation_security/manager/resource_manager_spec.rb +17 -0
- data/spec/annotation_security/manager/right_loader_spec.rb +17 -0
- data/spec/annotation_security/policy/abstract_policy_spec.rb +17 -0
- data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -0
- data/spec/annotation_security/policy/rule_set_spec.rb +112 -0
- data/spec/annotation_security/policy/rule_spec.rb +78 -0
- data/spec/annotation_security/policy/test_policy_spec.rb +81 -0
- data/spec/annotation_security/security_context_spec.rb +78 -0
- data/spec/annotation_security/utils_spec.rb +74 -0
- data/spec/helper/test_controller.rb +66 -0
- data/spec/helper/test_helper.rb +5 -0
- data/spec/helper/test_relations.rb +7 -0
- data/spec/helper/test_resource.rb +39 -0
- data/spec/helper/test_rights.yml +5 -0
- data/spec/helper/test_role.rb +22 -0
- data/spec/helper/test_user.rb +32 -0
- data/spec/rails_stub.rb +38 -0
- data/spec/spec_helper.rb +43 -0
- metadata +157 -0
@@ -0,0 +1,345 @@
|
|
1
|
+
#
|
2
|
+
# = lib/annotation_security/policy/abstract_policy.rb
|
3
|
+
#
|
4
|
+
|
5
|
+
# Abstract superclass for all policies
|
6
|
+
#
|
7
|
+
# For each resource type there is a corresponding policy class.
|
8
|
+
# In its entire lifetime, a policy object is responsible for a single user.
|
9
|
+
# A policy object can validate the rights for only one resource
|
10
|
+
# object at a time (it is not thread safe!).
|
11
|
+
#
|
12
|
+
class AnnotationSecurity::AbstractPolicy
|
13
|
+
|
14
|
+
# Creates a new policy class for a resource type.
|
15
|
+
#
|
16
|
+
def self.new_subclass(resource_type) #:nodoc:
|
17
|
+
returning Class.new(self) do |c|
|
18
|
+
c.initialize(resource_type)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initializes a subclass of AbstractPolicy
|
23
|
+
#
|
24
|
+
def self.initialize(resource_type) #:nodoc:
|
25
|
+
@resource_type = resource_type.to_s.underscore.to_sym
|
26
|
+
|
27
|
+
# register the class as constant
|
28
|
+
name = resource_type.to_s.camelize + classname_suffix
|
29
|
+
Object.const_set name, self
|
30
|
+
|
31
|
+
unless static?
|
32
|
+
# Each policy has a static partner
|
33
|
+
@static_policy_class = AnnotationSecurity::AbstractStaticPolicy.new_subclass(@resource_type)
|
34
|
+
@static_policy_class.belongs_to self
|
35
|
+
reset
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Suffix that is appended to the camlized resource type to
|
40
|
+
# generate a class name.
|
41
|
+
#
|
42
|
+
def self.classname_suffix # :nodoc:
|
43
|
+
static? ? 'StaticPolicy' : 'Policy'
|
44
|
+
end
|
45
|
+
|
46
|
+
# (Re-)Initializes the policy class.
|
47
|
+
# Removes all generated methods and clears the rule set.
|
48
|
+
#
|
49
|
+
def self.reset # :nodoc:
|
50
|
+
instance_methods(false).each { |m| remove_method m }
|
51
|
+
@has_rule = Hash.new {|h,k| h[k] = !get_rule(k).nil?}
|
52
|
+
@my_rules = Hash.new {|h,k| h[k] = load_rule(k)}
|
53
|
+
unless static?
|
54
|
+
# set of all rule objects available for this policy
|
55
|
+
@rule_set = AnnotationSecurity::RuleSet.new(self)
|
56
|
+
# {:rule => boolean} if true, the rule can be evaluated statically only
|
57
|
+
@static_only = Hash.new(false)
|
58
|
+
# {:rule => boolean} if true, the rule can be evaluated dynamically
|
59
|
+
@has_dynamic = Hash.new {|h,k| h[k] = has_rule?(k) && !@static_only[k]}
|
60
|
+
@static_policy_class.reset
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# List of strings that are not allowed as rule names (maybe incomplete).
|
65
|
+
#
|
66
|
+
def self.forbidden_rule_names # :nodoc:
|
67
|
+
instance_methods
|
68
|
+
end
|
69
|
+
|
70
|
+
# Rules that are defined for all resource types can be found here.
|
71
|
+
# (Overwritten by static policy)
|
72
|
+
def self.all_resources_policy # :nodoc:
|
73
|
+
AllResourcesPolicy
|
74
|
+
end
|
75
|
+
|
76
|
+
# Symbol representing the resource type this policy is responsible for.
|
77
|
+
#
|
78
|
+
def self.resource_type # :nodoc:
|
79
|
+
@resource_type
|
80
|
+
end
|
81
|
+
|
82
|
+
# The corresponding static policy class.
|
83
|
+
#
|
84
|
+
def self.static_policy_class # :nodoc:
|
85
|
+
@static_policy_class
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true iif this is policy class is responsible for static rules.
|
89
|
+
#
|
90
|
+
def self.static? # :nodoc:
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
# Rule set for this classes resource type
|
95
|
+
#
|
96
|
+
def self.rule_set # :nodoc:
|
97
|
+
@rule_set
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns true iif this policy can evaluate the rule
|
101
|
+
# * +symbol+ Name of the rule
|
102
|
+
def self.has_rule?(symbol) # :nodoc:
|
103
|
+
@has_rule[symbol]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns true iif the rule can be evaluated statically
|
107
|
+
# * +symbol+ Name of the rule
|
108
|
+
def self.has_static_rule?(symbol) # :nodoc:
|
109
|
+
static_policy_class.has_rule? symbol
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return true iif the rule can be evaluated dynamically
|
113
|
+
# * +symbol+ Name of the rule
|
114
|
+
def self.has_dynamic_rule?(symbol) # :nodoc:
|
115
|
+
@has_dynamic[symbol]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get a rule object
|
119
|
+
# * +symbol+ Name of the rule
|
120
|
+
def self.get_rule(symbol) #:nodoc:
|
121
|
+
@my_rules[symbol]
|
122
|
+
end
|
123
|
+
|
124
|
+
# The rule +symbol+ was requested, try to find and load it.
|
125
|
+
# Returns a rule object or nil.
|
126
|
+
def self.load_rule(symbol) #:nodoc:
|
127
|
+
# 1. Have a look in the rule set
|
128
|
+
# 2. Maybe the rule is defined for all resources
|
129
|
+
# 3. Redirect the rule to the static side
|
130
|
+
r = rule_set.get_rule(symbol,static?) ||
|
131
|
+
copy_rule_from(symbol,all_resources_policy) ||
|
132
|
+
use_static_rule(symbol)
|
133
|
+
# Create a method for the rule
|
134
|
+
r.extend_class(self) if r
|
135
|
+
r
|
136
|
+
end
|
137
|
+
|
138
|
+
# If possible, copies a rule from another policy class.
|
139
|
+
# * +symbol+ Name of the rule
|
140
|
+
# * +source_policy+ policy class to copy from
|
141
|
+
# Returns a rule object or nil.
|
142
|
+
def self.copy_rule_from(symbol,source_policy) # :nodoc:
|
143
|
+
rule_set.copy_rule_from(symbol,source_policy.rule_set,static?)
|
144
|
+
end
|
145
|
+
|
146
|
+
# If possible, redirects the rule to the static side.
|
147
|
+
# Returns a rule object or nil.
|
148
|
+
def self.use_static_rule(symbol) #:nodoc:
|
149
|
+
if has_static_rule?(symbol)
|
150
|
+
@static_only[symbol] = true
|
151
|
+
rule_set.create_dynamic_copy(symbol)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Add a rule
|
156
|
+
# * +symbol+ rule name
|
157
|
+
# * +args+ additional arguments
|
158
|
+
# * +block+ code block
|
159
|
+
# See AnnotationSecurity::Rule#initialize for details
|
160
|
+
#
|
161
|
+
def self.add_rule(symbol,*args,&block) #:nodoc:
|
162
|
+
rule_set.add_rule(symbol,*args,&block)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Initialize the instance for a user
|
166
|
+
# * +user+ user object this policy object is responsible for
|
167
|
+
# * +resource+ (optional) usually the resource object will be set using
|
168
|
+
# #allowed? or #with_resource
|
169
|
+
def initialize(user,resource=nil)
|
170
|
+
@user = user
|
171
|
+
@resource = resource
|
172
|
+
end
|
173
|
+
|
174
|
+
# Static policy object to evaluate the static rules
|
175
|
+
def static_policy # :nodoc:
|
176
|
+
@static_policy ||= self.class.static_policy_class.new(@user)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Symbol representing the resource type this policy is responsible for.
|
180
|
+
#
|
181
|
+
def resource_type # :nodoc:
|
182
|
+
self.class.resource_type
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns true iif the user has the +right+ for +resource_obj+
|
186
|
+
# * +right+ symbol
|
187
|
+
# * +resource_obj+ resource object to test the right for
|
188
|
+
# * +args+ (optional) additional arguments passed when evaluating the right
|
189
|
+
# This is not thread safe! Don't share policy objects between different
|
190
|
+
# threads (should be no problem though).
|
191
|
+
# ==== Example
|
192
|
+
# policy.allowed? :show, obj #=> true or false
|
193
|
+
def allowed?(right,resource_obj,*args)
|
194
|
+
@resource = resource_obj
|
195
|
+
__send__(right,*args)
|
196
|
+
# rescue
|
197
|
+
# raise "#{$!} in #{resource_type} policy " +
|
198
|
+
# "during rule #{right} of #{resource_obj} with args [#{args.join(", ")}]"
|
199
|
+
end
|
200
|
+
|
201
|
+
# Sets the resource object and returns self
|
202
|
+
# ==== Example
|
203
|
+
# policy.with_resource(obj).show? #=> true or false
|
204
|
+
def with_resource(resource_obj)
|
205
|
+
@resource = resource_obj
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
# Evaluate the rules in static mode.
|
210
|
+
# Rules that cannot be evaluated are skipped.
|
211
|
+
# * +rules+ array of symbols
|
212
|
+
# Throws a SecurityViolationError if a rule fails,
|
213
|
+
# returns true if all rules succeed.
|
214
|
+
def evaluate_statically(rules) #:nodoc:
|
215
|
+
static_policy.evaluate_statically(rules)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Evaluate the rules in dynamic mode.
|
219
|
+
# Rules that cannot be evaluated are skipped.
|
220
|
+
# * +rules+ array of symbols
|
221
|
+
# Throws a SecurityViolationError if a rule fails,
|
222
|
+
# returns true if all rules succeed.
|
223
|
+
def evaluate_dynamically(rules) #:nodoc:
|
224
|
+
rules.each do |rule|
|
225
|
+
if self.class.has_dynamic_rule?(rule) && !__send__(rule)
|
226
|
+
raise_access_denied(rule)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
true
|
230
|
+
end
|
231
|
+
|
232
|
+
# Evalutates all rules.
|
233
|
+
# * +rules+ array of symbols
|
234
|
+
# Throws a SecurityViolationError if a rule fails,
|
235
|
+
# returns true if all rules succeed.
|
236
|
+
def evaluate(rules) #:nodoc:
|
237
|
+
rules.each do |rule|
|
238
|
+
unless __send__(rule)
|
239
|
+
raise_access_denied(rule)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns true iif this policy can evaluate this rule
|
246
|
+
# * +symbol+ Name of the rule
|
247
|
+
def has_rule?(symbol)
|
248
|
+
self.class.has_rule? symbol
|
249
|
+
end
|
250
|
+
|
251
|
+
# Returns a rule object or raises an exception.
|
252
|
+
# * +symbol+ Name of the rule
|
253
|
+
def get_rule!(symbol) # :nodoc:
|
254
|
+
get_rule(symbol) or raise_rule_missing(symbol)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns a rule object or nil if it does not exist
|
258
|
+
# * +symbol+ Name of the rule
|
259
|
+
def get_rule(symbol) # :nodoc:
|
260
|
+
self.class.get_rule(symbol)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns a list of user wrappers for a role.
|
264
|
+
# See #all_for_role for details.
|
265
|
+
# * +symbol+ Name of the role
|
266
|
+
# * +require_user+ (boolean) Indicating if the rule that requested the roles
|
267
|
+
# requires a user for evaluation. If @user is nil and +require_user+ is
|
268
|
+
# true, an empty array is returned, which will make the rule fail
|
269
|
+
# immediately. If +require_user+ is false, an array containing nil is
|
270
|
+
# returned and the rule will be evaluated once (with +nil+ as current user).
|
271
|
+
def user_roles(symbol,require_user) # :nodoc:
|
272
|
+
return [nil] if @user.nil? && !require_user
|
273
|
+
# AnnotationSecurity::UserWrapper.all_for_role(@user,symbol)
|
274
|
+
all_for_role(@user,symbol)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Return objects for the requested role. The role(s) will be
|
278
|
+
# determined with sending user.as_'role'.
|
279
|
+
# (Normally a user has a role only once, however it will work when he
|
280
|
+
# has many roles of the same kind as well)
|
281
|
+
def all_for_role(user,role_name) # :nodoc:
|
282
|
+
return [] if user.nil?
|
283
|
+
# is it possible that user is a role? if so, implement conversion to user
|
284
|
+
return [user] if role_name.nil?
|
285
|
+
roles = user.__send__("as_#{role_name}")
|
286
|
+
return [] if roles.blank?
|
287
|
+
roles = [roles] unless roles.is_a?(Array)
|
288
|
+
roles.compact
|
289
|
+
end
|
290
|
+
|
291
|
+
# Evaluate a rule that is defined with a proc
|
292
|
+
# * +symbol+ Name of the rule
|
293
|
+
# * +user+ user object that has to fulfill the rule
|
294
|
+
# * +args+ List of additional arguments
|
295
|
+
def evaluate_rule(symbol,user,args) # :nodoc:
|
296
|
+
get_rule!(symbol).evaluate(self,user,@resource,*args)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Invoked by ruby when this object recieved a message it cannot handle.
|
300
|
+
# * +symbol+ Name of the method
|
301
|
+
# * +args+ Any arguments that were passed
|
302
|
+
def method_missing(symbol,*args) # :nodoc:
|
303
|
+
|
304
|
+
# If possible, create the missing method and send it again
|
305
|
+
if add_method_for_rule(symbol)
|
306
|
+
# method was created, try again
|
307
|
+
return __send__(symbol,*args)
|
308
|
+
end
|
309
|
+
|
310
|
+
# this will fail and an exception will be raised
|
311
|
+
get_rule!(symbol)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Return true if it was possible to create a method for the rule
|
315
|
+
# * +symbol+ Name of the method
|
316
|
+
def add_method_for_rule(symbol) # :nodoc:
|
317
|
+
# Check if symbol is a known rule. If available, it will be loaded and
|
318
|
+
# a method will be created automatically.
|
319
|
+
if has_rule?(symbol)
|
320
|
+
# method was created
|
321
|
+
return true
|
322
|
+
else
|
323
|
+
# Remove prefix or suffix if available
|
324
|
+
cleaned = AnnotationSecurity::Utils.method_body(symbol)
|
325
|
+
if cleaned
|
326
|
+
# Redirect to the cleaned method
|
327
|
+
self.class.class_eval "def #{symbol}(*args); #{cleaned}(*args); end"
|
328
|
+
return true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
# Hopeless
|
332
|
+
false
|
333
|
+
end
|
334
|
+
|
335
|
+
# Raises an error saying that a rule could not be found this policy class
|
336
|
+
# * +symbol+ Name of the rule
|
337
|
+
def raise_rule_missing(symbol) # :nodoc:
|
338
|
+
raise AnnotationSecurity::RuleNotFoundError.for_rule(symbol,self.class)
|
339
|
+
end
|
340
|
+
|
341
|
+
def raise_access_denied(rule) #:nodoc:
|
342
|
+
SecurityContext.raise_access_denied(rule,resource_type,@resource)
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# = lib/annotation_security/policy/abstract_static_policy.rb
|
3
|
+
#
|
4
|
+
|
5
|
+
# Abstract superclass for all static policies.
|
6
|
+
# For each policy there is a static policy that is responsible for evaluating
|
7
|
+
# static rules.
|
8
|
+
#
|
9
|
+
class AnnotationSecurity::AbstractStaticPolicy < AnnotationSecurity::AbstractPolicy # :nodoc:
|
10
|
+
|
11
|
+
# Rules that are defined for all resource types can be found here.
|
12
|
+
def self.all_resources_policy # :nodoc:
|
13
|
+
AllResourcesPolicy.static_policy_class
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets the dynamic policy class this policy class belongs to
|
17
|
+
def self.belongs_to(dynamic_policy_class) #:nodoc:
|
18
|
+
@dynamic_policy_class = dynamic_policy_class
|
19
|
+
end
|
20
|
+
|
21
|
+
# A static policy class has no other corresponding static policy class.
|
22
|
+
# This should never be called.
|
23
|
+
def self.static_policy_class #:nodoc:
|
24
|
+
method_missing(:static_policy_class)
|
25
|
+
end
|
26
|
+
|
27
|
+
# The corresponding dynamic policy class.
|
28
|
+
#
|
29
|
+
def self.dynamic_policy_class #:nodoc:
|
30
|
+
@dynamic_policy_class
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns true iif this is policy class is responsible for static rules.
|
34
|
+
#
|
35
|
+
def self.static? # :nodoc:
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Rule set for this classes resource type
|
40
|
+
#
|
41
|
+
def self.rule_set # :nodoc:
|
42
|
+
# Each dynamic and static policy pair shares one rule set.
|
43
|
+
dynamic_policy_class.rule_set
|
44
|
+
end
|
45
|
+
|
46
|
+
# If possible, redirects the rule to the static side.
|
47
|
+
# Returns a rule object or nil.
|
48
|
+
def self.use_static_rule(symbol) #:nodoc:
|
49
|
+
nil # This is not possible
|
50
|
+
end
|
51
|
+
|
52
|
+
# Evaluate the rules in static mode.
|
53
|
+
# Rules that cannot be evaluated are skipped.
|
54
|
+
# * +rules+ array of symbols
|
55
|
+
# Throws a SecurityViolationError if a rule fails,
|
56
|
+
# returns true if all rules succeed.
|
57
|
+
def evaluate_statically(rules) #:nodoc:
|
58
|
+
rules.each do |rule|
|
59
|
+
if has_rule?(rule) && !__send__(rule)
|
60
|
+
raise_access_denied(rule)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Evaluate a rule that is defined with a proc
|
67
|
+
# * +symbol+ Name of the rule
|
68
|
+
# * +user+ user object that has to fulfill the rule
|
69
|
+
# * +args+ List of additional arguments
|
70
|
+
def evaluate_rule(rule,user,args) #:nodoc:
|
71
|
+
# In contrast to AbstractPolicy#evaluate_rule,
|
72
|
+
# no resource is passed as argument
|
73
|
+
get_rule!(rule).evaluate(self,user,*args)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# = lib/annotation_security/policy/all_resources_policy.rb
|
3
|
+
#
|
4
|
+
# By default, two relations are provided for all resources.
|
5
|
+
#
|
6
|
+
# The system relation +logged_in+ evaluates to true if the provided
|
7
|
+
# credentials are not nil.
|
8
|
+
# logged_in(:system, :require_credential => false) {|u| not u.nil?}
|
9
|
+
#
|
10
|
+
# The relation +self+ is true when the accessed resource is the current user
|
11
|
+
# himself or a role that belongs to the current user.
|
12
|
+
# __self__ { |user, resource| resource.is_user?(user) }
|
13
|
+
#
|
14
|
+
AnnotationSecurity.define_relations :all_resources do
|
15
|
+
|
16
|
+
# can be used as "self" in a right definition
|
17
|
+
# success if the accessed resource is the user himself or one of his roles
|
18
|
+
__self__ { |user, resource| resource.is_user?(user) }
|
19
|
+
|
20
|
+
logged_in(:system, :require_credential => false) {|u| not u.nil?}
|
21
|
+
end
|