Rails - conditionally log for a specific hostname - ruby-on-rails

I'm looking for a way to configure a Rails server log only if the client has contacted a specific hostname. e.g. I could make it so that http://public.example.com doesn't get logged, but http://debug.example.com (same underlying Rails app server) does get logged (or ideally gets logged in more detail than the regular host). It would help with production debugging.

You can use gem Lograge to customize your log. This gem will give you much more custom to your log. For example, in your case, I will do this
After install the gem. Create a file at config/initializers/lograge.rb
# config/initializers/lograge.rb
Rails.application.configure do
config.lograge.enabled = true
config.lograge.custom_options = lambda do |event|
# custom log on specific domain
if event.payload[:host] == "debug.example.com"
{:host => event.payload[:host]}
else
{}
end
end
end
And in your Application Controller
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# This will add request's host to lograge so you can use it to filter log later
def append_info_to_payload(payload)
super
payload[:host] = request.host
end
end
Now you can customize your log base on domain, on how to customize it please read at: https://github.com/roidrage/lograge

Related

Prevent Rails from logging email attachments

When I send an email with an attachment the data is logged in hex and fills up my whole log. Is there a way to disable logging of attachments?
I know I can disable mailer logging with config.action_mailer.logger = nil.
Unfortunately, the attachments are included in the logs if the logging level is set to :debug, the default level for non-production environments. This means that in production you should be fine, but your dev and staging environments could bloat during testing. You could turn down logging for your entire app (config.log_level = :info), but this is obviously less than ideal.
You can configure a custom logger:
config.action_mailer.logger = ActiveSupport::BufferedLogger.new("mailer.log")
config.action_mailer.logger.level = ActiveSupport::BufferedLogger::Severity::INFO
Rails 4
config.action_mailer.logger = ActiveSupport::Logger.new("mailer.log")
config.action_mailer.logger.level = ActiveSupport::Logger::Severity::INFO
This will split the log, but you can isolate the logging level change to the action mailer.
If you still want your log level to be debug, you can remove the attachments from the log output by overriding ActionMailer's LogSubscriber class. Look at the class in your actionmailer gem, and adjust accordingly. For my Rails 4.2.10 install, the relevant file is:
gems/actionmailer-4.2.10/lib/action_mailer/log_subscriber.rb
My module is:
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
info do
recipients = Array(event.payload[:to]).join(', ')
"\nSent mail to #{recipients}, Subject: #{event.payload[:subject]}, on #{event.payload[:date]} (#{event.duration.round(1)}ms)"
end
debug { remove_attachments(event.payload[:mail]) }
end
def remove_attachments(message)
new_message = ''
skip = false
message.each_line do |line|
new_message << line unless skip
skip = true if line =~ /Content-Disposition: attachment;/
skip = false if skip && line =~ /----==_mimepart/
end
new_message
end
end
end
Save this to an .rb file anywhere under your app/ folder and it will be included.
in Application.rb you could try filtering the attachment parameter. I believe this should solve the issue, but I have not tested it myself
config.filter_parameters += [:attachment]

SSL-specific hostname in Heroku with Rails 3.1

I currently have a setup where I force SSL or http where I need it with this before_filter in my application controller:
def force_ssl
if params[:controller] == "sessions"
if !request.ssl? && Rails.env.production?
redirect_to :protocol => 'https://', :status => :moved_permanently
end
else
if request.ssl? && Rails.env.production?
redirect_to :protocol => 'http://', :status => :moved_permanently
end
end
end
What I'd like to do is to use https://secure.example.com when using SSL but keep using http://example.com when not using SSL. Is there a way I can switch between the hostnames depending on whether I'm using SSL?
First I'll show how to force SSL in current and earlier versions of Rails, then at the end I've posted how to use HTTP and HTTPS in Parallel with each other, which is what I think your looking for.
Rails >= 3.1
Simply use config.force_ssl = true in your environment configuration.
# config/application.rb
module MyApp
class Application < Rails::Application
config.force_ssl = true
end
end
You can also selectively enable https depending on the current Rails environment. For example, you might want to keep HTTPS turned off on development, and enable it on staging/production.
# config/application.rb
module MyApp
class Application < Rails::Application
config.force_ssl = false
end
end
# config/environments/production.rb
MyApp::Application.configure do
config.force_ssl = true
end
Rails < 3.1
Just in case you have any projects that are not Rails 3.1 and want the same feature. Enable HTTPS by adding the following line to your environment configuration.
config.middleware.insert_before ActionDispatch::Static, "Rack::SSL"
Note that I’m passing Rack::SSL as string to delegate the loading of the class at the end of the Rails application initialization. Also note the middleware must be inserted in a specific position in the stack, at least before ActionDispatch::Static and ActionDispatch::Cookies.
Don’t forget to define Rack::SSL dependency in your Gemfile.
# Gemfile
gem 'rack-ssl', :require => 'rack/ssl'
Enabling HTTPS and HTTP in parallel
Rack::SSL has a very interesting and undocumented feature. You can pass an :exclude option to determine when to enable/disable the use of HTTPS.
The following code enables Rack::SSL and all its filters only in case the request comes from a HTTPS connection.
config.middleware.insert_before ActionDispatch::Static, Rack::SSL, :exclude => proc { |env| env['HTTPS'] != 'on' }
Both the following URLs will continue to work, but the first one will trigger the Rack::SSL filters.
https://secure.example.com
http://example.com

