actionpack 4.2.11.3 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +379 -462
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller.rb +0 -2
  6. data/lib/abstract_controller/base.rb +17 -32
  7. data/lib/abstract_controller/callbacks.rb +52 -19
  8. data/lib/abstract_controller/collector.rb +4 -9
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  11. data/lib/abstract_controller/rendering.rb +27 -22
  12. data/lib/abstract_controller/translation.rb +8 -7
  13. data/lib/action_controller.rb +4 -3
  14. data/lib/action_controller/api.rb +146 -0
  15. data/lib/action_controller/base.rb +6 -10
  16. data/lib/action_controller/caching.rb +1 -3
  17. data/lib/action_controller/caching/fragments.rb +48 -3
  18. data/lib/action_controller/form_builder.rb +48 -0
  19. data/lib/action_controller/log_subscriber.rb +1 -10
  20. data/lib/action_controller/metal.rb +89 -62
  21. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  22. data/lib/action_controller/metal/conditional_get.rb +65 -24
  23. data/lib/action_controller/metal/cookies.rb +0 -2
  24. data/lib/action_controller/metal/data_streaming.rb +2 -22
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  26. data/lib/action_controller/metal/exceptions.rb +11 -6
  27. data/lib/action_controller/metal/force_ssl.rb +6 -6
  28. data/lib/action_controller/metal/head.rb +14 -7
  29. data/lib/action_controller/metal/helpers.rb +9 -5
  30. data/lib/action_controller/metal/http_authentication.rb +37 -38
  31. data/lib/action_controller/metal/implicit_render.rb +23 -6
  32. data/lib/action_controller/metal/instrumentation.rb +0 -1
  33. data/lib/action_controller/metal/live.rb +17 -55
  34. data/lib/action_controller/metal/mime_responds.rb +17 -37
  35. data/lib/action_controller/metal/params_wrapper.rb +8 -8
  36. data/lib/action_controller/metal/redirecting.rb +32 -9
  37. data/lib/action_controller/metal/renderers.rb +10 -8
  38. data/lib/action_controller/metal/rendering.rb +38 -6
  39. data/lib/action_controller/metal/request_forgery_protection.rb +67 -35
  40. data/lib/action_controller/metal/rescue.rb +2 -4
  41. data/lib/action_controller/metal/streaming.rb +4 -4
  42. data/lib/action_controller/metal/strong_parameters.rb +231 -78
  43. data/lib/action_controller/metal/testing.rb +1 -12
  44. data/lib/action_controller/metal/url_for.rb +12 -5
  45. data/lib/action_controller/renderer.rb +111 -0
  46. data/lib/action_controller/template_assertions.rb +9 -0
  47. data/lib/action_controller/test_case.rb +267 -363
  48. data/lib/action_dispatch.rb +2 -1
  49. data/lib/action_dispatch/http/cache.rb +23 -26
  50. data/lib/action_dispatch/http/filter_parameters.rb +6 -8
  51. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  52. data/lib/action_dispatch/http/headers.rb +28 -11
  53. data/lib/action_dispatch/http/mime_negotiation.rb +40 -26
  54. data/lib/action_dispatch/http/mime_type.rb +92 -61
  55. data/lib/action_dispatch/http/mime_types.rb +1 -4
  56. data/lib/action_dispatch/http/parameter_filter.rb +18 -8
  57. data/lib/action_dispatch/http/parameters.rb +45 -41
  58. data/lib/action_dispatch/http/request.rb +146 -82
  59. data/lib/action_dispatch/http/response.rb +180 -99
  60. data/lib/action_dispatch/http/url.rb +117 -8
  61. data/lib/action_dispatch/journey/formatter.rb +34 -28
  62. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  64. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  65. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  66. data/lib/action_dispatch/journey/parser_extras.rb +4 -0
  67. data/lib/action_dispatch/journey/path/pattern.rb +37 -41
  68. data/lib/action_dispatch/journey/route.rb +71 -17
  69. data/lib/action_dispatch/journey/router.rb +5 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  71. data/lib/action_dispatch/journey/routes.rb +14 -15
  72. data/lib/action_dispatch/journey/visitors.rb +86 -43
  73. data/lib/action_dispatch/middleware/cookies.rb +184 -135
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +115 -45
  75. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -20
  76. data/lib/action_dispatch/middleware/flash.rb +61 -45
  77. data/lib/action_dispatch/middleware/load_interlock.rb +21 -0
  78. data/lib/action_dispatch/middleware/params_parser.rb +30 -46
  79. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  80. data/lib/action_dispatch/middleware/reloader.rb +2 -4
  81. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  82. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  83. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  84. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  85. data/lib/action_dispatch/middleware/session/cookie_store.rb +29 -23
  86. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  87. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  88. data/lib/action_dispatch/middleware/ssl.rb +93 -36
  89. data/lib/action_dispatch/middleware/stack.rb +43 -48
  90. data/lib/action_dispatch/middleware/static.rb +52 -40
  91. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  92. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  98. data/lib/action_dispatch/railtie.rb +0 -2
  99. data/lib/action_dispatch/request/session.rb +66 -34
  100. data/lib/action_dispatch/request/utils.rb +51 -19
  101. data/lib/action_dispatch/routing.rb +3 -8
  102. data/lib/action_dispatch/routing/inspector.rb +6 -30
  103. data/lib/action_dispatch/routing/mapper.rb +447 -322
  104. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  105. data/lib/action_dispatch/routing/redirection.rb +3 -3
  106. data/lib/action_dispatch/routing/route_set.rb +124 -227
  107. data/lib/action_dispatch/routing/url_for.rb +27 -10
  108. data/lib/action_dispatch/testing/assertions.rb +1 -1
  109. data/lib/action_dispatch/testing/assertions/response.rb +27 -9
  110. data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
  111. data/lib/action_dispatch/testing/integration.rb +237 -76
  112. data/lib/action_dispatch/testing/test_process.rb +5 -5
  113. data/lib/action_dispatch/testing/test_request.rb +12 -21
  114. data/lib/action_dispatch/testing/test_response.rb +1 -4
  115. data/lib/action_pack.rb +1 -1
  116. data/lib/action_pack/gem_version.rb +4 -4
  117. metadata +26 -25
  118. data/lib/action_controller/metal/hide_actions.rb +0 -40
  119. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  120. data/lib/action_controller/middleware.rb +0 -39
  121. data/lib/action_controller/model_naming.rb +0 -12
  122. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  123. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  124. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  125. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -0,0 +1,11 @@
