param_protected 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +15 -0
- data/VERSION +1 -1
- data/lib/param_protected/controller_modifications.rb +1 -1
- data/lib/param_protected/protector.rb +87 -29
- data/param_protected.gemspec +7 -3
- data/test/app_root/app/controllers/conditions_controller.rb +21 -0
- data/test/app_root/app/controllers/users_controller.rb +1 -1
- data/test/conditions_controller_test.rb +13 -0
- data/test/protector_test.rb +18 -14
- data/test/users_controller_test.rb +6 -2
- metadata +16 -5
data/README.rdoc
CHANGED
@@ -46,6 +46,7 @@ If you call <tt>param_protected</tt> or <tt>param_accessible</tt> multiple times
|
|
46
46
|
param_protected [{ :user => [:first, :last] }, :password], :only => :some_action
|
47
47
|
Is equivalent to saying...
|
48
48
|
param_protected [:id, { :user => [:first, :last] }, :password], :only => :some_action
|
49
|
+
|
49
50
|
Credit: Moritz Heidkamp
|
50
51
|
|
51
52
|
=== Inheritance
|
@@ -53,6 +54,20 @@ Param protections will be inherited to derived controllers.
|
|
53
54
|
|
54
55
|
Credit: Moritz Heidkamp
|
55
56
|
|
57
|
+
=== Conditions
|
58
|
+
You can conditionally protect params...
|
59
|
+
param_protected :admin, :unless => "user_is_admin?"
|
60
|
+
param_accessible :admin, :if => :user_is_admin?
|
61
|
+
param_protected :admin, :unless => Proc.new{ |controller| controller.user_is_admin? }
|
62
|
+
|
63
|
+
Credit: Mortiz Heidkamp
|
64
|
+
|
65
|
+
== Regular Expressions
|
66
|
+
You can use regular expressions when specifying which params to make protected or accessible.
|
67
|
+
param_accessible /item\d/
|
68
|
+
|
69
|
+
Credit: Mortiz Heidkamp
|
70
|
+
|
56
71
|
=== How does it work?
|
57
72
|
It does an <tt>alias_method_chain</tt> on <tt>ActionController::Base#params</tt> that filters (and caches) the params. You can get the unfiltered, pristine params by calling <tt>ActionController::Base#params_without_protection</tt>.
|
58
73
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -46,7 +46,7 @@ module ParamProtected
|
|
46
46
|
elsif @params_protected
|
47
47
|
@params_protected
|
48
48
|
else
|
49
|
-
@params_protected = Protector.instance(self.class).protect(params_without_protection, action_name)
|
49
|
+
@params_protected = Protector.instance(self.class).protect(self, params_without_protection, action_name)
|
50
50
|
end
|
51
51
|
|
52
52
|
end
|
@@ -17,15 +17,15 @@ module ParamProtected
|
|
17
17
|
copy.instance_variable_set(:@protections, deep_copy(@protections))
|
18
18
|
end
|
19
19
|
|
20
|
-
def declare_protection(params,
|
20
|
+
def declare_protection(params, options, exclusivity)
|
21
21
|
params = normalize_params(params)
|
22
|
-
actions =
|
23
|
-
@protections << [params, actions, exclusivity]
|
22
|
+
actions, condition = normalize_options(options)
|
23
|
+
@protections << [params, actions, condition, exclusivity]
|
24
24
|
end
|
25
25
|
|
26
|
-
def protect(controller_params, action_name)
|
26
|
+
def protect(controller, controller_params, action_name)
|
27
27
|
returning(deep_copy(controller_params)) do |params|
|
28
|
-
protections_for_action(action_name).each do |exclusivity, protected_params|
|
28
|
+
protections_for_action(controller, action_name).each do |exclusivity, protected_params|
|
29
29
|
filter_params(protected_params, params, exclusivity) unless protected_params.empty?
|
30
30
|
end
|
31
31
|
end
|
@@ -33,12 +33,10 @@ module ParamProtected
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def protections_for_action(action_name)
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
action_matches?(actions[0], actions[1..-1], action_name)
|
41
|
-
end.inject({ WHITELIST => { }, BLACKLIST => { } }) do |result, (protected_params, action_name, exclusivity)|
|
36
|
+
def protections_for_action(controller, action_name)
|
37
|
+
@protections.select do |protected_params, actions, condition, exclusivity|
|
38
|
+
action_matches?(actions[0], actions[1..-1], action_name) && condition_applies?(controller, condition)
|
39
|
+
end.inject({ WHITELIST => { }, BLACKLIST => { } }) do |result, (protected_params, actions, condition, exclusivity)|
|
42
40
|
merge_protections(result[exclusivity], protected_params)
|
43
41
|
result
|
44
42
|
end
|
@@ -80,36 +78,59 @@ module ParamProtected
|
|
80
78
|
params.each{ |param| normalize_params(param, params_out) }
|
81
79
|
elsif params.instance_of?(Hash)
|
82
80
|
params.each do |k, v|
|
83
|
-
k = k
|
81
|
+
k = normalize_key(k)
|
84
82
|
params_out[k] = {}
|
85
83
|
normalize_params(v, params_out[k])
|
86
84
|
end
|
87
85
|
else
|
88
|
-
params_out[params
|
86
|
+
params_out[normalize_key(params)] = nil
|
89
87
|
end
|
90
88
|
params_out
|
91
89
|
end
|
90
|
+
|
91
|
+
def normalize_key(k)
|
92
|
+
if k.is_a?(Regexp)
|
93
|
+
k
|
94
|
+
else
|
95
|
+
k.to_s
|
96
|
+
end
|
97
|
+
end
|
92
98
|
|
93
99
|
# When specifying which actions param protection apply to, we allow a format like this...
|
94
100
|
# :only => [:action1, :action2]
|
95
101
|
# This method normalizes that to...
|
96
102
|
# [:only, "action1", "action2"]
|
97
|
-
def
|
98
|
-
error_message = "invalid
|
99
|
-
return [:except, nil] if
|
100
|
-
|
101
|
-
raise ArgumentError, error_message unless
|
102
|
-
|
103
|
-
|
104
|
-
|
103
|
+
def normalize_options(options)
|
104
|
+
error_message = "invalid options, use :only or :except, :if or :unless"
|
105
|
+
return [[:except, nil], [:if, true]] if options.blank?
|
106
|
+
|
107
|
+
raise ArgumentError, error_message unless options.instance_of?(Hash)
|
108
|
+
|
109
|
+
scope = [:only, :except] & options.keys
|
110
|
+
condition = [:if, :unless] & options.keys
|
111
|
+
|
112
|
+
raise ArgumentError, error_message unless (options.keys - [:only, :except, :if, :unless]).empty?
|
113
|
+
raise ArgumentError, error_message if scope.size > 1 || condition.size > 1
|
114
|
+
|
115
|
+
scope = scope.first || :except
|
116
|
+
actions = options[scope]
|
105
117
|
actions = [actions] unless actions.instance_of?(Array)
|
106
|
-
actions = actions.collect{ |action| action.to_s }
|
107
|
-
|
118
|
+
actions = actions.collect{ |action| action.try(:to_s) }
|
119
|
+
|
120
|
+
condition = condition.first || :if
|
121
|
+
|
122
|
+
if options.has_key?(condition)
|
123
|
+
condition_value = options[condition]
|
124
|
+
else
|
125
|
+
condition_value = true
|
126
|
+
end
|
127
|
+
|
128
|
+
[[scope, *actions], [condition, condition_value]]
|
108
129
|
end
|
109
130
|
|
110
131
|
# When #dup just isn't enough... :P
|
111
132
|
def deep_copy(object)
|
112
|
-
returning(
|
133
|
+
returning(try_to_clone(object)) do |new_object|
|
113
134
|
case new_object
|
114
135
|
when Hash
|
115
136
|
new_object.each{ |k, v| new_object[k] = deep_copy(v) }
|
@@ -120,11 +141,28 @@ module ParamProtected
|
|
120
141
|
end
|
121
142
|
|
122
143
|
# Some objects are not dupable... like TrueClass, FalseClass and NilClass.
|
123
|
-
def
|
124
|
-
object.
|
144
|
+
def try_to_clone(object)
|
145
|
+
object.clone
|
125
146
|
rescue TypeError
|
126
147
|
object
|
127
148
|
end
|
149
|
+
|
150
|
+
def condition_applies?(controller, condition)
|
151
|
+
result = case condition[1]
|
152
|
+
when Proc
|
153
|
+
condition[1].call(controller)
|
154
|
+
when Symbol, String
|
155
|
+
controller.send(condition[1])
|
156
|
+
else
|
157
|
+
condition[1]
|
158
|
+
end
|
159
|
+
|
160
|
+
if condition[0] == :unless
|
161
|
+
not result
|
162
|
+
else
|
163
|
+
result
|
164
|
+
end
|
165
|
+
end
|
128
166
|
|
129
167
|
def action_matches?(scope, actions, action_name)
|
130
168
|
if action_name.blank?
|
@@ -142,15 +180,35 @@ module ParamProtected
|
|
142
180
|
return unless params.kind_of?(Hash)
|
143
181
|
return if protected_params.nil?
|
144
182
|
if exclusivity == BLACKLIST
|
145
|
-
params.delete_if{ |k, v|
|
183
|
+
params.delete_if{ |k, v| key_exists?(protected_params, k) and find_by_key(protected_params, k).nil? }
|
146
184
|
elsif exclusivity == WHITELIST
|
147
|
-
params.delete_if{ |k, v| !
|
185
|
+
params.delete_if{ |k, v| !key_exists?(protected_params, k) }
|
148
186
|
else
|
149
187
|
raise ArgumentError, "unexpected exclusivity: #{exclusivity}"
|
150
188
|
end
|
151
189
|
params.each{ |k, v| filter_params(protected_params[k], v, exclusivity) }
|
152
190
|
params
|
153
191
|
end
|
192
|
+
|
193
|
+
def find_by_key(protected_params, key)
|
194
|
+
protected_params.detect do |k,v|
|
195
|
+
key_matches?(k, key)
|
196
|
+
end.try(:last)
|
197
|
+
end
|
154
198
|
|
199
|
+
def key_exists?(protected_params, key)
|
200
|
+
protected_params.any? do |k,v|
|
201
|
+
key_matches?(k, key)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def key_matches?(k, key)
|
206
|
+
if k.is_a? Regexp
|
207
|
+
key.to_s =~ k
|
208
|
+
else
|
209
|
+
key.to_s == k.to_s
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
155
213
|
end
|
156
|
-
end
|
214
|
+
end
|
data/param_protected.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{param_protected}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Christopher J. Bottaro"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-25}
|
13
13
|
s.description = %q{Provides two class methods on ActiveController::Base that filter the params hash for that controller's actions. You can think of them as the controller analog of attr_protected and attr_accessible.}
|
14
14
|
s.email = %q{cjbottaro@alumni.cs.utexas.edu}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"test/app_root/app/controllers/accessible_except_controller.rb",
|
35
35
|
"test/app_root/app/controllers/accessible_only_controller.rb",
|
36
36
|
"test/app_root/app/controllers/application_controller.rb",
|
37
|
+
"test/app_root/app/controllers/conditions_controller.rb",
|
37
38
|
"test/app_root/app/controllers/inherited_users_controller.rb",
|
38
39
|
"test/app_root/app/controllers/merge_controller.rb",
|
39
40
|
"test/app_root/app/controllers/protected_controller.rb",
|
@@ -48,6 +49,7 @@ Gem::Specification.new do |s|
|
|
48
49
|
"test/app_root/config/environments/sqlite3.rb",
|
49
50
|
"test/app_root/config/routes.rb",
|
50
51
|
"test/app_root/lib/console_with_fixtures.rb",
|
52
|
+
"test/conditions_controller_test.rb",
|
51
53
|
"test/inherited_users_controller_test.rb",
|
52
54
|
"test/merge_controller_test.rb",
|
53
55
|
"test/protected_controller_test.rb",
|
@@ -59,7 +61,7 @@ Gem::Specification.new do |s|
|
|
59
61
|
s.homepage = %q{http://github.com/cjbottaro/param_protected}
|
60
62
|
s.rdoc_options = ["--charset=UTF-8"]
|
61
63
|
s.require_paths = ["lib"]
|
62
|
-
s.rubygems_version = %q{1.3.
|
64
|
+
s.rubygems_version = %q{1.3.6}
|
63
65
|
s.summary = %q{Filter unwanted parameters in your controllers and actions.}
|
64
66
|
s.test_files = [
|
65
67
|
"test/accessible_except_test.rb",
|
@@ -67,6 +69,7 @@ Gem::Specification.new do |s|
|
|
67
69
|
"test/app_root/app/controllers/accessible_except_controller.rb",
|
68
70
|
"test/app_root/app/controllers/accessible_only_controller.rb",
|
69
71
|
"test/app_root/app/controllers/application_controller.rb",
|
72
|
+
"test/app_root/app/controllers/conditions_controller.rb",
|
70
73
|
"test/app_root/app/controllers/inherited_users_controller.rb",
|
71
74
|
"test/app_root/app/controllers/merge_controller.rb",
|
72
75
|
"test/app_root/app/controllers/protected_controller.rb",
|
@@ -80,6 +83,7 @@ Gem::Specification.new do |s|
|
|
80
83
|
"test/app_root/config/environments/sqlite3.rb",
|
81
84
|
"test/app_root/config/routes.rb",
|
82
85
|
"test/app_root/lib/console_with_fixtures.rb",
|
86
|
+
"test/conditions_controller_test.rb",
|
83
87
|
"test/inherited_users_controller_test.rb",
|
84
88
|
"test/merge_controller_test.rb",
|
85
89
|
"test/protected_controller_test.rb",
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ConditionsController < ApplicationController
|
2
|
+
param_accessible :a, :if => :something?
|
3
|
+
param_accessible :b, :unless => false
|
4
|
+
param_protected :b, :if => :first_action?
|
5
|
+
param_accessible :c, :if => lambda { |controller| controller.instance_eval { first_action? } }
|
6
|
+
|
7
|
+
def first; end
|
8
|
+
|
9
|
+
def second; end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def something?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def first_action?
|
18
|
+
action_name == 'first'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/test/protector_test.rb
CHANGED
@@ -20,25 +20,29 @@ class HelpersTest < Test::Unit::TestCase
|
|
20
20
|
assert_equal({"something" => {"stuff" => nil, "blah" => {"bleck" => nil}}}, params)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
assert_equal [:except, nil],
|
23
|
+
def test_normalize_options
|
24
|
+
options = @protector.send(:normalize_options, nil)
|
25
|
+
assert_equal [[:except, nil], [:if, true]], options
|
26
26
|
|
27
|
-
|
28
|
-
assert_equal [:only, "blah"],
|
27
|
+
options = @protector.send(:normalize_options, :only => :blah)
|
28
|
+
assert_equal [[:only, "blah"], [:if, true]], options
|
29
29
|
|
30
|
-
|
31
|
-
assert_equal [:only, "blah", "bleck"],
|
30
|
+
options = @protector.send(:normalize_options, :only => [:blah, :bleck])
|
31
|
+
assert_equal [[:only, "blah", "bleck"], [:if, true]], options
|
32
32
|
|
33
|
-
|
34
|
-
assert_equal [:except, "blah"],
|
33
|
+
options = @protector.send(:normalize_options, :except => :blah)
|
34
|
+
assert_equal [[:except, "blah"], [:if, true]], options
|
35
35
|
|
36
|
-
|
37
|
-
assert_equal [:except, "blah", "bleck"],
|
36
|
+
options = @protector.send(:normalize_options, :except => [:blah, :bleck])
|
37
|
+
assert_equal [[:except, "blah", "bleck"], [:if, true]], options
|
38
|
+
|
39
|
+
options = @protector.send(:normalize_options, :unless => :foo?)
|
40
|
+
assert_equal [[:except, nil], [:unless, :foo?]], options
|
38
41
|
|
39
|
-
assert_raises(ArgumentError){ @protector.send(:
|
40
|
-
assert_raises(ArgumentError){ @protector.send(:
|
41
|
-
assert_raises(ArgumentError){ @protector.send(:
|
42
|
+
assert_raises(ArgumentError){ @protector.send(:normalize_options, :onlyy => :blah) }
|
43
|
+
assert_raises(ArgumentError){ @protector.send(:normalize_options, :blah) }
|
44
|
+
assert_raises(ArgumentError){ @protector.send(:normalize_options, :only => :something, :except => :something) }
|
45
|
+
assert_raises(ArgumentError){ @protector.send(:normalize_options, :if => :something, :unless => :something) }
|
42
46
|
end
|
43
47
|
|
44
48
|
def test_deep_copy
|
@@ -2,11 +2,15 @@ require "test_helper"
|
|
2
2
|
|
3
3
|
class UsersControllerTest < ActionController::TestCase
|
4
4
|
PARAMS = { :user => { :id => 123,
|
5
|
-
|
6
|
-
|
5
|
+
'33' => 'ok',
|
6
|
+
'123' => 'ok',
|
7
|
+
:x21 => 'ok',
|
8
|
+
:name => { :first => "chris", :middle => "james", :last => "bottaro"},
|
9
|
+
:email => "cjbottaro@blah.com" },
|
7
10
|
:something => "something" }
|
8
11
|
|
9
12
|
EXPECTED_PARAMS = { "user" => { "name" => {"first" => "chris", "last" => "bottaro"},
|
13
|
+
'33' => 'ok', '123' => 'ok',
|
10
14
|
"email" => "cjbottaro@blah.com" } }
|
11
15
|
|
12
16
|
def test_create
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: param_protected
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 1.3.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Christopher J. Bottaro
|
@@ -9,7 +14,7 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-05-25 00:00:00 -05:00
|
13
18
|
default_executable:
|
14
19
|
dependencies: []
|
15
20
|
|
@@ -40,6 +45,7 @@ files:
|
|
40
45
|
- test/app_root/app/controllers/accessible_except_controller.rb
|
41
46
|
- test/app_root/app/controllers/accessible_only_controller.rb
|
42
47
|
- test/app_root/app/controllers/application_controller.rb
|
48
|
+
- test/app_root/app/controllers/conditions_controller.rb
|
43
49
|
- test/app_root/app/controllers/inherited_users_controller.rb
|
44
50
|
- test/app_root/app/controllers/merge_controller.rb
|
45
51
|
- test/app_root/app/controllers/protected_controller.rb
|
@@ -54,6 +60,7 @@ files:
|
|
54
60
|
- test/app_root/config/environments/sqlite3.rb
|
55
61
|
- test/app_root/config/routes.rb
|
56
62
|
- test/app_root/lib/console_with_fixtures.rb
|
63
|
+
- test/conditions_controller_test.rb
|
57
64
|
- test/inherited_users_controller_test.rb
|
58
65
|
- test/merge_controller_test.rb
|
59
66
|
- test/protected_controller_test.rb
|
@@ -74,18 +81,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
74
81
|
requirements:
|
75
82
|
- - ">="
|
76
83
|
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
77
86
|
version: "0"
|
78
|
-
version:
|
79
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
88
|
requirements:
|
81
89
|
- - ">="
|
82
90
|
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
83
93
|
version: "0"
|
84
|
-
version:
|
85
94
|
requirements: []
|
86
95
|
|
87
96
|
rubyforge_project:
|
88
|
-
rubygems_version: 1.3.
|
97
|
+
rubygems_version: 1.3.6
|
89
98
|
signing_key:
|
90
99
|
specification_version: 3
|
91
100
|
summary: Filter unwanted parameters in your controllers and actions.
|
@@ -95,6 +104,7 @@ test_files:
|
|
95
104
|
- test/app_root/app/controllers/accessible_except_controller.rb
|
96
105
|
- test/app_root/app/controllers/accessible_only_controller.rb
|
97
106
|
- test/app_root/app/controllers/application_controller.rb
|
107
|
+
- test/app_root/app/controllers/conditions_controller.rb
|
98
108
|
- test/app_root/app/controllers/inherited_users_controller.rb
|
99
109
|
- test/app_root/app/controllers/merge_controller.rb
|
100
110
|
- test/app_root/app/controllers/protected_controller.rb
|
@@ -108,6 +118,7 @@ test_files:
|
|
108
118
|
- test/app_root/config/environments/sqlite3.rb
|
109
119
|
- test/app_root/config/routes.rb
|
110
120
|
- test/app_root/lib/console_with_fixtures.rb
|
121
|
+
- test/conditions_controller_test.rb
|
111
122
|
- test/inherited_users_controller_test.rb
|
112
123
|
- test/merge_controller_test.rb
|
113
124
|
- test/protected_controller_test.rb
|