Rails - Ensuring www is in the URL

I have my app hosted on Heroku, and have a cert for www.mysite.com
I'm trying to solve for
Ensuring www is in the URL, and that the URL is HTTPS
Here's what I have so far:
class ApplicationController < ActionController::Base
before_filter :check_uri
def check_uri
redirect_to request.protocol + "www." + request.host_with_port + request.request_uri if !/^www/.match(request.host) if Rails.env == 'production'
end
But this doesn't seem to being working. Any suggestions or maybe different approaches to solve for ensuring HTTPs and www. is in the URL?
Thanks
For the SSL, use rack-ssl.
# config/environments/production.rb
MyApp::Application.configure do
require 'rack/ssl'
config.middleware.use Rack::SSL
# the rest of the production config....
end
For the WWW, create a Rack middleware of your own.
# lib/rack/www.rb
class Rack::Www
def initialize(app)
#app = app
end
def call(env)
if env['SERVER_NAME'] =~ /^www\./
#app.call(env)
else
[ 307, { 'Location' => 'https://www.my-domain-name.com/' }, '' ]
end
end
end
# config/environments/production.rb
MyApp::Application.configure do
config.middleware.use Rack::Www
# the rest of the production config....
end
To test this in the browser, you can edit your /etc/hosts file on your local development computer
# /etc/hosts
# ...
127.0.0.1 my-domain-name.com
127.0.0.1 www.my-domain-name.com
run the application in production mode on your local development computer
$ RAILS_ENV=production rails s -p 80
and browse to http://my-domain-name.com/ and see what happens.
For the duration of the test, you may want to comment out the line redirecting you to the HTTPS site.
There may also be ways to test this with the standard unit-testing and integration-testing tools that many Rails projects use, such as Test::Unit and RSpec.
Pivotal Labs has some middleware called Refraction that is a mod_rewrite replacement, except it lives in your source code instead of your Apache config.
It may be a little overkill for what you need, but it handles this stuff pretty easily.
In Rails 3
#config/routes.rb
Example::Application.routes.draw do
redirect_proc = Proc.new { redirect { |params, request|
URI.parse(request.url).tap { |x| x.host = "www.example.net"; x.scheme = "https" }.to_s
} }
constraints(:host => "example.net") do
match "(*x)" => redirect_proc.call
end
constraints(:scheme => "http") do
match "(*x)" => redirect_proc.call
end
# ....
# .. more routes ..
# ....
end
I think the issue is you are running on Heroku. Check the Heroku documentation regarding Wildcard domains:
"If you'd like your app to respond to any subdomain under your custom domain name (as in *.yourdomain.com), you’ll need to use the wildcard domains add-on. ..."
$ heroku addons:add wildcard_domains
Also look at Redirecting Traffic to Specific Domain:
"If you have multiple domains, or your app has users that access it via its Heroku subdomain but you later switched to your own custom domain, you will probably want to get all users onto the same domain with a redirect in a before filter. Something like this will do the job:"
class ApplicationController
before_filter :ensure_domain
TheDomain = 'myapp.mydomain.com'
def ensure_domain
if request.env['HTTP_HOST'] != TheDomain
redirect_to TheDomain
end
end
end
Try this
def check_uri
if Rails.env == 'production' && request && (request.subdomains.first != "www" || request.protocol != 'https://')
redirect_to "https://www.mysite.com" + request.path, :status => 301 and return
end
end
Your best bet would be to set up redirect with your DNS provider, so it happens long before any request reaches your server. From the Heroku Dev Center:
Subdomain redirection results in a 301 permanent redirect to the specified subdomain for all requests to the naked domain so all current and future requests are properly routed and the full www hostname is displayed in the user’s location field.
DNSimple provides a convenient URL redirect seen here redirecting from
the heroku-sslendpoint.com naked domain to the
www.heroku-sslendpoint.com subdomain.
For proper configuration on Heroku the www subdomain should then be a
CNAME record reference to yourappname.herokuapp.com.
It's not just DNSimple that does this. My DNS provider is 123 Reg and they support it but call it web forwarding.