1
+ module ActionController
2
+ module BasicImplicitRender
3
+ def send_action(method, *args)
4
+ super.tap { default_render unless performed? }
5
+ end
6
+
7
+ def default_render(*args)
8
+ head :no_content
9
+ end
10
+ end
11
+ end
@@ -4,7 +4,6 @@ module ActionController
4
4
  module ConditionalGet
5
5
  extend ActiveSupport::Concern
6
6
 
7
- include RackDelegation
8
7
  include Head
9
8
 
10
9
  included do
@@ -15,7 +14,7 @@ module ActionController
15
14
  module ClassMethods
16
15
  # Allows you to consider additional controller-wide information when generating an ETag.
17
16
  # For example, if you serve pages tailored depending on who's logged in at the moment, you
18
- # may want to add the current user id to be part of the ETag to prevent authorized displaying
17
+ # may want to add the current user id to be part of the ETag to prevent unauthorized displaying
19
18
  # of cached pages.
20
19
  #
21
20
  # class InvoicesController < ApplicationController
@@ -40,7 +39,7 @@ module ActionController
40
39
  # * <tt>:etag</tt>.
41
40
  # * <tt>:last_modified</tt>.
42
41
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
43
- # +true+ if you want your application to be cachable by other devices (proxy caches).
42
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
44
43
  # * <tt>:template</tt> By default, the template digest for the current
45
44
  # controller/action is included in ETags. If the action renders a
46
45
  # different template, you can include its digest instead. If the action
@@ -51,21 +50,31 @@ module ActionController
51
50
  #
52
51
  # def show
53
52
  # @article = Article.find(params[:id])
54
- # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
53
+ # fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
55
54
  # end
56
55
  #
57
56
  # This will render the show template if the request isn't sending a matching ETag or
