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.
Files changed (62) hide show
  1. data/CHANGELOG +2 -0
  2. data/HOW-TO +261 -0
  3. data/MIT-LICENSE +18 -0
  4. data/README +39 -0
  5. data/Rakefile +56 -0
  6. data/assets/app/helpers/annotation_security_helper.rb +9 -0
  7. data/assets/config/initializers/annotation_security.rb +12 -0
  8. data/assets/config/security/relations.rb +20 -0
  9. data/assets/config/security/rights.yml +16 -0
  10. data/assets/vendor/plugins/annotation_security/init.rb +14 -0
  11. data/bin/annotation_security +8 -0
  12. data/lib/annotation_security/exceptions.rb +125 -0
  13. data/lib/annotation_security/exec.rb +189 -0
  14. data/lib/annotation_security/filters.rb +38 -0
  15. data/lib/annotation_security/includes/action_controller.rb +144 -0
  16. data/lib/annotation_security/includes/active_record.rb +28 -0
  17. data/lib/annotation_security/includes/helper.rb +215 -0
  18. data/lib/annotation_security/includes/resource.rb +85 -0
  19. data/lib/annotation_security/includes/role.rb +31 -0
  20. data/lib/annotation_security/includes/user.rb +27 -0
  21. data/lib/annotation_security/manager/policy_factory.rb +30 -0
  22. data/lib/annotation_security/manager/policy_manager.rb +80 -0
  23. data/lib/annotation_security/manager/relation_loader.rb +273 -0
  24. data/lib/annotation_security/manager/resource_manager.rb +36 -0
  25. data/lib/annotation_security/manager/right_loader.rb +88 -0
  26. data/lib/annotation_security/model_observer.rb +61 -0
  27. data/lib/annotation_security/policy/abstract_policy.rb +345 -0
  28. data/lib/annotation_security/policy/abstract_static_policy.rb +76 -0
  29. data/lib/annotation_security/policy/all_resources_policy.rb +21 -0
  30. data/lib/annotation_security/policy/rule.rb +340 -0
  31. data/lib/annotation_security/policy/rule_set.rb +139 -0
  32. data/lib/annotation_security/rails.rb +39 -0
  33. data/lib/annotation_security/user_wrapper.rb +74 -0
  34. data/lib/annotation_security/utils.rb +142 -0
  35. data/lib/annotation_security.rb +98 -0
  36. data/lib/extensions/action_controller.rb +33 -0
  37. data/lib/extensions/active_record.rb +35 -0
  38. data/lib/extensions/filter.rb +134 -0
  39. data/lib/extensions/object.rb +11 -0
  40. data/lib/security_context.rb +551 -0
  41. data/spec/annotation_security/exceptions_spec.rb +17 -0
  42. data/spec/annotation_security/includes/helper_spec.rb +82 -0
  43. data/spec/annotation_security/manager/policy_manager_spec.rb +15 -0
  44. data/spec/annotation_security/manager/resource_manager_spec.rb +17 -0
  45. data/spec/annotation_security/manager/right_loader_spec.rb +17 -0
  46. data/spec/annotation_security/policy/abstract_policy_spec.rb +17 -0
  47. data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -0
  48. data/spec/annotation_security/policy/rule_set_spec.rb +112 -0
  49. data/spec/annotation_security/policy/rule_spec.rb +78 -0
  50. data/spec/annotation_security/policy/test_policy_spec.rb +81 -0
  51. data/spec/annotation_security/security_context_spec.rb +78 -0
  52. data/spec/annotation_security/utils_spec.rb +74 -0
  53. data/spec/helper/test_controller.rb +66 -0
  54. data/spec/helper/test_helper.rb +5 -0
  55. data/spec/helper/test_relations.rb +7 -0
  56. data/spec/helper/test_resource.rb +39 -0
  57. data/spec/helper/test_rights.yml +5 -0
  58. data/spec/helper/test_role.rb +22 -0
  59. data/spec/helper/test_user.rb +32 -0
  60. data/spec/rails_stub.rb +38 -0
  61. data/spec/spec_helper.rb +43 -0
  62. metadata +157 -0