analytics if on production site, not a local or heroku subdomain

This question is about getting an analytics script to run in one of these three environments.
mysite.heroku.com
mysite-staging.heroku.com
mysite.com - this is the only one I want it to run on.
This is how I plan to lay it out, but any suggestions are welcome.
In my helper
def render_analytics
if local_request? || #on a Heroku subdomain
false
else
true
end
end
In my layout
<%= render 'shared/analytics' if render_analytics %>
render_analytics returns a boolean: true if on mysite.com, false if a local_request? or on a Heroku subdomain (ex: mysite.heroku.com || mysite-staging.heroku.com)
So how can I find out if it is coming from Heroku.
Use hostname:
if local_request? || `hostname` =~ /heroku/i
A cleaner solution is to set a constant in your environment during deployment that allows you to know whether you are on Heroku. As the Heroku deploy process is pretty opaque in terms of letting you dork around with config files, you might have your method memoize the result so you aren't doing a system call each time you render a view.
I just did something similar with a method that checks the database adapter to account for differences between my development environment and Heroku. Here's my lib/adapter.rb:
class Adapter
cattr_reader :adapter
def self.postgres?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter == 'postgresql'
end
def self.mysql?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter == 'mysql'
end
def self.sqlite?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter.include?('sqlite')
end
end
Note that in addition to this, you have to change application.rb such that lib is added to your autoload path:
config.autoload_paths += Dir["#{config.root}/lib/**/"] # include all subdirectories

how to setup rails Authenticity Token to work with multiple domains?

I'm building an app that uses subdomains as account handles (myaccount.domain.com) and I have my sessions configured to work across the sub-domains like so:
config.action_controller.session = {:domain => '.domain.com'}
In addition to the subdomain a user can input a real domain name when they are creating their account. My Nginx config is setup to watch for *.com *.net etc, and this is working to serve out the pages.
The problem comes when a site visitor submits a comment form on a custom domain that was input by the user. The code is throwing an "Invalid AuthenticityToken" exception. I'm 99% sure this is because the domain the user is on isn't specified as the domain in the config.action_controller.session. Thus the authenticity token isn't getting matched up because Rails can't find their session.
So, the question is: Can you set config.action_controller.session to more than 1 domain, and if so can you add / remove from that value at runtime without restarting the app?
I found the answer to this question here: http://codetunes.com/2009/04/17/dynamic-cookie-domains-with-racks-middleware/
This solution worked for me because my app was running on Rails 2.3.5, which uses Rack. The request comes from web server, goes through middleware layers and enters the application. So this middleware layer detects the host with which the application is accessed and sets cookie domain for the request. Here it is:
# app/middlewares/set_cookie_domain.rb
class SetCookieDomain
def initialize(app, default_domain)
#app = app
#default_domain = default_domain
end
def call(env)
host = env["HTTP_HOST"].split(':').first
env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{#default_domain}"
#app.call(env)
end
def custom_domain?(host)
domain = #default_domain.sub(/^\./, '')
host !~ Regexp.new("#{domain}$", Regexp::IGNORECASE)
end
end
# turn it on in environment.rb
config.load_paths += %W( #{RAILS_ROOT}/app/middlewares )
# production.rb
config.middleware.use "SetCookieDomain", ".example.org"
.example.org is the default domain that will be used unless the application is accessed via custom domain (like site.com), we give it different values depending on environment (production/staging/development etc).
# tests/integration/set_cookie_domain_test.rb (using Shoulda and Webrat)
require 'test_helper'
class SetCookieDomainTest < ActionController::IntegrationTest
context "when accessing site at example.org" do
setup do
host! 'example.org'
visit '/'
end
should "set cookie_domain to .example.org" do
assert_equal '.example.org', #integration_session.controller.request.session_options[:domain]
end
end
context "when accessing site at site.com" do
setup do
host! 'site.com'
visit '/'
end
should "set cookie_domain to .site.com" do
assert_equal '.site.com', #integration_session.controller.request.session_options[:domain]
end
end
context "when accessing site at site.example.org" do
setup do
host! 'site.example.org'
visit '/'
end
should "set cookie_domain to .example.org" do
assert_equal '.example.org', #integration_session.controller.request.session_options[:domain]
end
end
end

Resources