58
57
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
59
58
  #
60
- # You can also just pass a record where +last_modified+ will be set by calling
61
- # +updated_at+ and the +etag+ by passing the object itself.
59
+ # You can also just pass a record. In this case +last_modified+ will be set
60
+ # by calling +updated_at+ and +etag+ by passing the object itself.
62
61
  #
63
62
  # def show
64
63
  # @article = Article.find(params[:id])
65
64
  # fresh_when(@article)
66
65
  # end
67
66
  #
68
- # When passing a record, you can still set whether the public header:
67
+ # You can also pass an object that responds to +maximum+, such as a
68
+ # collection of active records. In this case +last_modified+ will be set by
69
+ # calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
70
+ # most recently updated record) and the +etag+ by passing the object itself.
71
+ #
72
+ # def index
73
+ # @articles = Article.all
74
+ # fresh_when(@articles)
75
+ # end
76
+ #
77
+ # When passing a record or a collection, you can still set the public header:
69
78
  #
70
79
  # def show
71
80
  # @article = Article.find(params[:id])
@@ -77,18 +86,16 @@ module ActionController
77
86
  #
78
87
  # before_action { fresh_when @article, template: 'widgets/show' }
79
88
  #
80
- def fresh_when(record_or_options, additional_options = {})
81
- if record_or_options.is_a? Hash
82
- options = record_or_options
83
- options.assert_valid_keys(:etag, :last_modified, :public, :template)
84
- else
85
- record = record_or_options
86
- options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
89
+ def fresh_when(object = nil, etag: object, last_modified: nil, public: false, template: nil)
90
+ last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
91
+
92
+ if etag || template
93
+ response.etag = combine_etags(etag: etag, last_modified: last_modified,
94
+ public: public, template: template)
87
95
  end
88
96
 
89
- response.etag = combine_etags(options) if options[:etag] || options[:template]
90
- response.last_modified = options[:last_modified] if options[:last_modified]
91
- response.cache_control[:public] = true if options[:public]
97
+ response.last_modified = last_modified if last_modified
98
+ response.cache_control[:public] = true if public
92
99
 
93
100
  head :not_modified if request.fresh?(response)
94
101
  end
@@ -103,7 +110,7 @@ module ActionController
103
110
  # * <tt>:etag</tt>.
104
111
  # * <tt>:last_modified</tt>.
105
112
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
106
- # +true+ if you want your application to be cachable by other devices (proxy caches).
113
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
107
114
  # * <tt>:template</tt> By default, the template digest for the current
108
115
  # controller/action is included in ETags. If the action renders a
109
116
  # different template, you can include its digest instead. If the action
@@ -115,7 +122,7 @@ module ActionController
115
122
  # def show
116
123
  # @article = Article.find(params[:id])
117
124
  #
118
- # if stale?(etag: @article, last_modified: @article.created_at)
125
+ # if stale?(etag: @article, last_modified: @article.updated_at)
119
126
  # @statistics = @article.really_expensive_call
120
127
  # respond_to do |format|
121
128
  # # all the supported formats
@@ -123,8 +130,8 @@ module ActionController
123
130
  # end
124
131
  # end
125
132
  #
126
- # You can also just pass a record where +last_modified+ will be set by calling
127
- # +updated_at+ and the +etag+ by passing the object itself.
133
+ # You can also just pass a record. In this case +last_modified+ will be set
134
+ # by calling +updated_at+ and +etag+ by passing the object itself.
128
135
  #
129
136
  # def show
130
137
  # @article = Article.find(params[:id])
@@ -137,7 +144,23 @@ module ActionController
137
144
  # end
138
145
  # end
139
146
  #
140
- # When passing a record, you can still set whether the public header:
147
+ # You can also pass an object that responds to +maximum+, such as a
148
+ # collection of active records. In this case +last_modified+ will be set by
149
+ # calling +maximum(:updated_at)+ on the collection (the timestamp of the
150
+ # most recently updated record) and the +etag+ by passing the object itself.
151
+ #
152
+ # def index
153
+ # @articles = Article.all
154
+ #
155
+ # if stale?(@articles)
156
+ # @statistics = @articles.really_expensive_call
157
+ # respond_to do |format|
158
+ # # all the supported formats
159
+ # end
160
+ # end
161
+ # end
162
+ #
163
+ # When passing a record or a collection, you can still set the public header:
141
164
  #
