Rails under Apache and Passenger: no headers - ruby-on-rails

In an Rails app, I have a filter that checks some headers. Something like:
before_action :validate_consumer
...
def validate_consumer
consumer = self.request.headers['HTTP_CONSUMER_SECRET'] == 'random_key'
consumer || render_unauthorized("Consumer -")
end
def render_unauthorized(token="")
self.headers['WWW-Authenticate'] = 'Token realm="Application"'
render json: {error: "#{token} Bad credentials", error_description: "Denied"}, status: 401
end
this works perfect running locally (development), the header the view sends (CONSUMER_SECRET) is present in the request. But when I run in production mode, this header does not exists. I guess that this might have something related with my environment: that app runs under Apache (with Phusion+Passenger). Could it be this the problem? Anyone knows how to solve?

Related

How to catch a Rack RangeError in Rails 6

I have a Rails 6 app to which users can upload CSV files. Rails/Rack imposes a limit in the number of params that can be included in a request, and I've set this to a size larger than likely submissions to my app. However, I would like to return a friendly response if a too-large file is uploaded.
It looks like I need to add some custom middleware, to catch and rescue the error, but I can't get the code to work - the basic error is still raised without my rescue block being called.
The error from the server is:
Rack app error handling request { POST /[PATH_TO]/datasets }
#<RangeError: exceeded available parameter key space>
The code in my app/middleware/catch_errors.rb file is basically taken from a previous SO answer, where someone was catching ActionDispatch::ParamsParser::ParseError in JSON, but with my own code in the rescue block (which I realise may not work properly in this context, but that's not the issue right now):
class CatchErrors
def initialize(_app)
#app = _app
end
def call(_env)
begin
#app.call(_env)
rescue RangeError => _error
_error_output = "There were too many fields in the data you submitted: #{_error}"
if env['HTTP_ACCEPT'] =~ /application\/html/
Rails.logger.error("Caught RangeError: #{_error}")
flash[:error_title] = 'Too many fields in your data'
flash[:error_detail1] = _error_output
render 'static_pages/error', status: :bad_request
elsif env['HTTP_ACCEPT'] =~ /application\/json/
return [
:bad_request, { "Content-Type" => "application/json" },
[ { status: :bad_request, error: _error_output }.to_json ]
]
else
raise _error
end
end
end
end
I'm loading it in config.application.rb like this:
require_relative '../app/middleware/catch_errors'
...
config.middleware.use CatchErrors
I'm resetting the size limit for testing in app/initializers/rack.rb like this:
if Rack::Utils.respond_to?("key_space_limit=")
Rack::Utils.key_space_limit = 1
end
Any help gratefully received!
First, execute command to see all middlewares:
bin/rails middleware
config.middleware.use place your middleware at the bottom of the middleware stack. Because of that it can not catch error. Try to place it at the top:
config.middleware.insert_before 0, CatchErrors
Another point to mention, may be you will need to config.middleware.move_after or even config.middleware.delete some middleware. For instance, while tinkering I needed to place:
config.middleware.move_after CatchErrors, Rack::MiniProfiler

Rails - Ruby process sleep when open a file URL and not able to end or kill it

I am trying to open a image URL from Activestorage but ruby falls asleep and the last item in console log is:
Started GET "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--bfec3dc0d0745e49d7daa9faac7abdeaa46418c3/f2392748480.png" for 127.0.0.1 at 2018-12-02 14:45:01 -0600
and when it ends the error is: Net::ReadTimeout
This doesn't happened the first time, but after that it happen each time i run the method.
My method:
def upload_photos
require 'open-uri'
page_api = Koala::Facebook::API.new(params[:access_token])
image_urls = params[:images].map do |image|
open("#{request.protocol}#{request.host_with_port}#{image[:url]}") do |f|
page_api.put_picture(f, f.content_type, { "caption" => image[:filename] }, params[:id])
end
end
render json: { image_urls: image_urls, album_id: params }
end
It works the first time but not after that.
UPDATE: this works fine with external URLs but not with activestorage
This is due to Ruby is busy opening the file and when tries to reach the URL is not able to respond to that petition because is opening the file, is like trying to watch your own backhead in a mirror.

Rails | IP spoofing attack

lately I have received such an error on ruby on rails, but do not know what it means. Any help well appreciated!
ERROR: Failed to generate exception summary:
ActionView::Template::Error: IP spoofing attack?! HTTP_CLIENT_IP="172.17.3.20" HTTP_X_FORWARDED_FOR="79.170.168.251"
EDIT:
I have such a function in the application controller
before_filter :ensure_domain
APP_DOMAIN = 'www.mysite.com'
def ensure_domain
if request.env['HTTP_HOST'] != APP_DOMAIN && Rails.env.production?
#HTTP 301 is a "permanent" redirect
redirect_to "https://#{APP_DOMAIN}#{request.path}", :status => 301
end
end
Unless you're actually doing IP-based authorization, you can likely ignore this. All that this message is telling you is that the IP that the request is coming from is different than the IP that's passed in the HTTP_X_FORWARDED_FOR header, which doesn't necessarily mean that it's a spoof. There are plenty of legitimate reasons for this, so it's generally okay to simply disable the spoof check by setting config.action_dispatch.ip_spoofing_check = false

How can I handle generic errors with a JSON response in a hybrid (JSON API + HTML) Rails 5 app?

I spent one day trying several approaches, but still haven't quite got there so decided to ask now...
I have a Rails 5 app which is mainly a JSON API (using the actual JSON API specs), but also a "normal" Rails app with transactional emails and account related pages (reset password, etc).
What I'd like to achieve is that Rails always returns a JSON response with some meaningful error response to all API calls, rather than the default HTML error page or a header only 400 error.
The main cases I'm trying to handle are JSON parsing issues and Ruby exceptions (500 errors).
I tried:
using rescue_from on the ActionController level – seems the framework handles these exceptions before they would reach the controller
Handling them on the Rack level with a middleware – this worked in test but not in dev despite setting consider_all_requests_local to false in both
Registering a new Mime-type and a parser as JSON API Resources gem does it – looked promising, but the parser code is never hit
I'm really at my wit's end, something which sounded so simple ended up being deceptively complicated with me trying to hunt down where are these exceptions get handled in the framework without much success...
Well I managed to work it out in the end, so thought I should share what worked.
What I missed before is that I had to fiddle a bit with MIME types so that Rails would understand and properly use JSON API:
config/initializers/mime_types.rb
JSON_API_MIME_TYPES = %w[
application/vnd.api+json
text/x-json
application/json
].freeze
Mime::Type.unregister :json
Mime::Type.register 'application/json', :json, JSON_API_MIME_TYPES
Mime::Type.register_alias 'application/json', :json, %i[json_api jsonapi]
After this I could finally handle 500 errors in the base controller:
rescue_from StandardError,
with: :render_standard_error
def render_standard_error
render json: {
status: 500,
error: 'Unhandled error',
message: 'An unexpected error occurred.'
}, status: :internal_server_error
end
Then for handling JSON parse errors, this was the solution:
app/middleware/catch_json_parse_errors
class CatchJsonParseErrors
def initialize(app)
#app = app
end
def call(env)
#app.call(env)
rescue ActionDispatch::Http::Parameters::ParseError => error
if JSON_API_MIME_TYPES.include?(env['CONTENT_TYPE']) ||
JSON_API_MIME_TYPES.include?(env['HTTP_ACCEPT'])
return [
400, { 'Content-Type' => 'application/json' },
[{
status: 400,
error: 'JSON parse error',
message: "There was a problem in the JSON you submitted: #{error}"
}.to_json]
]
else
raise error
end
end
end
config/application.rb
require './app/middleware/catch_json_parse_errors'
...
config.middleware.use CatchJsonParseErrors

Automatically handle missing database connection in ActiveRecord?

With the launch of Amazon's Relational Database Service today and their 'enforced' maintenance windows I wondered if anyone has any solutions for handling a missing database connection in Rails.
Ideally I'd like to be able to automatically present a maintenance page to visitors if the database connection disappears (i.e. Amazon are doing their maintenance) - has anyone ever done anything like this?
Cheers
Arfon
You can do this with a Rack Middleware:
class RescueFromNoDB < Struct.new(:app)
def call(env)
app.call(env)
rescue Mysql::Error => e
if e.message =~ /Can't connect to/
[500, {"Content-Type" => "text/plain"}, ["Can't get to the DB server right now."]]
else
raise
end
end
end
Obviously you can customize the error message, and the e.message =~ /Can't connect to/ bit may just be paranoia, almost all other SQL errors should be caught inside ActionController::Dispatcher.

Resources