14 Jul 2007 fxn   » (Master)

Algorithm::Combinatorics

I published version 0.24 of Algorithm::Combinatorics today, which provides subsets().

Link generation for websites with SSL in Rails

Secure Actions is a helpful plugin that facilitates URL generation for websites that use SSL. The plugin provides a class method for controllers to declare their secure actions:


  class AccountController < ApplicationController
    include SecureActions
    require_ssl :login, :signup
  end
and that's enough to get URL generation with the right protocol. To accomplish that the plugin implements a hook Rails invokes at the beginning of URL generation in url_for. That hook is called default_url_options and receives the hash used in the view, say:

  <% form_tag :controller => 'account', :action => 
'login' do %>
  <% end %>
and so a table lookup is enough to determine the protocol needed by that URL. By default URLs in images, JavaScript includes, etc. are absolute paths in Rails, so with zero effort included resources inherit the right protocol as well.

The plugin is enabled via a global flag USE_SSL, so that you can turn SSL on just in production mode for example.

As of this writing there's no way to declare an entire controller to be secure, I wrote this trick in application.rb:


  # A controller makes this call to declare all their actions run behind SSL.
  # The call must be put at the bottom of the code, so that the public methods
  # are known and returned by public_instance_methods.
  def self.this_controller_only_responds_to_https
    include SecureActions
    require_ssl *self.public_instance_methods(false).map(&:to_sym)
  end
That way, you can declare everything to be secure in one shot:

  class BankAccountController < ApplicationController
    ...
    this_controller_only_responds_to_https
  end
Note that we need to put that call at the bottom of the class definition so all methods are known, I don't like such an important declaration not to be at the top, but the technique requires it that way.

Of course, if the controller has some magic via method_missing or whatever that won't be enough, but in that case you probably know what to do. Also controller inheritance trees may need some tweaking for inherited actions. Some simple array arithmetic will probably suffice in that case. You don't need to be precise nonetheless, the plugin just builds a hash table that maps symbols, so if :object_id ends up there it won't hurt, you are not going to link to BankAccountController#object_id anyway.

I found an interesting gotcha using this plugin: In Rails automatic class loading is implemented via const_missing. That is, when you try to use an unknown class that Ruby hook is triggered, and Rails looks up the class definition in the application tree according to well-known conventions. For instance, if the callback is called because you try to use UserController, Rails tries to require user_controller.rb. Since RAILS_ROOT/app/controllers belongs to $LOAD_PATH the file is executed and the controller loaded. In production mode that's done once, and in development mode that's done per request because of the way class reloading is implemented.

Right, suppose you have a link to a login form /account/login in the non-secure home of your application /public/index. That link should be secure, but when you launch the server nobody has yet hit anything related to the AccountController so that class is not yet loaded. The plugin knows nothing about the secure actions of the controller in consequence, and therefore generates a non-secure link. The first login in that setup goes in plain. That argument applies to the entire website, until a controller is loaded the plugin knows nothing about his secure actions.

To fix this I force controller loading in environment.rb with this little code:


  # Trigger controller class loading to execute SSL-related
  # declarations, this way we have the correct links right away.
  require 'application'
  ActionController::Routing.possible_controllers.each do |c|
    # known to work without directories
    "#{c.camelize}Controller".constantize
  end

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!