require 'rack/request' require 'rack/response' require 'uri' require 'active_support/tagged_logging' module Macmillan module Utils module Middleware class CookieMessage YEAR = 31_536_000 COOKIE = 'euCookieNotice'.freeze def initialize(app, options = {}) @app = app @log_level = options[:log_level] if (logger = options[:logger]) if logger.respond_to?(:tagged) @logger = logger else @logger = ActiveSupport::TaggedLogging.new(logger) end end end def call(env) @request = Rack::Request.new(env) if cookies_accepted?(@request) redirect_back(@request) else @app.call(env) end end private def cookies_accepted?(request) debug("request.post? IS #{request.post?.inspect}") debug("request.cookies[#{COOKIE}] IS #{request.cookies[COOKIE].inspect}") debug("request.params['cookies'] IS #{request.params['cookies'].inspect}") debug("request.cookies IS #{request.cookies.inspect}") unless request.post? debug("request.post? (#{request.post?.inspect}) means passthru") return false end unless request.cookies[COOKIE] != 'accepted' debug("request.cookies['#{COOKIE}'] (#{request.cookies[COOKIE].inspect}) means passthru") return false end unless request.params['cookies'] == 'accepted' debug("request.params['cookies'] (#{request.params['cookies'].inspect}) means passthru") return false end debug('About to set the acceptance cookie and redirect') true end def debug(msg) logger.tagged(self.class.name) { logger.debug(msg) } end def logger @logger ||= @request.logger || default_logger end def default_logger logger = ::Logger.new($stdout) logger.level = default_log_level ActiveSupport::TaggedLogging.new(logger) end def default_log_level @log_level || ::Logger::INFO end def redirect_back(request) response = Rack::Response.new location = build_location(request) debug("Redirecting to #{location}") response.redirect(location) response.set_cookie(COOKIE, cookie_options(request)) response.to_a end def cookie_options(request) { value: 'accepted', domain: ".#{request.host_with_port.split('.').drop(1).join('.')}", path: '/', httponly: true, expires: Time.now.getutc + YEAR } end def build_location(request) begin debug("Attempting to determine redirect by parsing referrer #{request.referrer}") uri = URI.parse(request.referrer.to_s) rescue URI::InvalidURIError debug("No that failed, attempting to determine redirect by parsing request.url #{request.url}") uri = URI.parse(request.url) end # Check that the redirect is an internal one for security reasons: # https://webmasters.googleblog.com/2009/01/open-redirect-urls-is-your-site-being.html if internal_redirect?(request, uri) uri.to_s else debug("Not internal redirect - so changing to #{request.url} instead of the above") request.url end end def internal_redirect?(request, uri) debug("Is redirect to #{uri.host}:#{uri.port} internal WRT #{request.host}:#{request.port}") request.host == uri.host # && request.port == uri.port end end end end end