padrino-core 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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