qm-acts-as-generic-controller 0.1.16 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ begin
10
10
  gemspec.email = "marcin@saepia.net"
11
11
  gemspec.homepage = "http://q.saepia.net"
12
12
  gemspec.authors = ["Marcin Lewandowski"]
13
- gemspec.version = "0.1.16"
13
+ gemspec.version = "0.1.17"
14
14
  gemspec.files = Rake::FileList.new [ "MIT-LICENSE", "Rakefile", "lib/*", "app/views/generic_controller/*" ]
15
15
  gemspec.add_dependency "qui-common-helpers", ">= 0.0.8"
16
16
  gemspec.add_dependency "qui-index-table", ">= 0.0.8"
@@ -48,7 +48,7 @@
48
48
  <%- tab_headers = {} -%>
49
49
  <%- record.class.generic_field_associations.each do |k,v| -%>
50
50
  <%- if current_user.respond_to? "has_privileges?" -%>
51
- <%- has_privileges = current_user.has_privileges?(:class_name => v[:class_name], :generic_action => :index_any) -%>
51
+ <%- has_privileges = current_user.has_privileges?(:class_name => v[:class_name], :generic_action => :index_any) || current_user.has_privileges?(:class_name => v[:class_name], :generic_action => :index_created)-%>
52
52
  <%- has_privileges_to_generic_create = current_user.has_privileges?(:class_name => v[:class_name], :generic_action => :create) -%>
53
53
  <%- else -%>
54
54
  <%- has_privileges = true -%>
@@ -63,14 +63,8 @@
63
63
 
64
64
  <%- end -%>
65
65
 
66
- <%- if v[:class_name].respond_to? :limit_for_user -%>
67
- <%- tab_headers[k] = t(:"#{section_prefix}tabs.#{table_name}.#{k}") + " (#{record.send(k).limit_for_user(current_user).count})" %>
68
- <%= render :partial => "generic_controller/index_table", :locals => { :records => record.send(k).limit_for_user(current_user), :class_name => v[:class_name] } %>
69
-
70
- <%- else -%>
71
- <%- tab_headers[k] = t(:"#{section_prefix}tabs.#{table_name}.#{k}") + " (#{record.send(k).count})" %>
72
- <%= render :partial => "generic_controller/index_table", :locals => { :records => record.send(k), :class_name => v[:class_name] } %>
73
- <%- end -%>
66
+ <%- tab_headers[k] = t(:"#{section_prefix}tabs.#{table_name}.#{k}") + " (#{record.send(k).limit_with_security_scheme(:user => current_user).size})" %>
67
+ <%= render :partial => "generic_controller/index_table", :locals => { :records => record.send(k).limit_with_security_scheme(:user => current_user), :class_name => v[:class_name] } %>
74
68
 
75
69
  <%- end -%>
76
70
  <%- end -%>
@@ -36,38 +36,14 @@ module QM
36
36
  module InstanceMethods
37
37
  def index
38
38
  unless instance_variable_defined?(plural_variable)
39
- if params[:scopes] and params[:scopes].is_a?(Array)
40
- valid_scopes = params[:scopes].uniq.each{ |scope| scope if model.generic_named_scopes.has_key? scope.to_sym }.compact
41
- if valid_scopes.size > 0
42
- if defined?(current_user) and model.respond_to? :limit_for_user
43
- data = eval "model.#{valid_scopes.join(".")}.limit_for_user(#{current_user})"
44
- else
45
- data = eval "model.#{valid_scopes.join(".")}"
46
- end
47
- else
48
- if defined?(current_user) and model.respond_to? :limit_for_user
49
- data = model.limit_for_user(current_user)
50
- else
51
- data = model.all
52
- end
53
- end
54
- else
55
- if defined?(current_user) and model.respond_to? :limit_for_user
56
- data = model.limit_for_user(current_user)
57
- else
58
- data = model.all
59
- end
60
- end
61
-
62
-
63
- instance_variable_set(plural_variable, data)
39
+ instance_variable_set(plural_variable, model.limit_with_security_scheme(:user => current_user))
64
40
  end