@@ -0,0 +1,31 @@
1
+ #
2
+ # = lib/annotation_security/includes/role.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::Role
6
+ #
7
+ # This module should be included by all role classes
8
+ # to enable full support of all features.
9
+ #
10
+ # A role class is a domain class that represents user roles
11
+ # and does not extend the user class. It should have the method #user that
12
+ # returns the user object it belongs to.
13
+ #
14
+ module AnnotationSecurity::Role
15
+
16
+ # Returns true if this belongs to the user given as parameter.
17
+ #
18
+ # Required to have a common interface with AnnotationSecurity::User.
19
+ #
20
+ def is_user?(user)
21
+ self.user == user
22
+ end
23
+
24
+ # If +obj+ is a UserWrapper, extract the role before comparing
25
+ #
26
+ def ==(obj)
27
+ obj = obj.__role__ if obj.is_a? AnnotationSecurity::UserWrapper
28
+ super(obj)
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # = lib/annotation_security/includes/user.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::User
6
+ #
7
+ # This module should be included by the user domain class to
8
+ # enable full support of all features.
9
+ #
10
+ module AnnotationSecurity::User
11
+
12
+ # Returns true if this is the user given as parameter.
13
+ #
14
+ # Required to have a common interface with AnnotationSecurity::Role.
15
+ #
16
+ def is_user?(user)
17
+ self == user
18
+ end
19
+
20
+ # If +obj+ is a UserWrapper, extract the user before comparing
21
+ #
22
+ def ==(obj)
23
+ obj = obj.__user__ if obj.is_a? AnnotationSecurity::UserWrapper
24
+ super(obj)
25
+ end
26
+
27
+ end
@@ -0,0 +1,30 @@
1
+ #
2
+ # = lib/annotation_security/manager/policy_factory.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::PolicyFactory
6
+ # Builds the policy classes.
7
+ #
8
+ class AnnotationSecurity::PolicyFactory # :nodoc:
9
+
10
+ def initialize(resource_class)
11
+ @klass = AnnotationSecurity::AbstractPolicy.new_subclass(resource_class)
12
+ end
13
+
14
+ def policy_class
15
+ @klass
16
+ end
17
+
18
+ def add_rule(symbol,*args,&block)
19
+ @klass.add_rule(symbol,*args,&block)
20
+ end
21
+
22
+ def create_policy(*args)
23
+ @klass.new(*args)
24
+ end
25
+
26
+ def reset
27
+ @klass.reset
28
+ end
29
+
30
+ end
@@ -0,0 +1,80 @@
1
+ #
2
+ # = lib/annotation_security/manager/policy_manager.rb
3
+ #
4
+ require 'yaml'
5
+
6
+ # = AnnotationSecurity::PolicyManager
7
+ #
8
+ # Manages loading and creation of all policy classes.
9
+ #
10
+ class AnnotationSecurity::PolicyManager # :nodoc:
11
+
12
+ # Get the policy factory for a resource class
13
+ def self.policy_factory(resource_type) # :nodoc:
14
+ policy_factories[resource_type.to_sym]
15
+ end
16
+
17
+ # Creates a policy object for a user and a resource type
18
+ #
19
+ # ==== Example
20
+ #
21
+ # picture = Picture.find_by_id(params[:picture])
22
+ # policy = PolicyManager.get_policy(:picture,@current_user)
23
+ # policy.allowed? :show, picture # => true or false
24
+ #
25
+ def self.create_policy(resource_type,*args)
26
+ policy_factory(resource_type).create_policy(*args)
27
+ end
28
+
29
+ def self.policy_class(resource_class) # :nodoc:
30
+ policy_factory(resource_class).policy_class
31
+ end
32
+
33
+ def self.config_files # :nodoc:
34
+ @files ||= []
35
+ end
36
+
37
+ # Adds a file that contains security configurations
38
+ # * +f+ file name
39
+ # * +ext+ 'yml' or 'rb'
40
+ def self.add_file(f,ext) # :nodoc:
41
+ unless config_files.include? [f,ext]
42
+ config_files.push [f,ext]
43
+ load_file(f,ext)
44
+ end
45
+ end
46
+
47
+ def self.reset
48
+ policy_factories.each_value(&:reset)
49
+ config_files.each { |f,ext| load_file(f,ext) }
50
+ end
51
+
52
+ private
53
+
54
+ def self.load_file(f,ext)
55
+ fname = get_file_name(f,ext)
56
+ case ext
57
+ when 'yml'
58
+ AnnotationSecurity::RightLoader.define_rights(YAML.load_file(fname))
59
+ when 'rb'
60
+ load fname
61
+ end
62
+ end
63
+
64
+ SEARCH_PATH = ['', RAILS_ROOT, RAILS_ROOT + '/config/security/',
65
+ RAILS_ROOT + '/config/', RAILS_ROOT + '/security/']
66
+
67
+ def self.get_file_name(f,ext)
68
+ SEARCH_PATH.each do |fname1|
69
+ [f, f+'.'+ext].each do |fname2|
70
+ return (fname1 + fname2) if File.exist?(fname1 + fname2)
71
+ end
72
+ end
73
+ raise "File not found: '#{f+'.'+ext}'"
74
+ end
75
+
76
+ def self.policy_factories
77
+ # Create a new factory if it is needed
78
+ @factories ||= Hash.new { |h,k| h[k] = AnnotationSecurity::PolicyFactory.new(k) }
79
+ end
80
+ end
@@ -0,0 +1,273 @@
1
+ #
2
+ # = lib/annotation_security/manager/relation_loader.rb
3
+ #
4
+
5
+ # Class responsible for loading the relation definitions for resources.
6
+ #
7
+ # == Defining a relation for a resource
8
+ #
9
+ # This example defines the owner relation between a picture and a user.
10
+ # A relation definition is a proc that returns true if the relation exists.
11
+ # All three examples are equivalent. However, in most cases the first way is
12
+ # the way you want to use.
13
+ # AnnotationSecurity.define_relations do
14
+ # resource :picture do
15
+ # owner { |user,picture| picture.user == user }
16
+ # end
17
+ # end
18
+ #
19
+ # If you need only one relation for a resource class, use this example:
20
+ # AnnotationSecurity.define_relations do
21
+ # picture.owner { |user,picture| picture.user == user }
22
+ # end
23
+ #
24
+ # If the entire file contains definitions for only one resource class,
25
+ # you might try this:
26
+ # AnnotationSecurity.define_relations :picture do
27
+ # owner { |user,picture| picture.user == user }
28
+ # end
29
+ #
30
+ # === Defining a relation for many resources
31
+ #
32
+ # Use +resources+ to define a relation once for more than one resource class.
33
+ # AnnotationSecurity.define_relations do
34
+ # resources(:picture, :comment) do
35
+ # owner { |user,res| res.user == user }
36
+ # end
37
+ # end
38
+ # As for one resource, you can also use
39
+ # AnnotationSecurity.define_relations do
40
+ # resources(:picture, :comment).owner { |user,res| res.user == user }
41
+ # end
42
+ # or
43
+ # AnnotationSecurity.define_relations(:picture, :comment) do
44
+ # owner { |user,res| res.user == user }
45
+ # end
46
+ #
47
+ # It is also possible to define relations for all resources:
48
+ # AnnotationSecurity.define_relations do
49
+ # all_resources do
50
+ # related { owner or friend_of_owner }
51
+ # end
52
+ # end
53
+ # or
54
+ # AnnotationSecurity.define_relations do
55
+ # all_resources.related { owner or friend_of_owner }
56
+ # end
57
+ #
58
+ # Notice that +owner+ and +friend_of_owner+ are relations that can be defined
59
+ # individually for each resource. The 2 parameters +user+ and +resource_object+
60
+ # dont need to be specified if they are not used.
61
+ #
62
+ # == Details on defining a relation
63
+ #
64
+ # As you have seen, the default way to define a relation is using a proc,
65
+ # like
66
+ # owner { |user,picture| picture.user == user }
67
+ # related { owner or friend_of_owner }
68
+ #
69
+ # If the condition is simple and uses only other relations,
70
+ # it also can be specified by a string:
71
+ # related 'if owner or friend_of_owner'
72
+ #
73
+ # === Implicit conditions
74
+ # Besides a string or a proc, a rule definition can contain a list of flags
75
+ # and an options-hash.
76
+ #
77
+ # ==== The :is option
78
+ #
79
+ # A Relation to which the <tt>:is => symbol</tt> option is passed as a parameter
80
+ # only exists if the relation exists and <tt>is_symbol?</tt> invoked on the
81
+ # current user evaluates to true.
82
+ #
83
+ # Let the user class have a method <tt>is_super_user?</tt>, which returns true
84
+ # or false, depending on wheter the user is a super user. This method can be
85
+ # used for defining a relation +super_owner+, that is true if the user is the
86
+ # owner and a super user.
87
+ #
88
+ # owner { |user,picture| picture.user == user }
89
+ # super_owner(:is => :super_user) "if owner"
90
+ #
91
+ # super_user(:system, :is => :super_user)
92
+ #
93
+ # ==== The :as option
94
+ #
95
+ # For a relation to which the <tt>:as => symbol</tt> option is passed as a
96
+ # parameter the current user is replaced by the invocation of
97
+ # <tt>current_credential.as_[symbol]</tt>. The method invocation may return +nil+
98
+ # indicating that the transformation failed. In this case the relation for
99
+ # which <tt>:as => ..</tt> was specified does not exist.
100
+ #
101
+ # ==== :require_credential
102
+ # By default, a relation requires a user to be executed. Therefore, rights will
103
+ # always fail if the user is nil. To enable rights like 'unless logged_in', the
104
+ # :require_credential option can be set to false.
105
+ # logged_in(:system, :require_credential => false) { |user| not user.nil? }
106
+ #
107
+ # === Evaluation time
108
+ # While most relations are between the user and a resource object, some are
109
+ # beween the user and an entire class of objects. This means that no instance of
110
+ # a resource is required to tell whether the user has that relation or not.
111
+ #
112
+ # ==== The :resource flag
113
+ # This flag is set by default. It is set for relations that need a resource.
114
+ #
115
+ # owner { |user,picture| picture.user == user }
116
+ # # is short for
117
+ # # owner(:resource) { |user,picture| picture.user == user }
118
+ #
119
+ # ==== The :system flag
120
+ # You can use the :system flag to denote that a relation does not
121
+ # require a resource object.
122
+ #
123
+ # all_resources do
124
+ # super_user(:system, :is => :super_user)
125
+ # end
126
+ #
127
+ # It is possible to define system relations only for certain resources, and they
128
+ # do not conflict with resource relations.
129
+ #
130
+ # resource :present do
131
+ # receiver(:system) { |user| user.was_good? }
132
+ # receiver { |user,present| present.receiver == user }
133
+ # end
134
+ #
135
+ # The advantage of system relations is that they improve the rights evaluation.
136
+ # Consider the right
137
+ # present:
138
+ # receive: if receiver
139
+ #
140
+ # If an action is invoked requiring the receive-present right,
141
+ # AnnotationSecurity will evaluate the system relation before even entering the
142
+ # action, thus improving the fail fast behavior and avoiding unnecessary
143
+ # operations.
144
+ #
145
+ # Once a present object is observed during the action, the resource relation
146
+ # will be evaluated as well.
147
+ #
148
+ # ==== The :pretest flag
149
+ #
150
+ # Using the :pretest flag, it is possible to define both resource and system
151
+ # relations in one block.
152
+ #
153
+ # resource :present do
154
+ # receiver(:pretest) do |user, present|
155
+ # if present
156
+ # present.receiver == user
157
+ # else
158
+ # user.was_good?
159
+ # end
160
+ # end
161
+ # end
162
+ #
163
+ # This can be helpfull if your relation depends on other relations, where a
164
+ # resource and a system version is available.
165
+ #
166
+ # all_resources do
167
+ # responsible(:pretest) { lecturer or corrector }
168
+ # lecturer(:system, :as => :lecturer)
169
+ # corrector(:system, :as => :corrector)
170
+ # end
171
+ #
172
+ # resource :course do
173
+ # lecturer(:as => :lecturer) { |lecturer, course| course.lecturers.include? lecturer }
174
+ # corrector(:as => :corrector) { |corrector, course| course.correctors.include? corrector }
175
+ # end
176
+ # # For other resources, lecturer and corrector are defined differently
177
+ #
178
+ # === Defining relations as strings
179
+ #
180
+ # Instead of a block, a string can be used to define the relation.
181
+ # responsible :pretest, "if lecturer or corrector"
182
+ #
183
+ # The string syntax provides more simplifications, like referring to relations
184
+ # of other resources.
185
+ #
186
+ # This example will evaluate the course-correction relation for the course
187
+ # property of an assignment resource.
188
+ # resource :assignment do
189
+ # corrector "if course.corrector: course"
190
+ # end
191
+ #
192
+ # As the course class includes AnnotationSecurity::Resource, the resource type
193
+ # is not explicitly needed.
194
+ # resource :assignment_result do
195
+ # corrector "if corrector: assignment.course"
196
+ # end
197
+ #
198
+ #
199
+ class AnnotationSecurity::RelationLoader
200
+
201
+ # Load relations of the +block+
202
+ # * +resources+ (optional) list of resources
203
+ # * +block+ block with relation definitions
204
+ def self.define_relations(*resources, &block)
205
+ if resources.blank?
206
+ class_eval(&block)
207
+ else
208
+ resources(*resources,&block)
209
+ end
210
+ end
211
+
212
+ #
213
+ def self.method_missing(symbol,*args,&block) #:nodoc:
214
+ return super unless args.empty?
215
+ resources(symbol,&block)
216
+ end
217
+
218
+ # Defines relations for a resource
219
+ # * +block+ (optional) proc with relation definitions
220
+ def self.resource(resource,&block)
221
+ resources(resource,&block)
222
+ end
223
+
224
+ # Defines relations for a list of resources
225
+ # * +block+ (optional) proc with relation definitions
226
+ def self.resources(*resources,&block)
227
+ new(resources,&block)
228
+ end
229
+
230
+ # Defines relations for all resources
231
+ # * +block+ (optional) proc with relation definitions
232
+ def self.all_resources(&block)
233
+ resources(:all_resources,&block)
234
+ end
235
+
236
+ ## ===========================================================================
237
+ ## Instance
238
+
239
+ # An instance of RelationLoader is responsible for loading the relations
240
+ # for a set of resources.
241
+ def initialize(resources,&block) #:nodoc:
242
+ @factories = get_factories_for(resources)
243
+ instance_eval(&block) if block
244
+ end
245
+
246
+ # if a method is missing this will be a new relation for the resource class
247
+ def method_missing(symbol,*args,&block) #:nodoc:
248
+ define_relation(symbol,*args,&block)
249
+ end
250
+
251
+ # Defines a new relation for the current resources. However, instead of using
252
+ # define_relation(:relation_name,args) { |user,res| some_condition }
253
+ # it is recommended to use
254
+ # relation_name(args) { |user,res| some_condition }
255
+ #
256
+ # ==== Parameters
257
+ # * +symbol+ name of the relation
258
+ # * +args+ additonal arguments, see AnnotationSecurity::Rule for details
259
+ # * +block+ (optional) The condition can be passed either as string or as proc
260
+ #
261
+ def define_relation(symbol,*args,&block)
262
+ @factories.each do |factory|
263
+ factory.add_rule(symbol,*args,&block)
264
+ end
265
+ end
266
+
267
+ private
268
+
269
+ def get_factories_for(resources)
270
+ resources.collect{ |res| AnnotationSecurity::PolicyManager.policy_factory(res) }
271
+ end
272
+
273
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # = lib/annotation_security/manager/resource_manager.rb
3
+ #
4
+
5
+ # Needed to find resource objects when only their id is known
6
+ #
7
+ class AnnotationSecurity::ResourceManager # :nodoc:
8
+
9
+ @classes = {}
10
+
11
+ def self.add_resource_class(res_type,klass)
12
+ @classes.delete_if { |k,v| v == klass }
13
+ @classes[res_type] = klass
14
+ end
15
+
16
+ def self.get_resource_class(res_type)
17
+ @classes[res_type]
18
+ end
19
+
20
+ unless RAILS_ENV == 'production'
21
+ def self.get_resource_class(res_type)
22
+ c = @classes[res_type]
23
+ unless c
24
+ res_type.to_s.camelize.constantize # load the class
25
+ c = @classes[res_type]
26
+ end
27
+ c
28
+ end
29
+ end
30
+
31
+ # Call get_resource of the class that is registered for +res_type+
32
+ def self.get_resource(res_type,object)
33
+ c = get_resource_class(res_type)
34
+ c ? c.get_resource(object) : object
35
+ end
36
+ end
@@ -0,0 +1,88 @@
1
+ #
2
+ # = lib/annotation_security/manager/right_loader.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::RightLoader
6
+ # Contains the right loader class, which is responsible for loading
7
+ # right definitions for resources. Load rights from a yaml file or a hash.
8
+ #
9
+ # == Example YAML
10
+ #
11
+ # The file <tt>config/security/rights.yml</tt> inside a rails app
12
+ # might look like this:
13
+ # picture:
14
+ # # a user may show a picture if he fulfils the 'related'-relation
15
+ # show: if related
16
+ # comment:
17
+ # # you have to be logged in to view comments
18
+ # show: if logged_in
19
+ # user:
20
+ # # like in ruby, 'unless' is equivalent to 'if not'
21
+ # register: unless logged_in
22
+ # delete: if administrator # comments are also possible behind a line
23
+ # user_content:
24
+ # # all rights of 'user_content' are defined for 'picture' and 'comment' too
25
+ # applies_to: picture, comment
26
+ # create: if logged_in
27
+ # edit: if owner
28
+ # delete: if owner or administrator
29
+ # The file can be loaded via <code>AnnotationSecurity#load_rights('rights')</code>.
30
+ #
31
+ # A right's condition can use the keywords +if+, +unless+, +and+, +or+ and
32
+ # +not+, brackets, other rights and all of the resource's relations
33
+ # (see AnnotationSecurity::RelationLoader). For better readability you may
34
+ # add the prefixes +may+, +is+, +can+ or +has+,
35
+ # or append one of the suffixes +for+, +in+, +of+ or +to+.
36
+ #
37
+ # user_content:
38
+ # edit: if is_owner_of
39
+ # delete: if may_edit or is_administrator
40
+ #
41
+ # However, it is recommended to use this feature sparingly.
42
+ #
43
+ class AnnotationSecurity::RightLoader
44
+
45
+ # Goes through all resources of +hash+ and load the defined rights.
46
+ #
47
+ def self.define_rights(hash) # :nodoc:
48
+ if hash
49
+ hash.each_pair do |resource_class, rights|
50
+ new(resource_class).define_rights(rights)
51
+ end
52
+ end
53
+ end
54
+
55
+ # An instance of RightLoader is responsible for loading the rights of a
56
+ # resource class.
57
+ #
58
+ def initialize(resource) # :nodoc:
59
+ @factory = AnnotationSecurity::PolicyManager.policy_factory(resource)
60
+ end
61
+
62
+ # Goes through all rights in +hash+ and creates rules for all policies these
63
+ # rights apply to.
64
+ #
65
+ def define_rights(hash) # :nodoc:
66
+ factories = extract_applies_to(hash) << @factory
67
+ hash.each_pair do |right,condition|
68
+ # Important: set the :right-flag to activate automatic detection of
69
+ # the other flags (static,dynamic,require_user)
70
+ factories.each { |f| f.add_rule(right,:right,condition) }
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ # Looks for the key 'applies_to', which is no right but a command to apply
77
+ # all rights of the current resource class to the resource classes listed
78
+ # in the value.
79
+ #
80
+ def extract_applies_to(hash)
81
+ applies_to = hash.delete('applies_to') || hash.delete(:applies_to)
82
+ return [] if applies_to.blank?
83
+ applies_to = [applies_to] if applies_to.is_a? String
84
+ applies_to.collect{ |r| r.split(',') }.flatten.
85
+ collect{ |r| AnnotationSecurity::PolicyManager.policy_factory(r.strip) }
86
+ end
87
+
88
+ end
@@ -0,0 +1,61 @@
1
+ #
2
+ # = lib/annotation_security/model_observer.rb
3
+ #
4
+ # Contains SecurityObserver which implements constraint checking for model
5
+ # classes.
6
+ #
7
+
8
+ module AnnotationSecurity
9
+
10
+ # Observes changes in models and applies security policy to them
11
+ #
12
+ class ModelObserver < ::ActiveRecord::Observer # :nodoc:
13
+
14
+ # Sets the observed model classes
15
+ #
16
+ observe # will be set automatically. However, observe must not be removed
17
+
18
+ def before_validation_on_create(record)
19
+ SecurityContext.observe record
20
+ end
21
+
22
+ def before_validation_on_update(record)
23
+ SecurityContext.observe record
24
+ end
25
+
26
+ # after_find is removed in favour of after_initialize
27
+
28
+ def after_initialize(record)
29
+ if record.new_record?
30
+ # The record is new
31
+ else
32
+ # The record came out of database
33
+ SecurityContext.observe record
34
+ end
35
+ end
36
+
37
+ def before_destroy(record)
38
+ SecurityContext.observe record
39
+ end
40
+
41
+ # Re-register on classes you are observing
42
+ # See http://riotprojects.com/2009/1/18/active-record-observers-in-gems-plugins
43
+ #
44
+ def reload_model_observer
45
+ observed_classes.each do |klass|
46
+ add_observer!(klass.name.constantize)
47
+ end
48
+ end
49
+
50
+ protected
51
+
52
+ def add_observer!(klass)
53
+ klass.delete_observer(self)
54
+ super
55
+
56
+ if respond_to?(:after_initialize) && !klass.method_defined?(:after_initialize)
57
+ klass.class_eval 'def after_initialize() end'
58
+ end
59
+ end
60
+ end
61
+ end