142
165
  # def show
143
166
  # @article = Article.find(params[:id])
@@ -157,8 +180,8 @@ module ActionController
157
180
  # super if stale? @article, template: 'widgets/show'
158
181
  # end
159
182
  #
160
- def stale?(record_or_options, additional_options = {})
161
- fresh_when(record_or_options, additional_options)
183
+ def stale?(object = nil, etag: object, last_modified: nil, public: nil, template: nil)
184
+ fresh_when(object, etag: etag, last_modified: last_modified, public: public, template: template)
162
185
  !request.fresh?(response)
163
186
  end
164
187
 
@@ -191,6 +214,24 @@ module ActionController
191
214
  response.cache_control.replace(:no_cache => true)
192
215
  end
193
216
 
217
+ # Cache or yield the block. The cache is supposed to never expire.
218
+ #
219
+ # You can use this method when you have a HTTP response that never changes,
220
+ # and the browser and proxies should cache it indefinitely.
221
+ #
222
+ # * +public+: By default, HTTP responses are private, cached only on the
223
+ # user's web browser. To allow proxies to cache the response, set +true+ to
224
+ # indicate that they can serve the cached response to all users.
225
+ #
226
+ # * +version+: the version passed as a key for the cache.
227
+ def http_cache_forever(public: false, version: 'v1')
228
+ expires_in 100.years, public: public
229
+
230
+ yield if stale?(etag: "#{version}-#{request.fullpath}",
231
+ last_modified: Time.parse('2011-01-01').utc,
232
+ public: public)
233
+ end
234
+
194
235
  private
195
236
  def combine_etags(options)
196
237
  etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
@@ -2,8 +2,6 @@ module ActionController #:nodoc:
2
2
  module Cookies
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include RackDelegation
6
-
7
5
  included do
8
6
  helper_method :cookies
9
7
  end
@@ -72,27 +72,7 @@ module ActionController #:nodoc:
72
72
 
73
73
  self.status = options[:status] || 200
74
74
  self.content_type = options[:content_type] if options.key?(:content_type)
75
- self.response_body = FileBody.new(path)
76
- end
77
-
78
- # Avoid having to pass an open file handle as the response body.
79
- # Rack::Sendfile will usually intercept the response and uses
80
- # the path directly, so there is no reason to open the file.
81
- class FileBody #:nodoc:
82
- attr_reader :to_path
83
-
84
- def initialize(path)
85
- @to_path = path
86
- end
87
-
88
- # Stream the file's contents if Rack::Sendfile isn't present.
89
- def each
90
- File.open(to_path, 'rb') do |file|
91
- while chunk = file.read(16384)
92
- yield chunk
93
- end
94
- end
95
- end
75
+ response.send_file path
96
76
  end
97
77
 
98
78
  # Sends the given binary data to the browser. This method is similar to
@@ -126,7 +106,7 @@ module ActionController #:nodoc:
126
106
  # See +send_file+ for more information on HTTP Content-* headers and caching.
127
107
  def send_data(data, options = {}) #:doc:
128
108
  send_file_headers! options
129
- render options.slice(:status, :content_type).merge(:text => data)
109
+ render options.slice(:status, :content_type).merge(body: data)
130
110
  end
131
111
 
132
112
  private
@@ -25,7 +25,7 @@ module ActionController
25
25
  class_attribute :etag_with_template_digest
26
26
  self.etag_with_template_digest = true
27
27
 
28
- ActiveSupport.on_load :action_view, yield: true do |action_view_base|
28
+ ActiveSupport.on_load :action_view, yield: true do
29
29
  etag do |options|
30
30
  determine_template_etag(options) if etag_with_template_digest
31
31
  end
@@ -3,14 +3,19 @@ module ActionController
3
3
  end
4
4
 
5
5
  class BadRequest < ActionControllerError #:nodoc:
