HOWTO Put a controller under a SSL subdomain

Let’s imagine that you need to create a secure login area, for example if you own http://www.domain.com you may want to put the user login page on https://secure.domain.com. How can you do it?

The best solution in my opinion is to put all the login procedures in a dedicated login controller (eg the ‘sessions’ controller if you are using the RESTful authentication plugin) and then modify a bit the url_for method in order to modify the calls to that controller.

Part 1, A little configuration.

In our environment.rb we’re going to create an array with the name of the controllers we want to put under a different subdomain and under SSL:

SECURE_MODE = [ /\/sessions$/ ].freeze

SECURE_SUBDOMAIN = "secure".freeze

Part 2, Url_for tweaking

Then in our application.rb we have to tweak a bit the url_for function in order to let the defined controllers being handled correctly.

def url_for(options = {}, *parameters_for_method_reference)

tmp_result = super((options.is_a?(Hash) ? options.merge({:only_path => false})

: options), parameters_for_method_reference)

if not (tmp_result =~ Regexp.new(request.host)).nil?

# this tweak is necessary just beacuse I use WebBrick for my development environment (aka I need to specify the port)

if ENV['RAILS_ENV'] == ‘production’

cut_result = tmp_result.to_s.gsub( Regexp.new("#{request.protocol}# {request.host}") ,"" )

elsif ENV['RAILS_ENV'] == ‘development’

cut_result = tmp_result.to_s.gsub( Regexp.new("#{request.protocol}# {request.host_with_port}"), "" )

end

# We’re going to talk about this function later

url_to_go = dispatch_ssl(cut_result)

end

super((url_to_go.nil? ? tmp_result : url_to_go), parameters_for_method_reference)

end

Part 3 Dispatching urls

dispatch_ssl is a function I wrote that receive a partial url (eg /session) and returns the path modified in order to match the new subdomain and protocol rules.

def dispatch_ssl(tmp_result = request.request_uri)

subdomain = SECURE_SUBDOMAIN.blank? ? "" : SECURE_SUBDOMAIN + "."

# need secure is a function that simply check if this path need to be put under SSL

if need_secure?(tmp_result)

if request.protocol == ‘http://’

url_to_go = "https://#{subdomain}" + request.host + tmp_result

end

else

if request.protocol == ‘https://’

host = request.host.gsub(Regexp.new(subdomain),"")

url_to_go = ‘http://’ + host + tmp_result

end

end

return url_to_go

end

Last but not Least: need_secure? and ssl_require

The first function is used by dispatch_ssl to check if the current url match the SECURE_MODE array, the second one (ssl_require) have to be called in a before_filter directive and detects if the user is trying to access a secure area without the correct subdomain and protocol; if this happens the user will be redirected to the correct url.

def need_secure?(uri)

SECURE_MODE.each{ |s|

a = (uri =~ s)

return true unless a.nil?

}

return false;

end

def ssl_required

if not (redir = dispatch_ssl.to_s).blank?

redirect_to(redir) and return false

end

end

Leave a Comment

*