Tatva-Artha

meaning of "it"

Archive for September, 2009

Authlogic: after the initial hype

without comments

On my rails projects, I’ve used restful_authentication before and I am using authlogic now. Even though I have passed the initial hesitation phase with authlogic, I can’t say that I am totally sold.

What I like about authlogic is it refrains from providing any controller/view level support and handles model layer better. Instead, it provides a solid model functionality that is similar in principle to ActiveRecord and provides lots of how-to examples on how you might code your controllers/views/workflows. It does it well. Although authlogic is complemented for refraining from too much code generation, Authlogic still does a lot of magical stuff. You encounter this readily with tests. Instantiating and persisting a user will log you in! It is hard to test your User model from console since authlogic will fail if you try to instantiate User object in console. You have to jump thru hoops (include Authlogic, set proper controller reference) to get it to work.

Problem with Perishable Token:
Today I encountered another issue with authlogic realted to perishable token. According to docs, perishable tokens are useful for implementing features such as reset-password and confirm email. These functionalities are essentially similar in implementation in that application generates a token and emails url containing this token to the user’s registered email address. User clicks on the url and resets his password or confirms that email is indeed his email. The implementation details are explained very well by author here.

However, it didn’t work in my case. The problem, I believe, is that perishable_token changes often… too often actually. Looking side the codebase for Authlogic::ActsAsAuthentic::PerishableToken reveals this:

def self.included(klass)
  return if !klass.column_names.include?("perishable_token")
  klass.class_eval do
    extend ClassMethods
    include InstanceMethods
 
    before_save :reset_perishable_token, :unless => :disable_perishable_token_maintenance?
  end
end
def reset_perishable_token
  self.perishable_token = Random.friendly_token
end

As you can see, the token changes on each save. This can be readily verified from console:

>> u = User.find_by_email("sjain@iit.edu")
#=> #<User id: 1, email: "sjain@iit.edu", perishable_token: "6qLHkU_vBQK6rLQGy-oL", login_count: 54, failed_login_count: 0, last_request_at: "2009-09-30 18:00:46", current_login_at: "2009-09-30 18:00:42", last_login_at: "2009-09-30 18:00:42", current_login_ip: "127.0.0.1", last_login_ip: "127.0.0.1"...>
>> u.perishable_token
#=> "6qLHkU_vBQK6rLQGy-oL"
>> u.save
#=> true
>> u.perishable_token
=> "1tlcpvc_sCGtNEfIQCt1"
>>

Everytime the user object is saved, the token changes. In a running app this could happen for any number of reasons. I wonder, if having other authlogic magic attributes like login_count, last_request_at etc. causes the instance to get saved immediately after the token was sent in email, practically overwriting the token that was critical to password-reset workflow…

Solution:
Fortunately, Authlogic allows you to take control and manage token yourself. To do this, you disable the automatic maintenance of perishable_token by authlogic as follows:

class User < ActiveRecord::Base
  acts_as_authentic
  disable_perishable_token_maintenance(true)
end

By doing this, authlogic leaves this column in database alone. You can now reset the token when you need it. Here is what my password-reset and confirm-email hooks look like then:

class User < ActiveRecord::Base
  def deliver_confirm_email_instructions!
    reset_perishable_token!
    Notifier.deliver_confirm_email_instructions(self)
  end
 
  def deliver_password_reset_instructions!
    reset_perishable_token!
    Notifier.deliver_password_reset_instructions(self)
  end
end

In addition, since we took the control of perishable_token in our hands and that it is a non-nullable field in database, we have to reset the token when a new user registers for an account on our website.

class UsersController < ApplicationController
  def create
    @user = User.new(params[:user])
    # set the token manually
    @user.reset_perishable_token
    if @user.save
      @user.deliver_confirm_email_instructions!
      flash[:notice] = "Registration successful. Please check your email to confirm your email address."
      redirect_to root_url
    else
      render :action => 'new'
    end
  end
end

Conclusion:
All in all, authlogic is a well designed authentication library. However, you can’t get around the fact that authentication and its surrounding sub-features (password reset, registration, email confirmation) can get complex. When coming away from restful_authentication to authlogic you will be glad to find intuitive for its API that is very similar to ActiveRecord, configurablility to enable/disable features that you need and don’t need. But, don’t expect an easy ride. You are bound to have to dive into the codebase to take advantage of some of the complex features.

Written by Sharad

September 30th, 2009 at 8:33 pm

Posted in All

Setting up Sphinx Search on remote server

without comments

