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