6
- attr_reader :original_exception
6
+ def initialize(msg = nil, e = nil)
7
+ if e
8
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
9
+ "Exceptions will automatically capture the original exception.", caller)
10
+ end
7
11
 
8
- def initialize(type = nil, e = nil)
9
- return super() unless type && e
12
+ super(msg)
13
+ set_backtrace $!.backtrace if $!
14
+ end
10
15
 
11
- super("Invalid #{type} parameters: #{e.message}")
12
- @original_exception = e
13
- set_backtrace e.backtrace
16
+ def original_exception
17
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
18
+ cause
14
19
  end
15
20
  end
16
21
 
@@ -55,10 +55,10 @@ module ActionController
55
55
  # You can pass any of the following options to affect the before_action callback
56
56
  # * <tt>only</tt> - The callback should be run only for this action
57
57
  # * <tt>except</tt> - The callback should be run for all actions except this action
58
- # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
59
- # will be called only when it returns a true value.
60
- # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
61
- # will be called only when it returns a false value.
58
+ # * <tt>if</tt> - A symbol naming an instance method or a proc; the
59
+ # callback will be called only when it returns a true value.
60
+ # * <tt>unless</tt> - A symbol naming an instance method or a proc; the
61
+ # callback will be called only when it returns a false value.
62
62
  def force_ssl(options = {})
63
63
  action_options = options.slice(*ACTION_OPTIONS)
64
64
  redirect_options = options.except(*ACTION_OPTIONS)
@@ -71,8 +71,8 @@ module ActionController
71
71
  # Redirect the existing request to use the HTTPS protocol.
72
72
  #
73
73
  # ==== Parameters
74
- # * <tt>host_or_options</tt> - Either a host name or any of the url & redirect options
75
- # available to the <tt>force_ssl</tt> method.
74
+ # * <tt>host_or_options</tt> - Either a host name or any of the url &
75
+ # redirect options available to the <tt>force_ssl</tt> method.
76
76
  def force_ssl_redirect(host_or_options = nil)
77
77
  unless request.ssl?
78
78
  options = {
@@ -17,8 +17,18 @@ module ActionController
17
17
  #
18
18
  # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
19
19
  def head(status, options = {})
20
- options, status = status, nil if status.is_a?(Hash)
21
- status ||= options.delete(:status) || :ok
20
+ if status.is_a?(Hash)
21
+ msg = status[:status] ? 'The :status option' : 'The implicit :ok status'
22
+ options, status = status, status.delete(:status)
23
+
24
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
25
+ #{msg} on `head` has been deprecated and will be removed in Rails 5.1.
26
+ Please pass the status as a separate parameter before the options, instead.
27
+ MSG
28
+ end
29
+
30
+ status ||= :ok
31
+
22
32
  location = options.delete(:location)
23
33
  content_type = options.delete(:content_type)
24
34
 
@@ -33,12 +43,9 @@ module ActionController
33
43
 
34
44
  if include_content?(self.response_code)
35
45
  self.content_type = content_type || (Mime[formats.first] if formats)
36
- self.response.charset = false if self.response
37
- else
38
- headers.delete('Content-Type')
39
- headers.delete('Content-Length')
46
+ self.response.charset = false
40
47
  end
41
-
48
+
42
49
  true
43
50
  end
44
51
 
@@ -7,8 +7,8 @@ module ActionController
7
7
  # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
8
8
  # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>
9
9
  #
10
- # In previous versions of \Rails the controller will include a helper whose
11
- # name matches that of the controller, e.g., <tt>MyController</tt> will automatically
10
+ # In previous versions of \Rails the controller will include a helper which
11
+ # matches the name of the controller, e.g., <tt>MyController</tt> will automatically
12
12
  # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
13
13
  #
14
14
  # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
@@ -44,7 +44,7 @@ module ActionController
44
44
  # the output might look like this:
45
45
  #
46
46
  # 23 Aug 11:30 | Carolina Railhawks Soccer Match
47
- # N/A | Carolina Railhaws Training Workshop
47
+ # N/A | Carolina Railhawks Training Workshop
48
48
  #
49
49
  module Helpers
50
50
  extend ActiveSupport::Concern
@@ -73,7 +73,7 @@ module ActionController
73
73
 
74
74
  # Provides a proxy to access helpers methods from outside the view.
75
75
  def helpers
76
- @helper_proxy ||= begin
76
+ @helper_proxy ||= begin
77
77
  proxy = ActionView::Base.new
78
78
  proxy.config = config.inheritable_copy
79
79
  proxy.extend(_helpers)
@@ -93,10 +93,14 @@ module ActionController
93
93
  super(args)
94
94
  end
95
95
 
96
+ # Returns a list of helper names in a given path.
97
+ #
98
+ # ActionController::Base.all_helpers_from_path 'app/helpers'
99
+ # # => ["application", "chart", "rubygems"]
96
100
  def all_helpers_from_path(path)
97
101
  helpers = Array(path).flat_map do |_path|
98
102
  extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
99
- names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
103
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
100
104
  names.sort!
101
105
  end
102
106
  helpers.uniq!
@@ -1,5 +1,4 @@
1
1
  require 'base64'
2
- require 'active_support/security_utils'
3
2
 
4
3
  module ActionController
5
4
  # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
@@ -35,7 +34,7 @@ module ActionController
35
34
  #
36
35
  # def authenticate
37
36
  # case request.format
38
- # when Mime::XML, Mime::ATOM
37
+ # when Mime[:xml], Mime[:atom]
39
38
  # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
40
39
  # @current_user = user
41
40
  # else
@@ -69,26 +68,22 @@ module ActionController
69
68
  def http_basic_authenticate_with(options = {})
70
69
  before_action(options.except(:name, :password, :realm)) do
71
70
  authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
72
- # This comparison uses & so that it doesn't short circuit and
73
- # uses `variable_size_secure_compare` so that length information
74
- # isn't leaked.
75
- ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
76
- ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
71
+ name == options[:name] && password == options[:password]
77
72
  end
78
73
  end
79
74
  end
80
75
  end
81
76
 
82
- def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
83
- authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
77
+ def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)
78
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
84
79
  end
