padrino-core 0.12.1 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/padrino-core/application/application_setup.rb +9 -0
- data/lib/padrino-core/application/params_protection.rb +129 -0
- data/lib/padrino-core/application/routing.rb +34 -7
- data/lib/padrino-core/application.rb +2 -0
- data/lib/padrino-core/logger.rb +9 -12
- data/lib/padrino-core/reloader/storage.rb +3 -3
- data/lib/padrino-core/reloader.rb +15 -0
- data/lib/padrino-core/router.rb +5 -1
- data/lib/padrino-core/server.rb +2 -0
- data/lib/padrino-core/version.rb +1 -1
- data/test/fixtures/apps/helpers/support.rb +1 -0
- data/test/test_application.rb +30 -0
- data/test/test_csrf_protection.rb +22 -1
- data/test/test_logger.rb +6 -6
- data/test/test_params_protection.rb +159 -0
- data/test/test_reloader_simple.rb +2 -2
- data/test/test_router.rb +16 -0
- data/test/test_routing.rb +30 -0
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dec1c7a3513869018c0576596a7888261759d25c
|
4
|
+
data.tar.gz: 6ba616394f122fc4bf166facc114554cbddf2331
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6507e1e6ae0a0d2ca472e740928b548d7d9340d5d046725c8c049ece42e595db33018b1f551c148c846a1618e5cc2dd583bca65d202b49e8e86aa669eb770c85
|
7
|
+
data.tar.gz: bc2666c168ba2678e3045db54803ddcec24fd9c486a8b77e03e01c7eb0a01785ed15ac95c487955723f5b7ed864cbd5939a67d4252a5aff5a622ba3c1ba760e0
|
@@ -151,6 +151,15 @@ module Padrino
|
|
151
151
|
I18n.reload!
|
152
152
|
end
|
153
153
|
|
154
|
+
# allow custome session management
|
155
|
+
def setup_sessions(builder)
|
156
|
+
if sessions.kind_of?(Hash) && sessions[:use]
|
157
|
+
builder.use sessions[:use], sessions[:config] || {}
|
158
|
+
else
|
159
|
+
super
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
154
163
|
# sets up csrf protection for the app
|
155
164
|
def setup_csrf_protection(builder)
|
156
165
|
check_csrf_protection_dependency
|
@@ -0,0 +1,129 @@
|
|
1
|
+
begin
|
2
|
+
require 'active_support/core_ext/object/deep_dup' # AS 4.1
|
3
|
+
rescue LoadError => ex
|
4
|
+
require 'active_support/core_ext/hash/deep_dup' # AS >= 3.1
|
5
|
+
end
|
6
|
+
|
7
|
+
module Padrino
|
8
|
+
##
|
9
|
+
# Padrino application module providing means for mass-assignment protection.
|
10
|
+
#
|
11
|
+
module ParamsProtection
|
12
|
+
class << self
|
13
|
+
def registered(app)
|
14
|
+
included(app)
|
15
|
+
end
|
16
|
+
|
17
|
+
def included(base)
|
18
|
+
base.send(:include, InstanceMethods)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
##
|
25
|
+
# Implements filtering of url query params. Can prevent mass-assignment.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# post :update, :params => [:name, :email]
|
29
|
+
# post :update, :params => [:name, :id => Integer]
|
30
|
+
# post :update, :params => [:name => proc{ |v| v.reverse }]
|
31
|
+
# post :update, :params => [:name, :parent => [:name, :position]]
|
32
|
+
# post :update, :params => false
|
33
|
+
# post :update, :params => true
|
34
|
+
# @example
|
35
|
+
# params :name, :email, :password => prox{ |v| v.reverse }
|
36
|
+
# post :update
|
37
|
+
# @example
|
38
|
+
# App.controller :accounts, :params => [:name, :position] do
|
39
|
+
# post :create
|
40
|
+
# post :update, :with => [ :id ], :params => [:name, :position, :addition]
|
41
|
+
# get :show, :with => :id, :params => false
|
42
|
+
# get :search, :params => true
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
def params(*allowed_params)
|
46
|
+
allowed_params = prepare_allowed_params(allowed_params)
|
47
|
+
condition do
|
48
|
+
@original_params = params.deep_dup
|
49
|
+
filter_params!(params, allowed_params)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def prepare_allowed_params(allowed_params)
|
56
|
+
param_filter = {}
|
57
|
+
allowed_params.each do |key,value|
|
58
|
+
case
|
59
|
+
when key.kind_of?(Hash) && !value
|
60
|
+
param_filter.update(prepare_allowed_params(key))
|
61
|
+
when value.kind_of?(Hash) || value.kind_of?(Array)
|
62
|
+
param_filter[key.to_s] = prepare_allowed_params(value)
|
63
|
+
else
|
64
|
+
param_filter[key.to_s] = value == false ? false : (value || true)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
param_filter.freeze
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module InstanceMethods
|
72
|
+
##
|
73
|
+
# Filters a hash of parameters leaving only allowed ones and possibly
|
74
|
+
# typecasting and processing the others.
|
75
|
+
#
|
76
|
+
# @param [Hash] params
|
77
|
+
# Parameters to filter.
|
78
|
+
# Warning: this hash will be changed by deleting or replacing its values.
|
79
|
+
# @param [Hash] allowed_params
|
80
|
+
# A hash of allowed keys and value classes or processing procs. Supported
|
81
|
+
# scalar classes are: Integer (empty string is cast to nil).
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# filter_params!( { "a" => "1", "b" => "abc", "d" => "drop" },
|
85
|
+
# { "a" => Integer, "b" => true } )
|
86
|
+
# # => { "a" => 1, "b" => "abc" }
|
87
|
+
# filter_params!( { "id" => "", "child" => { "name" => "manny" } },
|
88
|
+
# { "id" => Integer, "child" => { "name" => proc{ |v| v.camelize } } } )
|
89
|
+
# # => { "id" => nil, "child" => { "name" => "Manny" } }
|
90
|
+
# filter_params!( { "a" => ["1", "2", "3"] },
|
91
|
+
# { "a" => true } )
|
92
|
+
# # => { "a" => ["1", "2", "3"] }
|
93
|
+
# filter_params!( { "persons" => {"p-1" => { "name" => "manny", "age" => "50" }, "p-2" => { "name" => "richard", "age" => "50" } } },
|
94
|
+
# { "persons" => { "name" => true } } )
|
95
|
+
# # => { "persons" => {"p-1" => { "name" => "manny" }, "p-2" => { "name" => "richard" } } }
|
96
|
+
#
|
97
|
+
def filter_params!(params, allowed_params)
|
98
|
+
params.each do |key,value|
|
99
|
+
type = allowed_params[key]
|
100
|
+
next if value.kind_of?(Array) && type
|
101
|
+
case
|
102
|
+
when type.kind_of?(Hash)
|
103
|
+
if key == key.pluralize
|
104
|
+
value.each do |array_index,array_value|
|
105
|
+
value[array_index] = filter_params!(array_value, type)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
params[key] = filter_params!(value, type)
|
109
|
+
end
|
110
|
+
when type == Integer
|
111
|
+
params[key] = value.empty? ? nil : value.to_i
|
112
|
+
when type.kind_of?(Proc)
|
113
|
+
params[key] = type.call(value)
|
114
|
+
when type == true
|
115
|
+
else
|
116
|
+
params.delete(key)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Returns the original unfiltered query parameters hash.
|
123
|
+
#
|
124
|
+
def original_params
|
125
|
+
@original_params || params
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -132,6 +132,19 @@ class HttpRouter
|
|
132
132
|
@routes.sort!{ |a, b| a.order <=> b.order }
|
133
133
|
end
|
134
134
|
|
135
|
+
class Node::Glob
|
136
|
+
def to_code
|
137
|
+
id = root.next_counter
|
138
|
+
"request.params << (globbed_params#{id} = [])
|
139
|
+
until request.path.empty?
|
140
|
+
globbed_params#{id} << request.path.shift
|
141
|
+
#{super}
|
142
|
+
end
|
143
|
+
request.path[0,0] = globbed_params#{id}
|
144
|
+
request.params.pop"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
135
148
|
class Node::SpanningRegex
|
136
149
|
def to_code
|
137
150
|
params_count = @ordered_indicies.size
|
@@ -386,6 +399,7 @@ module Padrino
|
|
386
399
|
@_conditions, original_conditions = options.delete(:conditions), @_conditions
|
387
400
|
@_defaults, original_defaults = options, @_defaults
|
388
401
|
@_accepts, original_accepts = options.delete(:accepts), @_accepts
|
402
|
+
@_params, original_params = options.delete(:params), @_params
|
389
403
|
|
390
404
|
# Application defaults.
|
391
405
|
@filters, original_filters = { :before => @filters[:before].dup, :after => @filters[:after].dup }, @filters
|
@@ -401,6 +415,7 @@ module Padrino
|
|
401
415
|
@_controller, @_parents, @_cache = original_controller, original_parent, original_cache
|
402
416
|
@_defaults, @_provides, @_map = original_defaults, original_provides, original_map
|
403
417
|
@_conditions, @_use_format, @_accepts = original_conditions, original_use_format, original_accepts
|
418
|
+
@_params = original_params
|
404
419
|
else
|
405
420
|
include(*args) if extensions.any?
|
406
421
|
end
|
@@ -582,28 +597,32 @@ module Padrino
|
|
582
597
|
##
|
583
598
|
# Instance method for url generation.
|
584
599
|
#
|
600
|
+
# @option options [String] :fragment
|
601
|
+
# An addition to url to identify a portion of requested resource (i.e #something).
|
602
|
+
# @option options [String] :anchor
|
603
|
+
# Synonym for fragment.
|
604
|
+
#
|
585
605
|
# @example
|
586
606
|
# url(:show, :id => 1)
|
587
607
|
# url(:show, :name => 'test', :id => 24)
|
588
608
|
# url(:show, 1)
|
589
609
|
# url(:controller_name, :show, :id => 21)
|
590
610
|
# url(:controller_show, :id => 29)
|
611
|
+
# url(:index, :fragment => 'comments')
|
591
612
|
#
|
592
613
|
def url(*args)
|
593
|
-
params = args.extract_options!
|
614
|
+
params = args.extract_options!
|
594
615
|
names, params_array = args.partition{|a| a.is_a?(Symbol)}
|
595
616
|
name = names[0, 2].join(" ").to_sym # route name is concatenated with underscores
|
596
|
-
|
597
|
-
|
598
|
-
params = value_to_param(params.symbolize_keys)
|
599
|
-
end
|
617
|
+
fragment = params.delete(:fragment) || params.delete(:anchor)
|
618
|
+
params = value_to_param(params.symbolize_keys)
|
600
619
|
url =
|
601
620
|
if params_array.empty?
|
602
621
|
compiled_router.path(name, params)
|
603
622
|
else
|
604
623
|
compiled_router.path(name, *(params_array << params))
|
605
624
|
end
|
606
|
-
rebase_url(url)
|
625
|
+
rebase_url(fragment ? url + '#' + fragment : url)
|
607
626
|
rescue HttpRouter::InvalidRouteException
|
608
627
|
route_error = "route mapping for url(#{name.inspect}) could not be found!"
|
609
628
|
raise Padrino::Routing::UnrecognizedException.new(route_error)
|
@@ -621,8 +640,8 @@ module Padrino
|
|
621
640
|
def rebase_url(url)
|
622
641
|
if url.start_with?('/')
|
623
642
|
new_url = ''
|
624
|
-
new_url << conform_uri(uri_root) if defined?(uri_root)
|
625
643
|
new_url << conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
|
644
|
+
new_url << conform_uri(uri_root) if defined?(uri_root)
|
626
645
|
new_url << url
|
627
646
|
else
|
628
647
|
url.blank? ? '/' : url
|
@@ -693,6 +712,7 @@ module Padrino
|
|
693
712
|
route_options = options.dup
|
694
713
|
route_options[:provides] = @_provides if @_provides
|
695
714
|
route_options[:accepts] = @_accepts if @_accepts
|
715
|
+
route_options[:params] = @_params unless @_params.nil? || route_options.include?(:params)
|
696
716
|
|
697
717
|
# Add Sinatra condition to check rack-protection failure.
|
698
718
|
if protect_from_csrf && (report_csrf_failure || allow_disabled_csrf)
|
@@ -767,6 +787,13 @@ module Padrino
|
|
767
787
|
def parse_route(path, options, verb)
|
768
788
|
route_options = {}
|
769
789
|
|
790
|
+
if options[:params] == true
|
791
|
+
options.delete(:params)
|
792
|
+
elsif options.include?(:params)
|
793
|
+
options[:params] ||= []
|
794
|
+
options[:params] += options[:with] if options[:with]
|
795
|
+
end
|
796
|
+
|
770
797
|
# We need check if path is a symbol, if that it's a named route.
|
771
798
|
map = options.delete(:map)
|
772
799
|
|
@@ -3,6 +3,7 @@ require 'padrino-core/application/routing'
|
|
3
3
|
require 'padrino-core/application/show_exceptions'
|
4
4
|
require 'padrino-core/application/authenticity_token'
|
5
5
|
require 'padrino-core/application/application_setup'
|
6
|
+
require 'padrino-core/application/params_protection'
|
6
7
|
|
7
8
|
module Padrino
|
8
9
|
##
|
@@ -14,6 +15,7 @@ module Padrino
|
|
14
15
|
class Application < Sinatra::Base
|
15
16
|
register Padrino::ApplicationSetup
|
16
17
|
register Padrino::Routing
|
18
|
+
register Padrino::ParamsProtection
|
17
19
|
|
18
20
|
##
|
19
21
|
# Returns the logger for this application.
|
data/lib/padrino-core/logger.rb
CHANGED
@@ -101,7 +101,7 @@ module Padrino
|
|
101
101
|
duration = Time.now - began_at
|
102
102
|
color = :red if duration > 1
|
103
103
|
action = colorize(action.to_s.upcase.rjust(@_pad), color)
|
104
|
-
duration = colorize('%0.4fs' % duration, :bold
|
104
|
+
duration = colorize('%0.4fs' % duration, color, :bold)
|
105
105
|
push "#{action} (#{duration}) #{message}", level
|
106
106
|
end
|
107
107
|
|
@@ -203,11 +203,11 @@ module Padrino
|
|
203
203
|
# Colors for levels
|
204
204
|
ColoredLevels = {
|
205
205
|
:fatal => [:bold, :red],
|
206
|
-
:error => [:red],
|
207
|
-
:warn => [:yellow],
|
208
|
-
:info => [:green],
|
209
|
-
:debug => [:cyan],
|
210
|
-
:devel => [:magenta]
|
206
|
+
:error => [:default, :red],
|
207
|
+
:warn => [:default, :yellow],
|
208
|
+
:info => [:default, :green],
|
209
|
+
:debug => [:default, :cyan],
|
210
|
+
:devel => [:default, :magenta]
|
211
211
|
} unless defined?(ColoredLevels)
|
212
212
|
|
213
213
|
##
|
@@ -218,14 +218,11 @@ module Padrino
|
|
218
218
|
# @see Padrino::Logging::ColorizedLogger::ColoredLevels
|
219
219
|
#
|
220
220
|
def colorize(string, *colors)
|
221
|
-
|
222
|
-
string = string.colorize(c)
|
223
|
-
end
|
224
|
-
string
|
221
|
+
string.colorize(:color => colors[0], :mode => colors[1])
|
225
222
|
end
|
226
223
|
|
227
224
|
def stylized_level(level)
|
228
|
-
style = ColoredLevels[level].map
|
225
|
+
style = "\e[%d;%dm" % ColoredLevels[level].map{|color| String::Colorizer.modes[color] || String::Colorizer.colors[color] }
|
229
226
|
[style, super, "\e[0m"] * ''
|
230
227
|
end
|
231
228
|
end
|
@@ -437,7 +434,7 @@ module Padrino
|
|
437
434
|
env["PATH_INFO"],
|
438
435
|
env["QUERY_STRING"].empty? ? "" : "?" + env["QUERY_STRING"],
|
439
436
|
' - ',
|
440
|
-
logger.colorize(status.to_s[0..3], :bold),
|
437
|
+
logger.colorize(status.to_s[0..3], :default, :bold),
|
441
438
|
' ',
|
442
439
|
code_to_name(status)
|
443
440
|
] * '',
|
@@ -6,7 +6,7 @@ module Padrino
|
|
6
6
|
def clear!
|
7
7
|
files.each_key do |file|
|
8
8
|
remove(file)
|
9
|
-
|
9
|
+
Reloader.remove_feature(file)
|
10
10
|
end
|
11
11
|
@files = {}
|
12
12
|
end
|
@@ -14,7 +14,7 @@ module Padrino
|
|
14
14
|
def remove(name)
|
15
15
|
file = files[name] || return
|
16
16
|
file[:constants].each{ |constant| Reloader.remove_constant(constant) }
|
17
|
-
file[:features].each{ |feature|
|
17
|
+
file[:features].each{ |feature| Reloader.remove_feature(feature) }
|
18
18
|
files.delete(name)
|
19
19
|
end
|
20
20
|
|
@@ -27,7 +27,7 @@ module Padrino
|
|
27
27
|
}
|
28
28
|
features = file && file[:features] || []
|
29
29
|
features.each{ |feature| Reloader.safe_load(feature, :force => true) }
|
30
|
-
|
30
|
+
Reloader.remove_feature(name) if old_features.include?(name)
|
31
31
|
end
|
32
32
|
|
33
33
|
def commit(name)
|
@@ -87,6 +87,7 @@ module Padrino
|
|
87
87
|
began_at = Time.now
|
88
88
|
file = figure_path(file)
|
89
89
|
return unless options[:force] || file_changed?(file)
|
90
|
+
return require(file) if external_feature?(file)
|
90
91
|
|
91
92
|
Storage.prepare(file) # might call #safe_load recursively
|
92
93
|
logger.devel(file_new?(file) ? :loading : :reload, began_at, file)
|
@@ -116,6 +117,13 @@ module Padrino
|
|
116
117
|
rescue NameError
|
117
118
|
end
|
118
119
|
|
120
|
+
##
|
121
|
+
# Remove a feature from $LOADED_FEATURES so it can be required again.
|
122
|
+
#
|
123
|
+
def remove_feature(file)
|
124
|
+
$LOADED_FEATURES.delete(file) unless external_feature?(file)
|
125
|
+
end
|
126
|
+
|
119
127
|
##
|
120
128
|
# Returns the list of special tracked files for Reloader.
|
121
129
|
#
|
@@ -229,6 +237,13 @@ module Padrino
|
|
229
237
|
files + special_files
|
230
238
|
end
|
231
239
|
|
240
|
+
##
|
241
|
+
# Tells if a feature is internal or external for Padrino project.
|
242
|
+
#
|
243
|
+
def external_feature?(file)
|
244
|
+
!file.start_with?(Padrino.root)
|
245
|
+
end
|
246
|
+
|
232
247
|
def constant_excluded?(const)
|
233
248
|
(exclude_constants - include_constants).any?{ |c| const._orig_klass_name.start_with?(c) }
|
234
249
|
end
|
data/lib/padrino-core/router.rb
CHANGED
@@ -79,13 +79,17 @@ module Padrino
|
|
79
79
|
|
80
80
|
rest = "/" if rest.empty?
|
81
81
|
|
82
|
-
|
82
|
+
env['SCRIPT_NAME'] = script_name + path
|
83
|
+
env['PATH_INFO'] = rest
|
84
|
+
last_result = app.call(env)
|
83
85
|
|
84
86
|
cascade_setting = app.respond_to?(:cascade) ? app.cascade : true
|
85
87
|
cascade_statuses = cascade_setting.respond_to?(:include?) ? cascade_setting : Mounter::DEFAULT_CASCADE
|
86
88
|
break unless cascade_setting && cascade_statuses.include?(last_result[0])
|
87
89
|
end
|
88
90
|
last_result || begin
|
91
|
+
env['SCRIPT_NAME'] = script_name
|
92
|
+
env['PATH_INFO'] = path_info
|
89
93
|
Padrino::Logger::Rack.new(nil,'/').send(:log, env, 404, {}, began_at) if logger.debug?
|
90
94
|
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path_info}"]]
|
91
95
|
end
|
data/lib/padrino-core/server.rb
CHANGED
@@ -44,6 +44,8 @@ module Padrino
|
|
44
44
|
options.update(detect_address(options))
|
45
45
|
options[:pid] = prepare_pid(options[:pid]) if options[:daemonize]
|
46
46
|
options[:server] = detect_rack_handler if options[:server].blank?
|
47
|
+
# disable Webrick AccessLog
|
48
|
+
options[:AccessLog] = []
|
47
49
|
new(options, app).start
|
48
50
|
end
|
49
51
|
|
data/lib/padrino-core/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_support/logger_silence'
|
data/test/test_application.rb
CHANGED
@@ -65,6 +65,36 @@ describe "Application" do
|
|
65
65
|
assert_equal 'shared', body
|
66
66
|
end
|
67
67
|
|
68
|
+
it 'should able to set custome session management' do
|
69
|
+
class PadrinoTestApp3 < Padrino::Application
|
70
|
+
set :sessions, :use => Rack::Session::Pool
|
71
|
+
end
|
72
|
+
Padrino.mount("PadrinoTestApp3").to("/")
|
73
|
+
PadrinoTestApp3.get('/write') { session[:foo] = "pool" }
|
74
|
+
PadrinoTestApp3.get('/read') { session[:foo] }
|
75
|
+
@app = Padrino.application
|
76
|
+
get '/write'
|
77
|
+
get '/read'
|
78
|
+
assert_equal 'pool', body
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should have different session values in different session management' do
|
82
|
+
class PadrinoTestApp3 < Padrino::Application
|
83
|
+
enable :sessions
|
84
|
+
end
|
85
|
+
class PadrinoTestApp4 < Padrino::Application
|
86
|
+
set :sessions, :use => Rack::Session::Pool
|
87
|
+
end
|
88
|
+
Padrino.mount("PadrinoTestApp3").to("/write")
|
89
|
+
Padrino.mount("PadrinoTestApp4").to("/read")
|
90
|
+
PadrinoTestApp3.get('/') { session[:foo] = "cookie" }
|
91
|
+
PadrinoTestApp4.get('/') { session[:foo] }
|
92
|
+
@app = Padrino.application
|
93
|
+
get '/write'
|
94
|
+
get '/read'
|
95
|
+
assert_equal '', body
|
96
|
+
end
|
97
|
+
|
68
98
|
# compare to: test_routing: allow global provides
|
69
99
|
it 'should set content_type to nil if none can be determined' do
|
70
100
|
mock_app do
|
@@ -27,6 +27,17 @@ describe "Application" do
|
|
27
27
|
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
|
28
28
|
assert_equal 403, status
|
29
29
|
end
|
30
|
+
|
31
|
+
it 'should allow requests with correct X-CSRF-TOKEN' do
|
32
|
+
post "/", {}, 'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "a"
|
33
|
+
assert_equal 200, status
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should not allow requests with incorrect X-CSRF-TOKEN' do
|
37
|
+
post "/", {}, 'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "b"
|
38
|
+
assert_equal 403, status
|
39
|
+
end
|
40
|
+
|
30
41
|
end
|
31
42
|
|
32
43
|
describe "without CSRF protection on" do
|
@@ -45,13 +56,23 @@ describe "Application" do
|
|
45
56
|
|
46
57
|
it 'should allow requests with correct tokens' do
|
47
58
|
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
|
48
|
-
assert_equal 200, status
|
59
|
+
assert_equal 200, status
|
49
60
|
end
|
50
61
|
|
51
62
|
it 'should allow requests with incorrect tokens' do
|
52
63
|
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
|
53
64
|
assert_equal 200, status
|
54
65
|
end
|
66
|
+
|
67
|
+
it 'should allow requests with correct X-CSRF-TOKEN' do
|
68
|
+
post "/", {}, 'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "a"
|
69
|
+
assert_equal 200, status
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should allow requests with incorrect X-CSRF-TOKEN' do
|
73
|
+
post "/", {}, 'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "b"
|
74
|
+
assert_equal 200, status
|
75
|
+
end
|
55
76
|
end
|
56
77
|
|
57
78
|
describe "with optional CSRF protection" do
|
data/test/test_logger.rb
CHANGED
@@ -71,7 +71,7 @@ describe "PadrinoLogger" do
|
|
71
71
|
get("/"){ "Foo" }
|
72
72
|
end
|
73
73
|
get "/"
|
74
|
-
assert_match /\e\[
|
74
|
+
assert_match /\e\[1;9m200\e\[0m OK/, Padrino.logger.log.string
|
75
75
|
end
|
76
76
|
|
77
77
|
describe "static asset logging" do
|
@@ -110,11 +110,11 @@ describe "PadrinoLogger" do
|
|
110
110
|
it 'should output under debug level' do
|
111
111
|
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:debug] }
|
112
112
|
access_to_mock_app
|
113
|
-
assert_match /\e\[36m DEBUG\e\[0m/, Padrino.logger.log.string
|
113
|
+
assert_match /\e\[0;36m DEBUG\e\[0m/, Padrino.logger.log.string
|
114
114
|
|
115
115
|
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:devel] }
|
116
116
|
access_to_mock_app
|
117
|
-
assert_match /\e\[36m DEBUG\e\[0m/, Padrino.logger.log.string
|
117
|
+
assert_match /\e\[0;36m DEBUG\e\[0m/, Padrino.logger.log.string
|
118
118
|
end
|
119
119
|
it 'should not output over debug level' do
|
120
120
|
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:info] }
|
@@ -164,7 +164,7 @@ describe "alternate logger" do
|
|
164
164
|
end
|
165
165
|
get "/"
|
166
166
|
|
167
|
-
assert_match /\e\[
|
167
|
+
assert_match /\e\[1;9m200\e\[0m OK/, @log.string
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -190,7 +190,7 @@ describe "alternate logger: stdlib logger" do
|
|
190
190
|
end
|
191
191
|
get "/"
|
192
192
|
|
193
|
-
assert_match /\e\[
|
193
|
+
assert_match /\e\[1;9m200\e\[0m OK/, @log.string
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
@@ -207,7 +207,7 @@ describe "options :colorize_logging" do
|
|
207
207
|
Padrino::Logger.setup!
|
208
208
|
|
209
209
|
access_to_mock_app
|
210
|
-
assert_match /\e\[
|
210
|
+
assert_match /\e\[1;9m200\e\[0m OK/, Padrino.logger.log.string
|
211
211
|
end
|
212
212
|
end
|
213
213
|
describe 'set value is false' do
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/helper')
|
2
|
+
require 'active_support/core_ext/hash/conversions'
|
3
|
+
|
4
|
+
describe "Padrino::ParamsProtection" do
|
5
|
+
before do
|
6
|
+
@teri = { 'name' => 'Teri Bauer', 'position' => 'baby' }
|
7
|
+
@kim = { 'name' => 'Kim Bauer', 'position' => 'daughter', 'child' => @teri }
|
8
|
+
@jack = { 'name' => 'Jack Bauer', 'position' => 'terrorist', 'child' => @kim }
|
9
|
+
@family = { 'name' => 'Bauer', 'persons' => { 1 => @teri, 2 => @kim, 3 => @jack } }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should drop all parameters except allowed ones' do
|
13
|
+
result = nil
|
14
|
+
mock_app do
|
15
|
+
post :basic, :params => [ :name ] do
|
16
|
+
result = params
|
17
|
+
''
|
18
|
+
end
|
19
|
+
end
|
20
|
+
post '/basic?' + @jack.to_query
|
21
|
+
assert_equal({ 'name' => @jack['name'] }, result)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should preserve original params' do
|
25
|
+
result = nil
|
26
|
+
mock_app do
|
27
|
+
post :basic, :params => [ :name ] do
|
28
|
+
result = original_params
|
29
|
+
''
|
30
|
+
end
|
31
|
+
end
|
32
|
+
post '/basic?' + @jack.to_query
|
33
|
+
assert_equal(@jack, result)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should work with recursive data' do
|
37
|
+
result = nil
|
38
|
+
mock_app do
|
39
|
+
post :basic, :params => [ :name, :child => [ :name, :child => [ :name ] ] ] do
|
40
|
+
result = [params, original_params]
|
41
|
+
''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
post '/basic?' + @jack.to_query
|
45
|
+
assert_equal(
|
46
|
+
[
|
47
|
+
{ 'name' => @jack['name'], 'child' => { 'name' => @kim['name'], 'child' => { 'name' => @teri['name'] } } },
|
48
|
+
@jack
|
49
|
+
],
|
50
|
+
result
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should be able to process the data' do
|
55
|
+
result = nil
|
56
|
+
mock_app do
|
57
|
+
post :basic, :params => [ :name, :position => proc{ |v| 'anti-'+v } ] do
|
58
|
+
result = params
|
59
|
+
''
|
60
|
+
end
|
61
|
+
end
|
62
|
+
post '/basic?' + @jack.to_query
|
63
|
+
assert_equal({ 'name' => @jack['name'], 'position' => 'anti-terrorist' }, result)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should pass :with parameters' do
|
67
|
+
result = nil
|
68
|
+
mock_app do
|
69
|
+
post :basic, :with => [:id, :tag], :params => [ :name ] do
|
70
|
+
result = params
|
71
|
+
''
|
72
|
+
end
|
73
|
+
end
|
74
|
+
post '/basic/24/42?' + @jack.to_query
|
75
|
+
assert_equal({ 'name' => @jack['name'], 'id' => '24', 'tag' => '42' }, result)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should understand true or false values' do
|
79
|
+
result = nil
|
80
|
+
mock_app do
|
81
|
+
get :hide, :with => [ :id ], :params => false do
|
82
|
+
result = params
|
83
|
+
''
|
84
|
+
end
|
85
|
+
get :show, :with => [ :id ], :params => true do
|
86
|
+
result = params
|
87
|
+
''
|
88
|
+
end
|
89
|
+
end
|
90
|
+
get '/hide/1?' + @jack.to_query
|
91
|
+
assert_equal({"id"=>"1"}, result)
|
92
|
+
get '/show/1?' + @jack.to_query
|
93
|
+
assert_equal({"id"=>"1"}.merge(@jack), result)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should be configurable with controller options' do
|
97
|
+
result = nil
|
98
|
+
mock_app do
|
99
|
+
controller :persons, :params => [ :name ] do
|
100
|
+
post :create, :params => [ :name, :position ] do
|
101
|
+
result = params
|
102
|
+
''
|
103
|
+
end
|
104
|
+
post :update, :with => [ :id ] do
|
105
|
+
result = params
|
106
|
+
''
|
107
|
+
end
|
108
|
+
post :delete, :params => true do
|
109
|
+
result = params
|
110
|
+
''
|
111
|
+
end
|
112
|
+
post :destroy, :with => [ :id ], :params => false do
|
113
|
+
result = params
|
114
|
+
''
|
115
|
+
end
|
116
|
+
end
|
117
|
+
controller :noparam, :params => false do
|
118
|
+
get :index do
|
119
|
+
result = params
|
120
|
+
''
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
post '/persons/create?' + @jack.to_query
|
125
|
+
assert_equal({ 'name' => @jack['name'], 'position' => 'terrorist' }, result)
|
126
|
+
post '/persons/update/1?name=Chloe+O\'Brian&position=hacker'
|
127
|
+
assert_equal({ 'id' => '1', 'name' => 'Chloe O\'Brian' }, result)
|
128
|
+
post '/persons/delete?' + @jack.to_query
|
129
|
+
assert_equal(@jack, result)
|
130
|
+
post '/persons/destroy/1?' + @jack.to_query
|
131
|
+
assert_equal({"id"=>"1"}, result)
|
132
|
+
get '/noparam?a=1;b=2'
|
133
|
+
assert_equal({}, result)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should successfully filter hashes' do
|
137
|
+
result = nil
|
138
|
+
mock_app do
|
139
|
+
post :family, :params => [ :persons => [ :name ] ] do
|
140
|
+
result = params
|
141
|
+
''
|
142
|
+
end
|
143
|
+
end
|
144
|
+
post '/family?' + @family.to_query
|
145
|
+
assert_equal({"persons" => {"3" => {"name" => @jack["name"]}, "2" => {"name" => @kim["name"]}, "1" => {"name" => @teri["name"]}}}, result)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should pass arrays' do
|
149
|
+
result = nil
|
150
|
+
mock_app do
|
151
|
+
post :family, :params => [ :names => [] ] do
|
152
|
+
result = params
|
153
|
+
''
|
154
|
+
end
|
155
|
+
end
|
156
|
+
post '/family?names[]=Jack&names[]=Kim&names[]=Teri'
|
157
|
+
assert_equal({"names" => %w[Jack Kim Teri]}, result)
|
158
|
+
end
|
159
|
+
end
|
@@ -84,7 +84,7 @@ describe "SimpleReloader" do
|
|
84
84
|
assert_equal 2, @app.filters[:after].size # app + content-type + padrino-flash
|
85
85
|
assert_equal 0, @app.middleware.size
|
86
86
|
assert_equal 4, @app.routes.size # GET+HEAD of "/" + GET+HEAD of "/rand" = 4
|
87
|
-
assert_equal
|
87
|
+
assert_equal 4, @app.extensions.size # [Padrino::ApplicationSetup, Padrino::ParamsProtection, Padrino::Routing, Padrino::Flash]
|
88
88
|
assert_equal 0, @app.templates.size
|
89
89
|
@app.reload!
|
90
90
|
get "/rand"
|
@@ -94,7 +94,7 @@ describe "SimpleReloader" do
|
|
94
94
|
assert_equal 2, @app.filters[:after].size
|
95
95
|
assert_equal 0, @app.middleware.size
|
96
96
|
assert_equal 4, @app.routes.size # GET+HEAD of "/" = 2
|
97
|
-
assert_equal
|
97
|
+
assert_equal 4, @app.extensions.size # [Padrino::ApplicationSetup, Padrino::ParamsProtection, Padrino::Routing, Padrino::Flash]
|
98
98
|
assert_equal 0, @app.templates.size
|
99
99
|
end
|
100
100
|
end
|
data/test/test_router.rb
CHANGED
@@ -262,4 +262,20 @@ describe "Router" do
|
|
262
262
|
res = Rack::MockRequest.new(Padrino.application).get("/foo/", "HTTP_HOST" => "bar.padrino.org")
|
263
263
|
assert res.ok?
|
264
264
|
end
|
265
|
+
|
266
|
+
it 'should keep the same environment object' do
|
267
|
+
app = lambda { |env|
|
268
|
+
env['path'] = env['PATH_INFO']
|
269
|
+
[200, {'Content-Type' => 'text/plain'}, [""]]
|
270
|
+
}
|
271
|
+
map = Padrino::Router.new(
|
272
|
+
{ :path => '/bar', :to => app },
|
273
|
+
{ :path => '/foo/bar', :to => app },
|
274
|
+
{ :path => '/foo', :to => app }
|
275
|
+
)
|
276
|
+
|
277
|
+
env = Rack::MockRequest.env_for("/bar/foo")
|
278
|
+
map.call(env)
|
279
|
+
assert_equal "/foo", env["path"]
|
280
|
+
end
|
265
281
|
end
|
data/test/test_routing.rb
CHANGED
@@ -7,6 +7,7 @@ class FooError < RuntimeError; end
|
|
7
7
|
describe "Routing" do
|
8
8
|
before do
|
9
9
|
Padrino.clear!
|
10
|
+
ENV['RACK_BASE_URI'] = nil
|
10
11
|
end
|
11
12
|
|
12
13
|
it 'should serve static files with simple cache control' do
|
@@ -215,6 +216,8 @@ describe "Routing" do
|
|
215
216
|
get(:foo, :with => :id){ |id| "/foo/#{id}" }
|
216
217
|
get([:foo, :id]){ |id| "/foo/#{id}" }
|
217
218
|
get(:hash, :with => :id){ url(:hash, :id => 1) }
|
219
|
+
get(:anchor) { url(:anchor, :anchor => 'comments') }
|
220
|
+
get(:fragment) { url(:anchor, :fragment => 'comments') }
|
218
221
|
get([:hash, :id]){ url(:hash, :id => 1) }
|
219
222
|
get(:array, :with => :id){ url(:array, 23) }
|
220
223
|
get([:array, :id]){ url(:array, 23) }
|
@@ -232,6 +235,10 @@ describe "Routing" do
|
|
232
235
|
assert_equal "/foo/123", body
|
233
236
|
get "/hash/2"
|
234
237
|
assert_equal "/hash/1", body
|
238
|
+
get "/anchor"
|
239
|
+
assert_equal "/anchor#comments", body
|
240
|
+
get "/fragment"
|
241
|
+
assert_equal "/anchor#comments", body
|
235
242
|
get "/array/23"
|
236
243
|
assert_equal "/array/23", body
|
237
244
|
get "/hash_with_extra/1"
|
@@ -858,6 +865,18 @@ describe "Routing" do
|
|
858
865
|
ENV['RACK_BASE_URI'] = nil
|
859
866
|
end
|
860
867
|
|
868
|
+
it 'should use uri_root and RACK_BASE_URI' do
|
869
|
+
mock_app do
|
870
|
+
controller :foo do
|
871
|
+
get(:bar){ "bar" }
|
872
|
+
end
|
873
|
+
end
|
874
|
+
ENV['RACK_BASE_URI'] = '/base'
|
875
|
+
@app.uri_root = 'testing'
|
876
|
+
assert_equal '/base/testing/foo/bar', @app.url(:foo, :bar)
|
877
|
+
ENV['RACK_BASE_URI'] = nil
|
878
|
+
end
|
879
|
+
|
861
880
|
it 'should reset routes' do
|
862
881
|
mock_app do
|
863
882
|
get("/"){ "foo" }
|
@@ -898,6 +917,17 @@ describe "Routing" do
|
|
898
917
|
assert_equal "hello", body
|
899
918
|
end
|
900
919
|
|
920
|
+
it 'should set the params correctly even if using prioritized routes' do
|
921
|
+
mock_app do
|
922
|
+
get("*__sinatra__/:image.png"){}
|
923
|
+
get "/:primary/:secondary", :priority => :low do
|
924
|
+
"#{params[:primary]} #{params[:secondary]}"
|
925
|
+
end
|
926
|
+
end
|
927
|
+
get "/abc/def"
|
928
|
+
assert_equal "abc def", body
|
929
|
+
end
|
930
|
+
|
901
931
|
it 'should catch all after controllers' do
|
902
932
|
mock_app do
|
903
933
|
get(:index, :with => :slug, :priority => :low) { "catch all" }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: padrino-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Padrino Team
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-05-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: padrino-support
|
@@ -19,14 +19,14 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - '='
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.12.
|
22
|
+
version: 0.12.2
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - '='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.12.
|
29
|
+
version: 0.12.2
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: sinatra
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- lib/padrino-core/application/application_setup.rb
|
118
118
|
- lib/padrino-core/application/authenticity_token.rb
|
119
119
|
- lib/padrino-core/application/flash.rb
|
120
|
+
- lib/padrino-core/application/params_protection.rb
|
120
121
|
- lib/padrino-core/application/routing.rb
|
121
122
|
- lib/padrino-core/application/show_exceptions.rb
|
122
123
|
- lib/padrino-core/caller.rb
|
@@ -148,6 +149,7 @@ files:
|
|
148
149
|
- test/fixtures/apps/complex.rb
|
149
150
|
- test/fixtures/apps/demo_app.rb
|
150
151
|
- test/fixtures/apps/demo_demo.rb
|
152
|
+
- test/fixtures/apps/helpers/support.rb
|
151
153
|
- test/fixtures/apps/helpers/system_helpers.rb
|
152
154
|
- test/fixtures/apps/kiq.rb
|
153
155
|
- test/fixtures/apps/lib/myklass.rb
|
@@ -174,6 +176,7 @@ files:
|
|
174
176
|
- test/test_locale.rb
|
175
177
|
- test/test_logger.rb
|
176
178
|
- test/test_mounter.rb
|
179
|
+
- test/test_params_protection.rb
|
177
180
|
- test/test_reloader_complex.rb
|
178
181
|
- test/test_reloader_simple.rb
|
179
182
|
- test/test_reloader_system.rb
|
@@ -214,6 +217,7 @@ test_files:
|
|
214
217
|
- test/fixtures/apps/complex.rb
|
215
218
|
- test/fixtures/apps/demo_app.rb
|
216
219
|
- test/fixtures/apps/demo_demo.rb
|
220
|
+
- test/fixtures/apps/helpers/support.rb
|
217
221
|
- test/fixtures/apps/helpers/system_helpers.rb
|
218
222
|
- test/fixtures/apps/kiq.rb
|
219
223
|
- test/fixtures/apps/lib/myklass.rb
|
@@ -240,6 +244,7 @@ test_files:
|
|
240
244
|
- test/test_locale.rb
|
241
245
|
- test/test_logger.rb
|
242
246
|
- test/test_mounter.rb
|
247
|
+
- test/test_params_protection.rb
|
243
248
|
- test/test_reloader_complex.rb
|
244
249
|
- test/test_reloader_simple.rb
|
245
250
|
- test/test_reloader_system.rb
|