Permalink
Browse files

Support X-Http-Method-Override header in MethodOverride middleware

  • Loading branch information...
1 parent f7958a8 commit 8b8690bcb7762cde729088c2abdacb610ebea1f7 @josh josh committed Dec 30, 2008
Showing with 18 additions and 2 deletions.
  1. +6 −1 lib/rack/methodoverride.rb
  2. +12 −1 test/spec_rack_methodoverride.rb
@@ -2,14 +2,19 @@ module Rack
class MethodOverride
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
+ METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
+
def initialize(app)
@app = app
end
def call(env)
if env["REQUEST_METHOD"] == "POST"
req = Request.new(env)
- method = req.POST["_method"].to_s.upcase
+ method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
+ env[HTTP_METHOD_OVERRIDE_HEADER]
+ method = method.to_s.upcase
if HTTP_METHODS.include?(method)
env["REQUEST_METHOD"] = method
jcrosby
jcrosby Dec 30, 2008 Member

We may want to consider noting in the env somewhere (perhaps with something similar to HTTP Via headers?) that this middleware is in use or at least documenting that it should only be used behind auth middleware. OAuth, for example, uses the request method as part of signature verification and would report an invalid signature in the case of PUT overriding POST.

end
@@ -13,14 +13,25 @@
req.env["REQUEST_METHOD"].should.equal "GET"
end
- specify "should modify REQUEST_METHOD for POST requests" do
+ specify "_method parameter should modify REQUEST_METHOD for POST requests" do
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "PUT"
end
+ specify "X-HTTP-Method-Override header should modify REQUEST_METHOD for POST requests" do
+ env = Rack::MockRequest.env_for("/",
+ :method => "POST",
+ "HTTP_X_HTTP_METHOD_OVERRIDE" => "PUT"
+ )
+ app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
+ req = app.call(env)
+
+ req.env["REQUEST_METHOD"].should.equal "PUT"
+ end
+
specify "should not modify REQUEST_METHOD if the method is unknown" do
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })

3 comments on commit 8b8690b

@josh
Contributor
josh commented on 8b8690b Dec 30, 2008

I should not matter. I use this with OpenID and it validates the headers fine since redirects are always GET therefore the request method is never altered.

I suppose it would be a problem if you intentionally tried to use a PUT method with OAuth, but I think thats a rare case because most of the stuff happens over GET.

@jcrosby
Member

OpenID solves a different problem than OAuth. Rather than doing authentication with redirects like OpenID, OAuth does authorization. Each request to an API endpoint (GET /foos/123, POST /foos, DELETE /foos/123, etc.) is signed by the user agent using the request method, URL, parameters, a timestamp, and a nonce. OAuth can be used for all HTTP methods and thus needs to know the method used to generate the signature before sending the request. The signature is generated again by a service endpoint and matched against the client’s signature to determine if the request is valid and authorized.

See Section 9.1 of the spec for reference:

http://oauth.net/core/1.0/#anchor14

Also, see the assembly of a signature base string in action in the appendix, section A.5.1 “Generating Signature Base String.”

It is certainly useful to clean up POST method tunneling hacks as this middleware attempts to do, but there should be a transparent way of determining that this has happened. A pseudo-via-header is one possibility. Another might be storing the original request method in the env somewhere. A third might be storing the adjusted request method under its own env key so that this would be more opt-in and less likely to cause confusion.

@dkubb
Contributor
dkubb replied Jan 1, 2009

There is related discussion about this commit in chneukirchen/rack:

http://github.com/chneukirchen/rack/commit/8b8690bcb7762cde729088c2abdacb610ebea1f7

Please sign in to comment.