85
80
 
86
81
  def authenticate_with_http_basic(&login_procedure)
87
82
  HttpAuthentication::Basic.authenticate(request, &login_procedure)
88
83
  end
89
84
 
90
- def request_http_basic_authentication(realm = "Application")
91
- HttpAuthentication::Basic.authentication_request(self, realm)
85
+ def request_http_basic_authentication(realm = "Application", message = nil)
86
+ HttpAuthentication::Basic.authentication_request(self, realm, message)
92
87
  end
93
88
  end
94
89
 
@@ -99,7 +94,7 @@ module ActionController
99
94
  end
100
95
 
101
96
  def has_basic_credentials?(request)
102
- request.authorization.present? && (auth_scheme(request) == 'Basic')
97
+ request.authorization.present? && (auth_scheme(request).downcase == 'basic')
103
98
  end
104
99
 
105
100
  def user_name_and_password(request)
@@ -111,21 +106,22 @@ module ActionController
111
106
  end
112
107
 
113
108
  def auth_scheme(request)
114
- request.authorization.split(' ', 2).first
109
+ request.authorization.to_s.split(' ', 2).first
115
110
  end
116
111
 
117
112
  def auth_param(request)
118
- request.authorization.split(' ', 2).second
113
+ request.authorization.to_s.split(' ', 2).second
119
114
  end
120
115
 
121
116
  def encode_credentials(user_name, password)
122
117
  "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
123
118
  end
124
119
 
125
- def authentication_request(controller, realm)
126
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
120
+ def authentication_request(controller, realm, message)
121
+ message ||= "HTTP Basic: Access denied.\n"
122
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
127
123
  controller.status = 401
128
- controller.response_body = "HTTP Basic: Access denied.\n"
124
+ controller.response_body = message
129
125
  end
130
126
  end
131
127
 
@@ -175,8 +171,8 @@ module ActionController
175
171
  extend self
176
172
 
177
173
  module ControllerMethods
178
- def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
179
- authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
174
+ def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
175
+ authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
180
176
  end
181
177
 
182
178
  # Authenticate with HTTP Digest, returns true or false
@@ -207,7 +203,7 @@ module ActionController
207
203
  password = password_procedure.call(credentials[:username])
