lib/safe_cookies.rb in safe_cookies-0.1.5 vs lib/safe_cookies.rb in safe_cookies-0.1.6

- old
+ new

@@ -7,12 +7,10 @@ require "rack" # Naming: # - application_cookies: cookies received from the application. The 'Set-Cookie' header is a string # - request_cookies: cookies received from the client. Rack::Request#cookies returns a Hash of { 'name' => 'value' } -# - response_cookies: cookies to be sent to the client -# (= application_cookies + any cookies set in the middleware) module SafeCookies UnknownCookieError = Class.new(StandardError) @@ -37,49 +35,44 @@ reset_instance_variables @request = Rack::Request.new(env) ensure_no_unknown_cookies_in_request! + # calling the next middleware status, @headers, body = @app.call(env) cache_application_cookies_string - remove_application_cookies_from_request_cookies - rewrite_application_cookies + enhance_application_cookies! store_application_cookie_names - fix_cookie_paths if fix_cookie_paths? + + delete_cookies_on_bad_path if fix_cookie_paths? rewrite_request_cookies unless cookies_have_been_rewritten_before? [ status, @headers, body ] end private def reset_instance_variables - @request, @headers, @application_cookies = nil + @request, @headers, @application_cookies_string = nil end - + + # Make sure we get notified if a client comes with an unregistered cookie, + # because we do not want any cookie not to be secured. def ensure_no_unknown_cookies_in_request! request_cookie_names = request_cookies.keys.map(&:to_s) unknown_cookie_names = request_cookie_names - known_cookie_names if unknown_cookie_names.any? handle_unknown_cookies(unknown_cookie_names) end end - - def remove_application_cookies_from_request_cookies - if @application_cookies - application_cookie_names = @application_cookies.scan(COOKIE_NAME_REGEX) - application_cookie_names.each do |cookie| - request_cookies.delete(cookie) - end - end - end - - def rewrite_application_cookies - if @application_cookies - cookies = @application_cookies.split("\n") + + # Overwrites @header['Set-Cookie'] + def enhance_application_cookies! + if @application_cookies_string + cookies = @application_cookies_string.split("\n") # On Rack 1.1, cookie values sometimes contain trailing newlines. # Example => ["foo=1; path=/\n", "bar=2; path=/"] # Note that they also mess up browsers, when this array is merged # again and the "Set-Cookie" header then contains double newlines. @@ -94,27 +87,37 @@ # browser's request contained, so a `Rack::Request` can't parse it for # us. A `Rack::Response` doesn't offer a way either. @headers['Set-Cookie'] = cookies.join("\n") end end - + + # Store the names of cookies that are set by the application. We are already + # securing those and therefore do not need to rewrite them. def store_application_cookie_names - if @application_cookies - application_cookie_names = stored_application_cookie_names + @application_cookies.scan(COOKIE_NAME_REGEX) + if @application_cookies_string + application_cookie_names = stored_application_cookie_names + @application_cookies_string.scan(COOKIE_NAME_REGEX) application_cookies_string = application_cookie_names.uniq.join(KNOWN_COOKIES_DIVIDER) set_cookie!(STORE_COOKIE_NAME, application_cookies_string, :expire_after => HELPER_COOKIES_LIFETIME) end end - # This method takes all cookies sent with the request and rewrites them, + # This method takes the cookies sent with the request and rewrites them, # making them both secure and http-only (unless specified otherwise in # the configuration). # With the SECURED_COOKIE_NAME cookie we remember the exact time that we # rewrote the cookies. def rewrite_request_cookies - if request_cookies.any? + cookies_to_rewrite = request_cookies || [] + + # don't rewrite request cookies that the application is setting in the response + if @application_cookies_string + application_cookie_names = @application_cookies_string.scan(COOKIE_NAME_REGEX) + Util.except!(cookies_to_rewrite, *application_cookie_names) + end + + if cookies_to_rewrite.any? registered_cookies_in_request.each do |cookie_name, options| value = request_cookies[cookie_name] set_cookie!(cookie_name, value, options) end @@ -122,9 +125,10 @@ formatted_now = Rack::Utils.rfc2822(Time.now.gmtime) set_cookie!(SECURED_COOKIE_NAME, formatted_now, :expire_after => HELPER_COOKIES_LIFETIME) end end + # API method def handle_unknown_cookies(cookie_names) raise SafeCookies::UnknownCookieError.new("Request for '#{@request.url}' had unknown cookies: #{cookie_names.join(', ')}") end end