trust 0.5.0
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/MIT-LICENSE +23 -0
- data/README.md +244 -0
- data/Rakefile +37 -0
- data/lib/tasks/trust_tasks.rake +42 -0
- data/lib/trust/active_record.rb +65 -0
- data/lib/trust/authorization.rb +85 -0
- data/lib/trust/controller/properties.rb +134 -0
- data/lib/trust/controller/resource.rb +306 -0
- data/lib/trust/controller.rb +197 -0
- data/lib/trust/exceptions.rb +45 -0
- data/lib/trust/inheritable_attribute.rb +91 -0
- data/lib/trust/permissions.rb +268 -0
- data/lib/trust/test_helper.rb +56 -0
- data/lib/trust/version.rb +27 -0
- data/lib/trust.rb +39 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/accounts.js +2 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/clients.js +2 -0
- data/test/dummy/app/assets/javascripts/users.js +2 -0
- data/test/dummy/app/assets/stylesheets/accounts.css +4 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/assets/stylesheets/clients.css +4 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/test/dummy/app/assets/stylesheets/users.css +4 -0
- data/test/dummy/app/controllers/accounts_controller.rb +100 -0
- data/test/dummy/app/controllers/application_controller.rb +31 -0
- data/test/dummy/app/controllers/clients_controller.rb +107 -0
- data/test/dummy/app/controllers/savings_accounts_controller.rb +27 -0
- data/test/dummy/app/controllers/settlements_controller.rb +26 -0
- data/test/dummy/app/controllers/users_controller.rb +107 -0
- data/test/dummy/app/helpers/accounts_helper.rb +26 -0
- data/test/dummy/app/helpers/application_helper.rb +26 -0
- data/test/dummy/app/helpers/clients_helper.rb +26 -0
- data/test/dummy/app/helpers/users_helper.rb +26 -0
- data/test/dummy/app/models/account/credit.rb +26 -0
- data/test/dummy/app/models/account.rb +35 -0
- data/test/dummy/app/models/client.rb +35 -0
- data/test/dummy/app/models/permissions.rb +68 -0
- data/test/dummy/app/models/savings_account.rb +26 -0
- data/test/dummy/app/models/user.rb +40 -0
- data/test/dummy/app/views/accounts/_form.html.erb +46 -0
- data/test/dummy/app/views/accounts/edit.html.erb +31 -0
- data/test/dummy/app/views/accounts/index.html.erb +48 -0
- data/test/dummy/app/views/accounts/new.html.erb +30 -0
- data/test/dummy/app/views/accounts/show.html.erb +35 -0
- data/test/dummy/app/views/clients/_form.html.erb +46 -0
- data/test/dummy/app/views/clients/edit.html.erb +31 -0
- data/test/dummy/app/views/clients/index.html.erb +48 -0
- data/test/dummy/app/views/clients/new.html.erb +30 -0
- data/test/dummy/app/views/clients/show.html.erb +35 -0
- data/test/dummy/app/views/layouts/application.html.erb +39 -0
- data/test/dummy/app/views/users/_form.html.erb +46 -0
- data/test/dummy/app/views/users/edit.html.erb +31 -0
- data/test/dummy/app/views/users/index.html.erb +48 -0
- data/test/dummy/app/views/users/new.html.erb +30 -0
- data/test/dummy/app/views/users/show.html.erb +35 -0
- data/test/dummy/config/application.rb +56 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +38 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20120522115011_create_accounts.rb +36 -0
- data/test/dummy/db/migrate/20120522130322_create_users.rb +33 -0
- data/test/dummy/db/migrate/20120523144144_create_clients.rb +34 -0
- data/test/dummy/db/schema.rb +38 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/fixtures/accounts.yml +7 -0
- data/test/dummy/test/fixtures/clients.yml +7 -0
- data/test/dummy/test/fixtures/users.yml +7 -0
- data/test/dummy/test/functional/accounts_controller_test.rb +123 -0
- data/test/dummy/test/functional/clients_controller_test.rb +74 -0
- data/test/dummy/test/functional/users_controller_test.rb +74 -0
- data/test/dummy/test/unit/account_test.rb +31 -0
- data/test/dummy/test/unit/client_test.rb +31 -0
- data/test/dummy/test/unit/helpers/accounts_helper_test.rb +28 -0
- data/test/dummy/test/unit/helpers/clients_helper_test.rb +28 -0
- data/test/dummy/test/unit/helpers/users_helper_test.rb +28 -0
- data/test/dummy/test/unit/permissions_test.rb +171 -0
- data/test/dummy/test/unit/user_test.rb +31 -0
- data/test/test_helper.rb +45 -0
- data/test/trust_test.rb +31 -0
- data/test/unit/trust/active_record_test.rb +56 -0
- data/test/unit/trust/authorization_test.rb +108 -0
- data/test/unit/trust/controller/properties_test.rb +132 -0
- data/test/unit/trust/controller/resource_test.rb +251 -0
- data/test/unit/trust/controller_test.rb +160 -0
- data/test/unit/trust/inheritable_attribute_test.rb +65 -0
- data/test/unit/trust/permissions_test.rb +258 -0
- metadata +280 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Copyright (c) 2012 Bingo Entreprenøren AS
|
|
2
|
+
# Copyright (c) 2012 Teknobingo Scandinavia AS
|
|
3
|
+
# Copyright (c) 2012 Knut I. Stenmark
|
|
4
|
+
# Copyright (c) 2012 Patrick Hanevold
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
module Trust
|
|
26
|
+
module Controller
|
|
27
|
+
class Resource
|
|
28
|
+
# = Trust::Controller::Resource
|
|
29
|
+
# Collects information about the current resource and relations.
|
|
30
|
+
# Handles the loading of the resource and its possible parent, i.e. setting the relevant instance variables
|
|
31
|
+
# It assumes the name of the resource is built on the controllers name, but this can be overridden in your
|
|
32
|
+
# controller by setting the +model+
|
|
33
|
+
#
|
|
34
|
+
# Examples:
|
|
35
|
+
#
|
|
36
|
+
# # controller name AccountsController
|
|
37
|
+
# resource.instance # => @account
|
|
38
|
+
#
|
|
39
|
+
# # controller name Customer::AccountsController
|
|
40
|
+
# resource.instance # => @customer_account
|
|
41
|
+
#
|
|
42
|
+
|
|
43
|
+
delegate :logger, :to => Rails
|
|
44
|
+
attr_reader :properties, :params, :action
|
|
45
|
+
attr_reader :info, :parent_info, :relation
|
|
46
|
+
|
|
47
|
+
def initialize(controller, properties, action_name, params, request) # nodoc
|
|
48
|
+
@action = action_name.to_sym
|
|
49
|
+
|
|
50
|
+
@controller, @properties, @params = controller, properties, params
|
|
51
|
+
@info = extract_resource_info(properties.model, params)
|
|
52
|
+
if properties.has_associations?
|
|
53
|
+
@parent_info = extract_parent_info(properties.associations, params, request)
|
|
54
|
+
end
|
|
55
|
+
@relation = @info.relation(@parent_info)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns the instance variable in the controller
|
|
59
|
+
def instance
|
|
60
|
+
@controller.instance_variable_get(:"@#{instance_name}")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Sets the instance variable
|
|
64
|
+
# Normally set by +load+
|
|
65
|
+
# You can access this method from the resource object.
|
|
66
|
+
#
|
|
67
|
+
# ==== Example
|
|
68
|
+
#
|
|
69
|
+
# resource.instance = Account.find_by_number(123456)
|
|
70
|
+
def instance=(instance)
|
|
71
|
+
@controller.instance_variable_set(:"@#{instance_name}", instance)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns the parameters for the instance
|
|
75
|
+
#
|
|
76
|
+
# ==== Example
|
|
77
|
+
#
|
|
78
|
+
# # in AccountsController
|
|
79
|
+
# resource.instance_params # same as params[:account]
|
|
80
|
+
def instance_params
|
|
81
|
+
info.params
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns the parents instance variable when you use +belongs_to+ for nested routes
|
|
85
|
+
def parent
|
|
86
|
+
@controller.instance_variable_get(:"@#{parent_name}")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Sets the parent instance variable
|
|
90
|
+
def parent=(instance)
|
|
91
|
+
@controller.instance_variable_set(:"@#{parent_name}", instance)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the cinstance variable for ollection
|
|
95
|
+
def instances
|
|
96
|
+
@controller.instance_variable_get(:"@#{plural_instance_name}")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Sets the instance variable for collection
|
|
100
|
+
# You may want to set this variable in your index action, we do not yet support loading of collections
|
|
101
|
+
def instances=(instances)
|
|
102
|
+
@controller.instance_variable_set(:"@#{plural_instance_name}", instances)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns either the instances or the instance.
|
|
106
|
+
# We have found that this can be useful in some implementation patterns
|
|
107
|
+
def instantiated
|
|
108
|
+
instances || instance
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns the class for the resource
|
|
112
|
+
def klass
|
|
113
|
+
info.klass
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Loads the resource
|
|
117
|
+
# See Trust::Controller::Properties which controls the behavior of this method.
|
|
118
|
+
# It will normally find the instance variable for existing object or initialize them as new.
|
|
119
|
+
# If using nested resources and +belongs_to+ has been declared in the controller it will use the
|
|
120
|
+
# parent relation if found.
|
|
121
|
+
def load
|
|
122
|
+
self.parent = parent_info.object if parent_info
|
|
123
|
+
if properties.new_actions.include?(action)
|
|
124
|
+
# logger.debug "Trust.load: Setting new: class: #{klass} info.params: #{info.params.inspect}"
|
|
125
|
+
self.instance ||= relation.new(info.params)
|
|
126
|
+
@controller.send(:build, action) if @controller.respond_to?(:build)
|
|
127
|
+
elsif properties.member_actions.include?(action)
|
|
128
|
+
# logger.debug "Trust.load: Finding parent: #{parent.inspect}, relation: #{relation.inspect}"
|
|
129
|
+
self.instance ||= relation.find(params[:id])
|
|
130
|
+
@controller.send(:build, action) if @controller.respond_to?(:build)
|
|
131
|
+
else # other outcome would be collection actions
|
|
132
|
+
# logger.debug "Trust.load: Parent is: #{parent.inspect}, collection or unknown action."
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Returns the name of the instance for the resource
|
|
137
|
+
# ==== Example
|
|
138
|
+
#
|
|
139
|
+
# # in AccountsController
|
|
140
|
+
# resource.instance_name # => :account
|
|
141
|
+
def instance_name
|
|
142
|
+
info.name
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Returns the plural name of the instance for the resource
|
|
146
|
+
# ==== Example
|
|
147
|
+
#
|
|
148
|
+
# # in AccountsController
|
|
149
|
+
# resource.plural_instance_name # => :accounts
|
|
150
|
+
def plural_instance_name
|
|
151
|
+
info.plural_name
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns the name of the parent resource
|
|
155
|
+
# ==== Example
|
|
156
|
+
#
|
|
157
|
+
# # in AccountsController where belongs_to :customer has been declared
|
|
158
|
+
# resource.parent_name # => :customer
|
|
159
|
+
def parent_name
|
|
160
|
+
parent_info && parent_info.name
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
def extract_resource_info(model, params) # nodoc
|
|
166
|
+
ResourceInfo.new(model, params)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def extract_parent_info(associations, params, request) #nodoc
|
|
170
|
+
ParentInfo.new(associations, params, request)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# ResorceInfo resolves information about the resource accessed in action controller
|
|
175
|
+
#
|
|
176
|
+
# Examples in PeopleController (simple case)
|
|
177
|
+
# ===
|
|
178
|
+
# resource.info.klass => Person
|
|
179
|
+
# resource.info.params => {:person => {...}} # fetches the parameters for the resource
|
|
180
|
+
# resource.info.name => :person
|
|
181
|
+
# resource.info.plural_name => :people
|
|
182
|
+
# resource.info.path => 'people' # this is the controller_path
|
|
183
|
+
#
|
|
184
|
+
# Examples in Lottery::AssignmentsController (with name space)
|
|
185
|
+
# ===
|
|
186
|
+
# resource.info.klass => Lottery::Assignment
|
|
187
|
+
# resource.info.params => {:lottery_assignment => {...}}
|
|
188
|
+
# resource.info.name => :lottery_assignment
|
|
189
|
+
# resource.info.plural_name => :lottery_assignments
|
|
190
|
+
# resource.info.path => 'lottery/assignments' # this is the controller_path
|
|
191
|
+
#
|
|
192
|
+
# Examples in ArchiveController (with inheritance)
|
|
193
|
+
# Assumptions on routes:
|
|
194
|
+
# resources :archives
|
|
195
|
+
# resources :secret_acrvives, :controller => :archives
|
|
196
|
+
# resources :public_acrvives, :controller => :archives
|
|
197
|
+
# examples below assumes that the route secret_arcives is being accessed at the moment
|
|
198
|
+
# ===
|
|
199
|
+
# resource.info.klass => Archive
|
|
200
|
+
# resource.info.params => {:secret_archive => {...}}
|
|
201
|
+
# resource.info.name => :archive
|
|
202
|
+
# resource.info.plural_name => :archives
|
|
203
|
+
# resource.info.path => 'archive' # this is the controller_path
|
|
204
|
+
# resource.info.real_class => SecretArchive # Returns the real class which is accessed at the moment
|
|
205
|
+
#
|
|
206
|
+
|
|
207
|
+
class Resource::Info
|
|
208
|
+
attr_reader :klass, :params, :name, :path, :real_class
|
|
209
|
+
|
|
210
|
+
def params
|
|
211
|
+
@data
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
protected
|
|
215
|
+
def self.var_name(klass)
|
|
216
|
+
klass.to_s.underscore.tr('/','_').to_sym
|
|
217
|
+
end
|
|
218
|
+
def var_name(klass)
|
|
219
|
+
self.class.var_name(klass)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Resource::ResourceInfo < Resource::Info
|
|
225
|
+
|
|
226
|
+
def initialize(model, params)
|
|
227
|
+
@path, params = model, params
|
|
228
|
+
@klass = model.to_s.classify.constantize
|
|
229
|
+
@name = model.to_s.singularize.underscore.gsub('/','_').to_sym
|
|
230
|
+
ptr = @klass.descendants.detect do |c|
|
|
231
|
+
params.key? var_name(c)
|
|
232
|
+
end || @klass
|
|
233
|
+
@real_class = ptr
|
|
234
|
+
@data = params[var_name(ptr)]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def plural_name
|
|
238
|
+
@plural_name ||= path.underscore.tr('/','_').to_sym
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# returns an accessor for association. Tries with full name association first, and if that does not match, tries the demodularized association.
|
|
242
|
+
#
|
|
243
|
+
# Explanation:
|
|
244
|
+
# Assuming
|
|
245
|
+
# resource is instance of Lottery::Package #1 (@lottery_package)
|
|
246
|
+
# association is Lottery::Prizes
|
|
247
|
+
# if association is named lottery_prizes, then that association is returned
|
|
248
|
+
# if association is named prizes, then that association is returned
|
|
249
|
+
#
|
|
250
|
+
def relation(associated_resource)
|
|
251
|
+
if associated_resource && associated_resource.object
|
|
252
|
+
name = associated_resource.as || plural_name
|
|
253
|
+
associated_resource.klass.reflect_on_association(name) ?
|
|
254
|
+
associated_resource.object.send(name) : associated_resource.object.send(klass.to_s.demodulize.underscore.pluralize)
|
|
255
|
+
else
|
|
256
|
+
klass
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
class Resource::ParentInfo < Resource::Info
|
|
262
|
+
attr_reader :object,:as
|
|
263
|
+
def initialize(resources, params, request)
|
|
264
|
+
ptr = resources.detect do |r,as|
|
|
265
|
+
@klass = classify(r)
|
|
266
|
+
@as = as
|
|
267
|
+
([@klass] + @klass.descendants).detect do |c|
|
|
268
|
+
@name = c.to_s.underscore.tr('/','_').to_sym
|
|
269
|
+
unless @id = request.symbolized_path_parameters["#{@name}_id".to_sym]
|
|
270
|
+
# see if name space handling is necessary
|
|
271
|
+
if c.to_s.include?('::')
|
|
272
|
+
@name = c.to_s.demodulize.underscore.to_sym
|
|
273
|
+
@id = request.symbolized_path_parameters["#{@name}_id".to_sym]
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
@id
|
|
277
|
+
end
|
|
278
|
+
@id
|
|
279
|
+
end
|
|
280
|
+
if ptr
|
|
281
|
+
@object = @klass.find(@id)
|
|
282
|
+
else
|
|
283
|
+
@klass = @name = nil
|
|
284
|
+
end
|
|
285
|
+
@data = params[var_name(ptr)]
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def object?
|
|
289
|
+
!!@object
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def real_class
|
|
293
|
+
@object && @object.class
|
|
294
|
+
end
|
|
295
|
+
private
|
|
296
|
+
def classify(resource)
|
|
297
|
+
case resource
|
|
298
|
+
when Symbol, String
|
|
299
|
+
resource.to_s.classify.constantize
|
|
300
|
+
else
|
|
301
|
+
resource
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Copyright (c) 2012 Bingo Entreprenøren AS
|
|
2
|
+
# Copyright (c) 2012 Teknobingo Scandinavia AS
|
|
3
|
+
# Copyright (c) 2012 Knut I. Stenmark
|
|
4
|
+
# Copyright (c) 2012 Patrick Hanevold
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
module Trust
|
|
26
|
+
module Controller
|
|
27
|
+
autoload :Resource, 'trust/controller/resource'
|
|
28
|
+
autoload :Properties, 'trust/controller/properties'
|
|
29
|
+
|
|
30
|
+
extend ActiveSupport::Concern
|
|
31
|
+
|
|
32
|
+
module ClassMethods
|
|
33
|
+
|
|
34
|
+
# Returns the controller Trust::Controller::Properties.
|
|
35
|
+
# If no properties are instantiated, it will be instantiated
|
|
36
|
+
#
|
|
37
|
+
# == Delegated methods
|
|
38
|
+
#
|
|
39
|
+
# The following methods are delegated to properties. See Trust::Controller::Properties for details
|
|
40
|
+
# * <tt>belongs_to</tt> - define one or more associations to parents
|
|
41
|
+
# * <tt>actions</tt> - acion definitions outside the restful actions
|
|
42
|
+
# * <tt>model</tt> - Redefine the model used in the controller (if it's name does not match the
|
|
43
|
+
# controller_path)
|
|
44
|
+
#
|
|
45
|
+
def properties
|
|
46
|
+
@properties ||= Trust::Controller::Properties.instantiate(self)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
delegate :belongs_to, :actions, :model, :to => :properties
|
|
50
|
+
|
|
51
|
+
# Enables authorization in controller
|
|
52
|
+
# +trustee+ accepts +:off+ or a hash of +callback+ options such as +:except+ and +:only+
|
|
53
|
+
#
|
|
54
|
+
# +trustee+ automatically calls the class methods: +set_user+, +load_resource+ and +access_control+
|
|
55
|
+
#
|
|
56
|
+
# +trustee+ will raise an Trust::AccessDenied exception if the user is not permitted the action
|
|
57
|
+
#
|
|
58
|
+
# ==== Examples
|
|
59
|
+
#
|
|
60
|
+
# # enable permission check for all restful actions
|
|
61
|
+
# class AccountsController < ApplicationController
|
|
62
|
+
# login_required
|
|
63
|
+
# trustee
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# # disable all permission check
|
|
67
|
+
# class PasswordController < ApplicationController
|
|
68
|
+
# # assuming login_required and trustee has been in your application controller
|
|
69
|
+
# trustee :off
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# # enable permission check and loading for only :new and :create action
|
|
73
|
+
# class AccountsController < ApplicationController
|
|
74
|
+
# login_required
|
|
75
|
+
# trustee :only => [:new, :create]
|
|
76
|
+
# end
|
|
77
|
+
#
|
|
78
|
+
# ==== Caching Trust::AccessDenied exception
|
|
79
|
+
# Normally an exception handler is included in the ApplicationController. Example:
|
|
80
|
+
# class ApplicationController < ActionController::Base
|
|
81
|
+
# rescue_from Trust::AccessDenied do |exception|
|
|
82
|
+
# redirect_to root_url, :alert => exception.message
|
|
83
|
+
# end
|
|
84
|
+
# end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def trustee(*args)
|
|
88
|
+
module_eval do
|
|
89
|
+
include TrustInstanceMethods
|
|
90
|
+
set_user *args
|
|
91
|
+
load_resource *args
|
|
92
|
+
access_control *args
|
|
93
|
+
helper_method :can?, :resource
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Enable or disable +before_filter+ callback for setting the current user
|
|
98
|
+
# Arguments:
|
|
99
|
+
# :off - switch callback off
|
|
100
|
+
# :only - only include these actions
|
|
101
|
+
# :except - except these actions
|
|
102
|
+
def set_user(*args)
|
|
103
|
+
_filter_setting(:set_user, *args)
|
|
104
|
+
end
|
|
105
|
+
# Enable or disable +before_filter+ callback for setting the loading resource
|
|
106
|
+
# Arguments:
|
|
107
|
+
# :off - switch callback off
|
|
108
|
+
# :only - only include these actions
|
|
109
|
+
# :except - except these actions
|
|
110
|
+
def load_resource(*args)
|
|
111
|
+
_filter_setting(:load_resource, *args)
|
|
112
|
+
end
|
|
113
|
+
# Enable or disable +before_filter+ callback for setting the access control, i.e. verifying permissions
|
|
114
|
+
# for the logged in user
|
|
115
|
+
# Arguments:
|
|
116
|
+
# :off - switch callback off
|
|
117
|
+
# :only - only include these actions
|
|
118
|
+
# :except - except these actions
|
|
119
|
+
def access_control(*args)
|
|
120
|
+
_filter_setting(:access_control, *args)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
def _filter_setting(method, *args)
|
|
125
|
+
options = args.extract_options!
|
|
126
|
+
skip_before_filter method
|
|
127
|
+
unless args.include? :off
|
|
128
|
+
before_filter method, options
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
module TrustInstanceMethods
|
|
134
|
+
# Returns the controller Trust::Controller::Properties.
|
|
135
|
+
# If no properties are instantiated, it will be instantiated
|
|
136
|
+
#
|
|
137
|
+
# == Delegated methods
|
|
138
|
+
#
|
|
139
|
+
# The following methods are delegated to properties. See Trust::Controller::Properties for details
|
|
140
|
+
# * <tt>belongs_to</tt> - define one or more associations to parents
|
|
141
|
+
# * <tt>actions</tt> - acion definitions outside the restful actions
|
|
142
|
+
# * <tt>model</tt> - Redefine the model used in the controller (if it's name does not match the
|
|
143
|
+
# controller_path)
|
|
144
|
+
#
|
|
145
|
+
def properties
|
|
146
|
+
self.class.properties
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Sets the current user. It assumes +current_user+ is defined
|
|
150
|
+
# This method is triggered as a callback on +before_filter+
|
|
151
|
+
# You may override this method.
|
|
152
|
+
#
|
|
153
|
+
# ==== Example
|
|
154
|
+
#
|
|
155
|
+
# def set_user
|
|
156
|
+
# Trust::Authorization.user = Thread[:current_user]
|
|
157
|
+
# end
|
|
158
|
+
def set_user
|
|
159
|
+
Trust::Authorization.user = current_user
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Returns the Trust::Controller::Resource resource for the controller.
|
|
163
|
+
# Available as a helper in views
|
|
164
|
+
# See Trust::Controller::Resource for relevant methods
|
|
165
|
+
def resource
|
|
166
|
+
@resource ||= Trust::Controller::Resource.new(self, self.class.properties, action_name, params, request)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Loads the resource which basically means loading the instance and eventual parent defined through +belongs_to+
|
|
170
|
+
# This method is triggered as a callback on +before_filter+
|
|
171
|
+
# See Trust::Controller::Resource for more information
|
|
172
|
+
def load_resource
|
|
173
|
+
resource.load
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Performs the actual access_control
|
|
177
|
+
# This method is triggered as a callback on +before_filter+
|
|
178
|
+
def access_control
|
|
179
|
+
Trust::Authorization.authorize!(action_name, resource.instance || resource.klass, resource.parent)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Tests for current users permissions
|
|
183
|
+
# If access control is not sufficient in controller, you may use this method.
|
|
184
|
+
# Also available as a helper in views
|
|
185
|
+
#
|
|
186
|
+
# ==== Examples
|
|
187
|
+
# can? :edit # does the current user have permission to edit the current resource?
|
|
188
|
+
# # If there is a nested resource, the parent is automatically associated
|
|
189
|
+
# can? :edit, @customer # does the current user have permission to edit the given customer?
|
|
190
|
+
# # Parent is also passed on here.
|
|
191
|
+
# can? :edit, @account, @client # is current user allowed to edit the account associated with the client?
|
|
192
|
+
def can?(action_name, subject = resource.instance || resource.klass, parent = resource.parent)
|
|
193
|
+
Trust::Authorization.authorized?(action_name, subject, parent)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright (c) 2012 Bingo Entreprenøren AS
|
|
2
|
+
# Copyright (c) 2012 Teknobingo Scandinavia AS
|
|
3
|
+
# Copyright (c) 2012 Knut I. Stenmark
|
|
4
|
+
# Copyright (c) 2012 Patrick Hanevold
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
module Trust
|
|
26
|
+
class NoBlockError < StandardError; end
|
|
27
|
+
class UnsupportedCondition < StandardError; end
|
|
28
|
+
class RoleAssigmnentMissing < StandardError; end
|
|
29
|
+
|
|
30
|
+
class AccessDenied < StandardError
|
|
31
|
+
attr_reader :action, :subject
|
|
32
|
+
attr_writer :default_message
|
|
33
|
+
|
|
34
|
+
def initialize(message = nil, action = nil, subject = nil)
|
|
35
|
+
@message = message
|
|
36
|
+
@action = action
|
|
37
|
+
@subject = subject
|
|
38
|
+
@default_message = I18n.t(:"unauthorized.default", :default => "You are not authorized to access this page.")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_s
|
|
42
|
+
@message || @default_message
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Copyright (c) 2012 Bingo Entreprenøren AS
|
|
2
|
+
# Copyright (c) 2012 Teknobingo Scandinavia AS
|
|
3
|
+
# Copyright (c) 2012 Knut I. Stenmark
|
|
4
|
+
# Copyright (c) 2012 Patrick Hanevold
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
module Trust
|
|
26
|
+
module InheritableAttribute
|
|
27
|
+
|
|
28
|
+
def self.deep_copy value
|
|
29
|
+
if value.is_a? Hash
|
|
30
|
+
Hash[*value.map{ |k,v| [self.deep_copy(k),self.deep_copy(v)] }.flatten(1)]
|
|
31
|
+
elsif value.is_a? Array
|
|
32
|
+
value.map{ |v| self.deep_copy(v) }
|
|
33
|
+
elsif value.is_a? Symbol
|
|
34
|
+
value
|
|
35
|
+
else
|
|
36
|
+
value.clone
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
extend ActiveSupport::Concern
|
|
41
|
+
|
|
42
|
+
module ClassMethods
|
|
43
|
+
# Creates an inheritable attribute with accessors in the singleton class. Derived classes inherit the
|
|
44
|
+
# attributes. This is especially helpful with arrays or hashes that are extended in the inheritance
|
|
45
|
+
# chain. Note that you have to initialize the inheritable attribute.
|
|
46
|
+
#
|
|
47
|
+
# Example:
|
|
48
|
+
#
|
|
49
|
+
# class Cat
|
|
50
|
+
# inheritable_attr :drinks
|
|
51
|
+
# self.drinks = ["Becks"]
|
|
52
|
+
#
|
|
53
|
+
# class Garfield < Cat
|
|
54
|
+
# self.drinks << "Fireman's 4"
|
|
55
|
+
#
|
|
56
|
+
# and then, later
|
|
57
|
+
#
|
|
58
|
+
# Cat.drinks #=> ["Becks"]
|
|
59
|
+
# Garfield.drinks #=> ["Becks", "Fireman's 4"]
|
|
60
|
+
def inheritable_attr(name, options = {})
|
|
61
|
+
src = <<-end_src
|
|
62
|
+
def #{name}=(v)
|
|
63
|
+
@#{name} = v
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def #{name}
|
|
67
|
+
return @#{name} unless superclass.respond_to?(:#{name}) and value = superclass.#{name}
|
|
68
|
+
@#{name} ||= ::Trust::InheritableAttribute::deep_copy(value) # only do this once.
|
|
69
|
+
end
|
|
70
|
+
end_src
|
|
71
|
+
instance_eval src, __FILE__, __LINE__
|
|
72
|
+
if !options.key?(:instance_writer) || options[:instance_writer]
|
|
73
|
+
src = <<-end_src
|
|
74
|
+
def #{name}=(v)
|
|
75
|
+
self.class.#{name} = v
|
|
76
|
+
end
|
|
77
|
+
end_src
|
|
78
|
+
class_eval src, __FILE__, __LINE__
|
|
79
|
+
end
|
|
80
|
+
if !options.key?(:instance_reader) || options[:instance_reader]
|
|
81
|
+
src = <<-end_src
|
|
82
|
+
def #{name}
|
|
83
|
+
self.class.#{name}
|
|
84
|
+
end
|
|
85
|
+
end_src
|
|
86
|
+
class_eval src, __FILE__, __LINE__
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|