If you are using sphinx gem for search functionality in your rails application, here is a quick rundown of commands that you will need to install and setup sphinx server and rails plugin on remote server.

Download and installl Sphinx package:

# download from http://sphinxsearch.com/downloads.html
$ tar zxf <tar.gz file>
$ ./configure
$ make
$ sudo make install

The rails app should already list gem dependency. Install the necessary gem on system:

# environment.rb:
config.gem 'freelancing-god-thinking-sphinx', :lib => 'thinking_sphinx'
$ sudo gem install freelancing-god-thinking-sphinx

Here are 2 rake commands to index the database for sphinx search. And to start the sphinx daemon when the app is running.

$ rake thinking_sphinx:index (have a cron job for this)
$ rake thinking_sphinx:start (start the daemon)

There is also the capitate gem that makes managing sphinx on remote server easier.

Written by Sharad

September 30th, 2009 at 1:52 am

Posted in All

Rails monkeypatching to pin-point offending code

without comments

Monkey-patching is generally a bad thing but it can help you pin point offending code. I ran into a situation today where I was getting logger to be nil inside ActiveRecord model class. Normally, every ActiveRecord object has reference to logger for logging purposes. This logger is ideally never nil.

I knew, this must be a code somewhere in my codebase that must be setting it to nil. The trouble with rails’ dynamic nature is you can’t find reference to a method call in your IDE. You are left with brute-force find/grep … or monkey patching.

I was able to pin-point offending code using following code:

# this can go in environment.rb temporarily
module ActiveRecord
  class Base
    class << self
      def logger_with_nil_check=(new_logger)
        raise "logger cannot be set to nil" if !new_logger
        logger_without_nil_check
      end
      alias_method_chain :logger=, :nil_check
    end
  end
end

Isn’t this what they call “developer freedom”.

Written by Sharad

September 25th, 2009 at 8:17 pm

Posted in All

Getting session_id in rails

with one comment

If you are using database sessions, rails sessions table stores session information in “sessions” table that contains two columns: “session_id” and “data”.
Now, if you need to know the session_id of current session your first instinct would be to do the following:

session.session_id

This will not work since session is just hash containing name/value pairs and not an ActiveRecord object. So what do we do introspect session’s own information.

Solution: We use “request.session_options”. It is a hash that contains session_id and a few more options related to session.

request.session_options.inspect ={:secure=>false, :secret=>"ZBYULTOIJYLYWVNONXNSWUJCKMVVFPIZCEEGDAIE", :expire_after=>nil, :key=>"_SOMEGT_session", :id=>"4d4999d4650f4b21121c8f2b65917fac", :cookie_only=>true, :httponly=>true, :path=>"/", :domain=>nil}

Here :id is the session_id and :key is the cookie name under which it stores the session_id.

Now I know.

Written by Sharad

September 25th, 2009 at 4:57 am

Posted in All

Working with Authlogic in script/console

with 6 comments

When probing / debugging issues with Authlogic on rails console, you want to be able to create UserSessions and authenticate just like a live app does. However, you may run into issues if you plainly try to instantiate and save UserSession objects in console.

>> UserSession.new(:email => "validemail@domain.com", :password => "password")
Authlogic::Session::Activation::NotActivatedError: You must activate the Authlogic::Session::Base.controller with a controller object before creating objects
	from /var/lib/gems/1.8/gems/authlogic-2.1.1/lib/authlogic/session/activation.rb:47:in `initialize'
	from /var/lib/gems/1.8/gems/authlogic-2.1.1/lib/authlogic/session/klass.rb:61:in `initialize'
	from /var/lib/gems/1.8/gems/authlogic-2.1.1/lib/authlogic/session/scopes.rb:79:in `initialize'
	from (irb):2:in `new'
	from (irb):2

Authlogic uses framework adapters (RailsAdapter or MerbAdapter) to extract information such as session/cookies when working on authentication stuff. In order to enable this same mechanism for rails console, you need to do the following inside console:

>> Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::RailsAdapter.new(self)

After this, you should be able to create UserSession objects like you normally would:

>> us = UserSession.new(:email => "validemail@domain.com", :password => "password")
=> #<UserSession: {:password="<protected", :email="validemail@domain.com"}>
>> us.save!
  User Load (0.6ms)   SELECT * FROM `users` WHERE (LOWER(`users`.email) = 'validemail@domain.com') LIMIT 1
  SQL (0.2ms)   BEGIN
  User Update (0.4ms)   UPDATE `users` SET `failed_login_count` = 2, `updated_at` = '2009-09-25 02:31:57', `perishable_token` = 'bP_s1_z9gwCFmrIX9cdV' WHERE `id` = 1
  SQL (1706.2ms)   COMMIT