65
41
 
66
42
  respond_to do |format|
67
43
  format.html
68
44
  format.xml {
69
45
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
70
- render :xml => instance_variable_get(plural_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id])
46
+ render :xml => instance_variable_get(plural_variable).to_xml(:only => current_user_xml_privileged_attributes)
71
47
  else
72
48
  render :xml => instance_variable_get(plural_variable)
73
49
  end
@@ -76,15 +52,16 @@ module QM
76
52
  end
77
53
 
78
54
  def show
79
-
80
55
  instance_variable_set(singular_variable, model.find(params[:id])) unless instance_variable_defined?(singular_variable)
81
56
 
57
+ check_generic_actions or return
58
+
82
59
  respond_to do |format|
83
- format.html
60
+ format.html
84
61
 
85
62
  format.xml {
86
63
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
87
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id])
64
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes)
88
65
  else
89
66
  render :xml => instance_variable_get(singular_variable)
90
67
  end
@@ -94,6 +71,8 @@ module QM
94
71
 
95
72
  def edit
96
73
  instance_variable_set(singular_variable, model.find(params[:id])) unless instance_variable_defined?(singular_variable)
74
+
75
+ check_generic_actions or return
97
76
  end
98
77
 
99
78
  def new
@@ -103,7 +82,7 @@ module QM
103
82
  format.html
104
83
  format.xml {
105
84
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
106
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id])
85
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes)
107
86
  else
108
87
  render :xml => instance_variable_get(singular_variable)
109
88
  end
@@ -137,7 +116,7 @@ module QM
137
116
  end
138
117
  }
139
118
 
140
- format.xml { render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id]), :status => :created, :location => [ section, instance_variable_get(singular_variable) ] }
119
+ format.xml { render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes), :status => :created, :location => [ section, instance_variable_get(singular_variable) ] }
141
120
 
142
121
 
143
122
  else
@@ -157,9 +136,9 @@ module QM
157
136
 
158
137
  format.xml {
159
138
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
160
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id]), :status => :created, :location => instance_variable_get(singular_variable)
139
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes), :status => :created, :location => instance_variable_get(singular_variable)
161
140
  else
162
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id]), :status => :created, :location => instance_variable_get(singular_variable)
141
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes), :status => :created, :location => instance_variable_get(singular_variable)
163
142
  end
164
143
  }
165
144
  end
@@ -174,7 +153,7 @@ module QM
174
153
  }
175
154
  format.xml {
176
155
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
177
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id]), :status => :unprocessable_entity
156
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes), :status => :unprocessable_entity
178
157
  else
179
158
  render :xml => instance_variable_get(singular_variable), :status => :unprocessable_entity
180
159
  end
@@ -186,6 +165,8 @@ module QM
186
165
  def update
187
166
  instance_variable_set(singular_variable, model.find(params[:id])) unless instance_variable_defined?(singular_variable)
188
167
 
168
+ check_generic_actions or return
169
+
189
170
  remove_unprivileged_keys_from_params
190
171
 
191
172
  instance_variable_get(singular_variable).update_attributes params[singular_variable(true)]
@@ -212,7 +193,7 @@ module QM
212
193
  }
213
194
  format.xml {
214
195
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
215
- render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user.privileged_attributes(model, :read, true) + [:id]), :status => :unprocessable_entity
196
+ render :xml => instance_variable_get(singular_variable).to_xml(:only => current_user_xml_privileged_attributes), :status => :unprocessable_entity
216
197
  else
217
198
  render :xml => instance_variable_get(singular_variable), :status => :unprocessable_entity
218
199
  end
@@ -223,6 +204,9 @@ module QM
223
204
 
224
205
  def destroy
225
206
  instance_variable_set(singular_variable, model.find(params[:id])) unless instance_variable_defined?(singular_variable)
207
+
208
+ check_generic_actions or return
209
+
226
210
  instance_variable_get(singular_variable).destroy
