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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f503cfe1974ffddc13bc280ad2abfbe7a47f397b
4
- data.tar.gz: 9d7b43288edeb327d60848ac5a7f539e05f5ec0b
3
+ metadata.gz: dec1c7a3513869018c0576596a7888261759d25c
4
+ data.tar.gz: 6ba616394f122fc4bf166facc114554cbddf2331
5
5
  SHA512:
6
- metadata.gz: 5503c652282579166fbafe304e349f97b8956555eaabb7edfae2952daaec2e18c5656f742055fb98a5e3386f3e0d47183ef00ea294a8eafe5a9c1cdc4ea6b14f
7
- data.tar.gz: a250c8dc4dc33f17a6f5c3072f918733b9de07005753c1a8efc55662a6f9ba2db1d2c31a53c988283ac3c45f098a25ffdbcf2b5881006ddd20fff493c58f18a9
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! # parameters is hash at end
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
- if params.is_a?(Hash)
597
- params[:format] = params[:format].to_s unless params[:format].nil?
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.
@@ -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, color)
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
- colors.each do |c|
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 { |c| "\e[%dm" % String::Colorizer.colors[c] } * ''
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
- $LOADED_FEATURES.delete(file)
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| $LOADED_FEATURES.delete(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
- $LOADED_FEATURES.delete(name) if old_features.include?(name)
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
@@ -79,13 +79,17 @@ module Padrino
79
79
 
80
80
  rest = "/" if rest.empty?
81
81
 
82
- last_result = app.call(env.merge('SCRIPT_NAME' => script_name + path, 'PATH_INFO' => rest))
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
@@ -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
 
@@ -6,7 +6,7 @@
6
6
  #
7
7
  module Padrino
8
8
  # The version constant for the current version of Padrino.
9
- VERSION = '0.12.1' unless defined?(Padrino::VERSION)
9
+ VERSION = '0.12.2' unless defined?(Padrino::VERSION)
10
10
 
11
11
  #
12
12
  # The current Padrino version.
@@ -0,0 +1 @@
1
+ require 'active_support/logger_silence'
@@ -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\[1m200\e\[0m OK/, Padrino.logger.log.string
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\[1m200\e\[0m OK/, @log.string
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\[1m200\e\[0m OK/, @log.string
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\[1m200\e\[0m OK/, Padrino.logger.log.string
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 3, @app.extensions.size # [Padrino::ApplicationSetup, Padrino::Routing, Padrino::Flash]
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 3, @app.extensions.size # [Padrino::ApplicationSetup, Padrino::Routing, Padrino::Flash]
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.1
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-04-04 00:00:00.000000000 Z
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.1
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.1
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