208
204
  return false unless password
209
205
 
210
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
206
+ method = request.get_header('rack.methodoverride.original_method') || request.get_header('REQUEST_METHOD')
211
207
  uri = credentials[:uri]
212
208
 
213
209
  [true, false].any? do |trailing_question_mark|
@@ -264,8 +260,8 @@ module ActionController
264
260
  end
265
261
 
266
262
  def secret_token(request)
267
- key_generator = request.env["action_dispatch.key_generator"]
268
- http_auth_salt = request.env["action_dispatch.http_auth_salt"]
263
+ key_generator = request.key_generator
264
+ http_auth_salt = request.http_auth_salt
269
265
  key_generator.generate_key(http_auth_salt)
270
266
  end
271
267
 
@@ -319,7 +315,7 @@ module ActionController
319
315
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
320
316
  end
321
317
 
322
- # Opaque based on random generation - but changing each request?
318
+ # Opaque based on digest of secret key
323
319
  def opaque(secret_key)
324
320
  ::Digest::MD5.hexdigest(secret_key)
325
321
  end
@@ -365,7 +361,7 @@ module ActionController
365
361
  #
366
362
  # def authenticate
367
363
  # case request.format
368
- # when Mime::XML, Mime::ATOM
364
+ # when Mime[:xml], Mime[:atom]
369
365
  # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
370
366
  # @current_user = user
371
367
  # else
@@ -401,21 +397,21 @@ module ActionController
401
397
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
402
398
  module Token
403
399
  TOKEN_KEY = 'token='
404
- TOKEN_REGEX = /^Token /
400
+ TOKEN_REGEX = /^(Token|Bearer)\s+/
405
401
  AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
406
402
  extend self
407
403
 
408
404
  module ControllerMethods
409
- def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
410
- authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
405
+ def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
406
+ authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
411
407
  end
412
408
 
413
409
  def authenticate_with_http_token(&login_procedure)
414
410
  Token.authenticate(self, &login_procedure)
415
411
  end
416
412
 
417
- def request_http_token_authentication(realm = "Application")
418
- Token.authentication_request(self, realm)
413
+ def request_http_token_authentication(realm = "Application", message = nil)
414
+ Token.authentication_request(self, realm, message)
419
415
  end
420
416
  end
421
417
 
@@ -440,15 +436,17 @@ module ActionController
440
436
  end
441
437
  end
442
438
 
443
- # Parses the token and options out of the token authorization header. If
444
- # the header looks like this:
439
+ # Parses the token and options out of the token authorization header.
440
+ # The value for the Authorization header is expected to have the prefix
441
+ # <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
445
442
  # Authorization: Token token="abc", nonce="def"
446
- # Then the returned token is "abc", and the options is {nonce: "def"}
443
+ # Then the returned token is <tt>"abc"</tt>, and the options are
444
+ # <tt>{nonce: "def"}</tt>
447
445
  #
448
446
  # request - ActionDispatch::Request instance with the current headers.
449
447
  #
450
- # Returns an Array of [String, Hash] if a token is present.
451
- # Returns nil if no token is found.
448
+ # Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
449
+ # Returns +nil+ if no token is found.
452
450
  def token_and_options(request)
453
451
  authorization_request = request.authorization.to_s
454
452
  if authorization_request[TOKEN_REGEX]
@@ -497,15 +495,16 @@ module ActionController
497
495
  "Token #{values * ", "}"
498
496
  end
499
497
 
500
- # Sets a WWW-Authenticate to let the client know a token is desired.
498
+ # Sets a WWW-Authenticate header to let the client know a token is desired.
501
499
  #
502
500
  # controller - ActionController::Base instance for the outgoing response.
503
501
  # realm - String realm to use in the header.
504
502
  #
505
503
  # Returns nothing.
506
- def authentication_request(controller, realm)
507
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
508
- controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
504
+ def authentication_request(controller, realm, message = nil)
505
+ message ||= "HTTP Token: Access denied.\n"
506
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
507
+ controller.__send__ :render, plain: message, status: :unauthorized
509
508
  end
510
509
  end
511
510
  end