227
211
 
228
212
  flash[:notice] = :destroyedAsFlash
@@ -235,11 +219,11 @@ module QM
235
219
 
236
220
  protected
237
221
  def save_creator
238
- instance_variable_get(singular_variable).send("creator_#{current_user.class.to_s.tableize.singularize}_id=", current_user.id) if defined?(current_user) and instance_variable_get(singular_variable).respond_to? "creator_#{current_user.class.to_s.tableize.singularize}_id="
222
+ instance_variable_get(singular_variable).send("#{creator_column}=", current_user.id) if defined?(current_user) and instance_variable_get(singular_variable).respond_to? "#{creator_column}="
239
223
  end
240
224
 
241
225
  def save_updator
242
- instance_variable_get(singular_variable).send("updator_#{current_user.class.to_s.tableize.singularize}_id=", current_user.id) if defined?(current_user) and instance_variable_get(singular_variable).respond_to? "updator_#{current_user.class.to_s.tableize.singularize}_id="
226
+ instance_variable_get(singular_variable).send("#{updator_column}=", current_user.id) if defined?(current_user) and instance_variable_get(singular_variable).respond_to? "#{updator_column}="
243
227
  end
244
228
 
245
229
  def model
@@ -255,6 +239,29 @@ module QM
255
239
  end
256
240
 
257
241
 
242
+ def check_generic_actions
243
+ if has_creator?
244
+ action = case params[:action].to_sym
245
+ when :show
246
+ :show_created
247
+ when :edit
248
+ :update_created
249
+ when :update
250
+ :update_created
251
+ when :destroy
252
+ :delete_created
253
+ end
254
+
255
+ if not current_user.has_privileges?({:class_name => model, :generic_action => "#{params[:action]}_any".to_sym}) and current_user.has_privileges?({:class_name => model, :generic_action => action.to_sym}) and instance_variable_get(singular_variable).send(creator_column) != current_user.id
256
+
257
+ logger.warn "Security warning: User #{current_user.login} has #{action} privileges, but there is a creator's ID mismatch (current user: #{current_user.id}, record: #{instance_variable_get(singular_variable).send(creator_column)}. Action #{params[:action]} on #{model} was stopped."
258
+ render_generic_forbidden
259
+ return false
260
+ end
261
+ end
262
+ true
263
+ end
264
+
258
265
  def check_generic_privileges
259
266
  return true unless defined?(current_user)
260
267
  return true unless current_user.respond_to? "has_privileges?"
@@ -262,24 +269,38 @@ module QM
262
269
  action = params[:action].to_sym
263
270
  action = case action
264
271
  when :index
265
- :index_any
272
+ [ :index_any, :index_created ] # Limited access to the elements should be provided by limit_for_user, which (TODO) should be automatically generated
266
273
  when :show
267
- :show_any
274
+ [ :show_any, :show_created ]
268
275
  when :edit
269
- :update_any
276
+ [ :update_any, :update_created ]
270
277
  when :update
271
- :update_any
278
+ [ :update_any, :update_created ]
272
279
  when :create
273
280
  :create
274
281
  when :destroy
275
- :delete_any
282
+ [ :delete_any, :delete_created ]
276
283
  end
277
284
 
278
- unless current_user.has_privileges? :class_name => model, :action => action
279
- logger.info "Security warning: User #{current_user.login} has not enough privileges to perform action #{action} on #{model}"
285
+ if action.is_a? Array
286
+ has_privileges = false
287
+ action.each do |a|
288
+ if current_user.has_privileges? :class_name => model, :generic_action => a
289
+ has_privileges = true
290
+ break
291
+ end
292
+ end
293
+ else
294
+ has_privileges = current_user.has_privileges? :class_name => model, :generic_action => action
295
+ end
296
+
297
+ unless has_privileges
298
+ logger.warn "Security warning: User #{current_user.login} has not enough privileges to perform action #{params[:action]} on #{model}"
280
299
  render_generic_forbidden
300
+ return false
281
301
  end
282
302
 