Authlogic::Session::Existence::SessionInvalidError: Your session is invalid and has the following errors: Password is not valid
	from /var/lib/gems/1.8/gems/authlogic-2.1.1/lib/authlogic/session/existence.rb:85:in `save!'
	from (irb):11

Written by Sharad

September 25th, 2009 at 2:38 am

Posted in All

How to update current system time on Unix/Linux

without comments

$ sudo date --set='Thu Sep 24 16:00:30 CDT 2009'

Written by Sharad

September 24th, 2009 at 9:01 pm

Posted in All

Reverse Tunneling and mysql

without comments

Recently, I needed my app in staging to use my local database. Since staging server was on a different network that didn’t have access to my local machine (DMZ), I had to use ssh tunnelling. Here is how I did it:

I setup reverse tunnel that allowed mysql connection from staging server to my local database:

ssh -R 33306:localhost:3306 staging_server

This setup reverse ssh tunnel that allowed anybody trying to connecting to msyql server on staging_server at port 33306 to be routed to mysql server running on my local machine on default port (3306).

With this, I was able to do following on remote server:

mysql -h 127.0.0.1 -P 33306 -u root -p

This comment is trying to connect to mysql on port 33306 on localhost which is getting tunneled to mysql server on my local machine. In the above command, “-h 127.0.0.1″ is required since otherwise mysql will try to connect to “localhost” using /tmp/mysql.sock (socket connection). Also, firewall on staging_server prevented any incoming connection for port 33306, so I couldn’t use “-h 192.168.84.70″ which was the real IP of staging server. Instead, I had to use 127.0.0.1.

Nothing earth-shaking, just a few quirks when getting reverse tunnelling to work.

Written by Sharad

September 23rd, 2009 at 10:02 pm

Posted in All

Respect the Pomodoro

without comments

An article on agile pair programming on NY Times, it is nice to see what is a routine thing in many of software development thru the eyes of somebody who is newly excited about it. It brings the fresh pair of critique and any reviews are expected to be positive.

Also, learnt about a 5 minute break every 25 minutes… called “Respect the Pomodoro” (Pomodoro == Tomato, in Italian).

Written by Sharad

September 21st, 2009 at 2:40 pm

Posted in All

Defining custom roles/servers in capistrano

without comments

Capistrano works seamlessly for standard deployment where your app is deployed on at most 3 servers, identified as roles: app, web, db. In most cases, these usually point to the same machine where everything is deployed.

set :deploy_host, "192.168.84.70"
role :app, deploy_host
role :web, deploy_host
role :db,  deploy_host, :primary => true

On a recent project, I needed to do some setup on a 4th machine. And so I added a role

set :deploy_host, "192.168.84.70"
role :app, deploy_host
role :web, deploy_host
role :db,  deploy_host, :primary => true
role :asterisk, "asterisk.domain.com"

Soon, I noticed that default rails tasks such as update_code, symlink started attempting to deploy to this new role. I wondered why those tasks weren’t setup to run only on “app” role. There had to be a solution since I am not the first one who would have needed to define a custom role.

A little digging inside capistrano code revealed :o nly and :except options. Most of these default tasks skipped any roles that were defined with option { :no_release => true }.

# <capistrano_gem>/lib/capistrano/recipes/deploy.rb
task :update_code, :except => { :no_release => true } do
end
task :symlink, :except => { :no_release => true } do
end

So, I had to change my role definition to:

role :asterisk, "asterisknow.pathf.com", :no_release => true

Googling a bit revealed that this was introduced in v1.1.9, as described by the author here.

Note to self: Always pay attention to release notes!

[My capistrano version is v2.5.8.]

Written by Sharad

September 18th, 2009 at 5:35 pm

Posted in All

Rails: Optimizing Database Indexes using query_analyzer and query_reviewer

without comments

When trying to identify various database query indexes (indices?) for optimizing databases, two plugins are useful.

Query Analyzer is very useful and has been around for a quite some time. It runs an EXPLAIN on each query sent to database and prints tabular output on console.

Query Reviewer is a new one that I used recently. It is very similar to Query Analyzer in functionality. However, it displays the result rendered on top of you existing page using absolutely positioned divs. It is well designed to be not very intrusive and can be enabled/disabled.

Both plugins require just installing them in vendor/plugins folders. That’s it.

Warning: Both these plugins work with mysql database only, since they depend on mysql’s EXPLAIN command.

Written by Sharad

September 18th, 2009 at 2:14 pm

Posted in All