303
+ true
283
304
  end
284
305
 
285
306
  def check_limit_for_user
@@ -290,14 +311,32 @@ module QM
290
311
  if defined?(current_user) and current_user.respond_to? :privileged_attributes
291
312
  params[singular_variable(true)].keys.each do |key|
292
313
  if not current_user.privileged_attributes(model, :write).include?(key) and (model.generic_field_associations.has_key?(key.to_sym) and not current_user.privileged_attributes(model, :write).include?(model.generic_field_associations[key.to_sym][:foreign_key]))
293
- logger.info "Security warning: Deleting key '#{key}' from params hash, because user #{current_user.login} has not enough privileges to modify that attribute"
314
+ logger.warn "Security warning: Deleting key '#{key}' from params hash, because user #{current_user.login} has not enough privileges to modify that attribute"
294
315
  params[singular_variable(true)].delete key
295
316
  end
296
317
  end
297
318
  end
298
319
  end
299
320
 
321
+ def current_user_xml_privileged_attributes
322
+ current_user.privileged_attributes(model, :read, true) + [:id]
323
+ end
300
324
 
325
+ def creator_column
326
+ "creator_#{current_user.class.to_s.tableize.singularize}_id"
327
+ end
328
+
329
+ def has_creator?
330
+ model.columns_hash.has_key? creator_column
331
+ end
332
+
333
+ def updator_column
334
+ "updator_#{current_user.class.to_s.tableize.singularize}_id"
335
+ end
336
+
337
+ def has_updator?
338
+ model.columns_hash.has_key? updator_column
339
+ end
301
340
 
302
341
  def render_generic_forbidden
303
342
  # FIXME TODO render something more nice to user
@@ -3,10 +3,36 @@ module QM
3
3
  module ModelIncludes
4
4
  def self.included(base) # :nodoc:
5
5
  base.extend ClassMethods
6
-
7
6
  end
8
7
 
9
8
  module ClassMethods
9
+ def limit_with_security_scheme(options)
10
+ # options[:user] - current user that performs the query
11
+
12
+ raise ArgumentError, "You must pass options[:user] to limit_with_security_scheme" unless options.has_key? :user
13
+
14
+ # Return empty set if user has no privileges at all (nor index_any and index_created)
15
+ return [] if !options[:user].has_privileges?(:class_name => self, :generic_action => :index_any) && !options[:user].has_privileges?(:class_name => self, :generic_action => :index_created)
16
+
17
+ creator_column = "creator_#{options[:user].class.to_s.tableize.singularize}_id"
18
+ should_limit_index_for_created = columns_hash.has_key?(creator_column) && options[:user].respond_to?(:has_privileges?) && !options[:user].has_privileges?(:class_name => self, :generic_action => :index_any) && options[:user].has_privileges?(:class_name => self, :generic_action => :index_created)
19
+
20
+
21
+ if respond_to? :limit_for_user
22
+ if should_limit_index_for_created
23
+ limit_for_user(options[:user]).find(:all, :conditions => { creator_column => options[:user].id })
24
+ else
25
+ limit_for_user(options[:user])
26
+ end
27
+ else
28
+ if should_limit_index_for_created
29
+ find(:all, :conditions => { creator_column => options[:user].id })
30
+ else
31
+ all
32
+ end
33
+ end
34
+ end
35
+
10
36
  def generic_field_associations
11
37
  @generic_field_associations || {}
12
38
  end
@@ -62,8 +88,6 @@ module QM
62
88
 
63
89
  super name, options, &block
64
90
  end
65
-
66
-
67
91
 
68
92
  def generic_actions(options = {})
69
93
  @generic_actions ||= {}
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qm-acts-as-generic-controller
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
4
+ hash: 57
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 16
10
- version: 0.1.16
9
+ - 17
10
+ version: 0.1.17
11
11
  platform: ruby
12
12
  authors:
13
13
  - Marcin Lewandowski
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-07 00:00:00 +01:00
18
+ date: 2011-01-10 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency