Programming

Easy Rails API Authentication Using restful-authentication

You’ve got a Rails application and you’d like to add an API. This tutorial assumes you are using restful-authentication.

Creating the API key

You’ll need to add a new column, api_key, to your users table. This field will store the unique API key used for authentication.

./script/generate migration AddApiKeyToUsers
class AddApiKeyToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :api_key, :string, :limit => 40, :default => ""
  end
 
  def self.down
    remove_column :users, :api_key
  end
end

The API key is a simple SHA hash based on the current time and a random number — we need this to be unique. We’re not going to enable the API for a user by default, though you could easily do this using before_create. Instead, we’ll let the user enable or disable the API using the API Keys Controller, which we’ll create next.

Make the following changes to your app/models/user.rb file:

class User < ActiveRecord::Base
 
  def enable_api!
    self.generate_api_key!
  end
 
  def disable_api!
    self.update_attribute(:api_key, "")
  end
 
  def api_is_enabled?
    !self.api_key.empty?
  end
 
  protected
 
    def secure_digest(*args)
      Digest::SHA1.hexdigest(args.flatten.join('--'))
    end
 
    def generate_api_key!
      self.update_attribute(:api_key, secure_digest(Time.now, (1..10).map{ rand.to_s }))
    end
 
end

Now, let’s create the API Keys Controller, which we’ll use to allow the user to create, re-generate, and delete their API Key. This effectively allows the user to enable and disable API access to their account.

./script/generate controller APIKeys
class APIKeysController < ApplicationController
  before_filter :login_from_cookie
  before_filter :login_required
 
  # Create or re-generate the API key
  def create
    current_user.enable_api!
 
    respond_to do |format|
      format.html { redirect_to edit_user_path(current_user) }
    end
  end
 
  # Delete the API key
  def destroy
    current_user.disable_api!
 
    respond_to do |format|
      format.html { redirect_to edit_user_path(current_user) }
    end
  end
 
end

Add the resource to config/routes.rb

map.resource :api_key

You’ll need to allow the user to enable and disable the API, as well as re-generate their API key if they feel it’s been compromised. Add something like this to app/views/users/edit.html.erb:

<% if @user.api_is_enabled? %>
  <p>
    Your API Key:
    (<%= link_to "re-generate", api_key_path, :method => :post %> | <%= link_to "disable", api_key_path, :method => :delete %>)
  </p>
  <p>
    <strong><%= @user.api_key %></strong>
  </p>
<% else %>
  <p>
    You'll need a unique key to make calls to the API.  Remember to keep this key a secret as it can be used to access your account.
  </p>
  <p>
    <%= link_to("Get a key", api_key_path, :method => :post) %>
  </p>
<% end %>

Authenticating the API Key

Add the following to lib/authenticated_system.rb:

def login_from_api_key
  self.current_user = User.find_by_api_key(params[:api_key]) unless params[:api_key].empty?
end

Modify lib/authenticated_system.rb to add login_from_api_key as follows:

def current_user
  @current_user ||= (login_from_session || login_from_api_key || login_from_basic_auth || login_from_cookie) unless @current_user == false
end

Optionally, if you’d also like to support access to the API via HTTP Basic Authentication using the API key in addition to a user’s login and password, you can make the following change to app/models/user.rb:

def self.authenticate(login, password)
  return nil if login.blank? || password.blank?
  if password.downcase == "x"
    # This is an API request
    u = find_by_api_key(login)
  else
    u = find_by_login(login.downcase)
    u && u.authenticated?(password) ? u : nil
  end
end

When prompted to log in using HTTP Basic Authentication, use the API Key as the login and ‘X’ as the password.

Testing it Out

Assuming you have a RESTful resource called Items, you should be able to use curl as so:

curl http://www.yoursite.com/items/1.xml?api_key=356a192b
Saturday, May 16th, 2009 Programming 7 Comments

God Init.d Script for CentOS

This is the /etc/init.d/god script we are using for God on CentOS 5.

Get God

sudo gem install god

Create a Default Config File

sudo touch /etc/god.conf

Create the Init Script

Put this in /etc/init.d/god

#!/bin/bash
#
# God
#
# chkconfig: - 85 15
# description: start, stop, restart God
#
 
RETVAL=0
 
case "$1" in
    start)
      /usr/local/bin/god -P /var/run/god.pid -l /var/log/god.log
      /usr/local/bin/god load /etc/god.conf
      RETVAL=$?
  ;;
    stop)
      kill `cat /var/run/god.pid`
      RETVAL=$?
  ;;
    restart)
      kill `cat /var/run/god.pid`
      /usr/local/bin/god -P /var/run/god.pid -l /var/log/god.log
      /usr/local/bin/god load /etc/god.conf
      RETVAL=$?
  ;;
    status)
      RETVAL=$?
  ;;
    *)
      echo "Usage: god {start|stop|restart|status}"
      exit 1
  ;;
esac
 
exit $RETVAL

Make it Executable

sudo chmod a+x /etc/init.d/god

Ensure God Launches on System Boot

sudo chkconfig --add god
sudo chkconfig --level 345 god on

Fire it Up

sudo /etc/init.d/god start
Friday, May 15th, 2009 Programming No Comments

Private, Authenticated RSS Feeds in Rails

So, you want to allow your users to access a RSS feed of private data in their account.  Something like,

http://subdomain.yoursite.com/comments/123abc/feed.rss, where “123abc” is a secret token.

Here’s how to do this in Rails 2.1.2, though it will most likely work in other versions of Rails.  In this example, each Account has_many Comments, and we’d like to get a RSS feed of comments.

1. Create a route for the feed in your routes.rb file.

map.resources :accounts do |account|
  account.comments_feed 'comments/:token/feed.:format', :controller => 'comments', :action => 'feed', :token => nil
end

2. Create a new migration and add the feed_token to the Account.

class AddFeedTokenToAccount < ActiveRecord::Migration
  def self.up
    add_column :accounts, :feed_token, :string, :limit => 40, :default => ""
  end
 
  def self.down
    remove_column :accounts, :feed_token
  end
end

3. When a new Account is created, create the feed_token. We’ll just use a standard SHA1 hash of the current time and the account id. This should be unique enough. Let’s also create a method to validate the feed token. All of this goes in app/models/account.rb

class Account < ActiveRecord::Base
  before_create :create_feed_token
 
  def valid_feed_token?(token)
    self.feed_token == token
  end
 
  protected
 
  def create_feed_token
    self.feed_token = Digest::SHA1.hexdigest(Time.now.to_s + self.id.to_s)
  end
end

4. Add a new feed method to your Comments controller. This goes in app/controllers/comments_controller.rb

def feed
  @account = Account.find(params[account_id])
  @comments = @account.comments
  @token = params[:token]
 
  respond_to do |format|
    if @account.valid_feed_token?(@token)
      format.rss { render :layout > false }
    else
      format.rss { render :nothing > true, :status > :forbidden }
    end
  end
end

5. Create the file app/views/comments/feed.rss.builder. This is what generates the RSS feed.

xml.instruct! :xml, :version => "1.0"
xml.rss :version => "2.0" do
  xml.channel do
    xml.title "Comments"
    xml.description "A bunch of comments"
    xml.link account_comments_url(@account)
 
    for comment in @comments
      xml.item do
        xml.title comment.title
        xml.description comment.body
        xml.pubDate comment.created_at.to_s(:rfc822)
        xml.link account_comments_url(@account)
      end
    end
  end
end

6. Link to the comments RSS feed somewhere on your site. For example, somewhere in app/views/comments/index.html.erb place the following link:

<%= link_to 'Subscribe', account_comments_feed_path(@account, :format => :rss, :token => @account.feed_token)) %>
Tuesday, February 17th, 2009 Programming 4 Comments

Backup Your WordPress Blog to Amazon S3 using Ruby

I’ve been running this blog for months without a backup solution.  Mostly because I didn’t really care for any of the existing solutions which required me to do too many manual steps.  I wanted something completely hands-off (automatically run every night), secure and reliable (store the backups on Amazon’s S3).

So, enjoy wordpress-s3-backup.  Use it to backup your WordPress blog — both the database and the site.  It’s a Ruby Rake script and you’ll need the AWS-S3 gem.  Stick it in your crontab to run nightly and move on with your life.

Get it here.

Thursday, January 15th, 2009 Programming 3 Comments

How to Set an Expires Header in Apache

Setting an Expires (or Cache-Control) header in Apache will help speed up your Web site.  We’re running Apache 2.x, and define an expires header for all of our static assets (images, stylesheets, and scripts).

Just add the following to your <VirtualHost> section of your Apache configuration:

ExpiresActive On
ExpiresByType image/png “now plus 365 days”
ExpiresByType image/jpeg “now plus 365 days”
ExpiresByType image/gif “now plus 365 days”
ExpiresByType application/javascript “now plus 365 days”
ExpiresByType application/x-javascript “now plus 365 days”
ExpiresByType text/javascript “now plus 365 days”
ExpiresByType text/css “now plus 365 days”

You can read all about expires headers by reading Yahoo!’s Best Practices for Speeding Up Your Website guide.

Also, be sure to check out our post on how to speed up your Website by Configuring Apache to Gzip Your Components.

Tuesday, December 30th, 2008 Programming 2 Comments

How to Configure Apache to Gzip Your Components

Compressing your Web components will help speed up your Website.  The majority of your visitors will benefit as most all Web browsers support gzip compression.  You’ll want to compress all text, which includes html, css, javascript, xml, json, etc.

Apache 2.x uses mod_deflate, and all you need to do is add the following to your <VirtualHost> section of your Apache configuration:

AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/x-javascript
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

All you truly need is the first line.  The BrowserMatch lines are there to handle issues with old browsers.

You can read all about gzipping by reading Yahoo!’s Best Practices for Speeding Up Your Website guide.

Also, be sure to check out our post on How to Set an Expires Header in Apache, which will also help speed up your Website.

Tuesday, December 30th, 2008 Programming 3 Comments

Calling Rails Render Partial in a Model or Background Task

Why?

You want to store raw HTML in the database for a given model and you want to do this with the ease of partials and not a bunch of nasty string manipulation.

Rails makes this difficult for you by not giving you access to the render method when not called from ActionController.  However, you’d like to call the partial helper from within a model, or a background task (such as BackgrounDRb or Starling).

How?

As an example, let’s say you have a Page model made up of a title and a body.  You’d like to cache what a rendered page would look like in the model as the attribute cached_content.

The old way.  This really sucks.

class Page < ActiveRecord::Base

  def write_cache
    the_content = "<div class='title'>"
    the_content += "<h1>#{self.title}</h1>"
    the_content += "</div>"
    the_content += "<div class='body'>"
    the_content += "#{self.body}"
    the_content += "</div>"

    self.cached_content = the_content
    self.save
  end

end

The new way.  This is so much easier and cleaner.

class Page < ActiveRecord::Base

  def write_cache
    self.cached_content = ActionView::Base.new(Rails::Configuration.new.view_path).render(:partial => "pages/show", :locals => {:page => self})
    self.save
  end

end

app/views/pages/_show.html.erb

<div class="title">
  <h1>
    <%= page.title %>
  </h1>
</div>

<div class="body">
  <%= page.body %>
</div>
Wednesday, October 29th, 2008 Programming 11 Comments

Pilot your Project with Cockpit

This past weekend we competed in RailsRumble 2008 – a 48-hour Ruby on Rails programming competition where the goal is to produce a complete application in a weekend.  We partnered with Marcus Mateus of SimpliTex to create Cockpit.

We were tired of constantly logging in to various sites to see key data for the various projects we were working on. While RSS feeds can provide much of the data, digging through RSS feeds doesn’t provide you with a true executive level overview of your project. You really want to see only the most important, relevant, and timely data… and ideally act on it. Cockpit is our answer to this most annoying problem.

In the 48 hours we had for the competition we decided to focus on getting a simple proof of concept view of key data from just four widely used web services. Lots of other sites provide similarly useful data, and full interactivity would be great, but the clock was a tickin’, so we limited our scope to the following:

We have high hopes that with the feedback that comes via the competition we will be able to create an application to help rescue all of us from project overload. Over the next 10 days, anyone can vote for Cockpit.  So far we’re off to a great start.  As of the first day we’re ranked #10 of 131 in the Usefulness category — in our opinion the only category that truly matters in the long run.

The competition was a blast, even if tiring. A great big thank you goes out to all the organizers, volunteers, and sponsors.

Note: Most of this article was authored by Marcus Mateus and was originally posted to the SimpliTex blog.

Thursday, October 23rd, 2008 Programming No Comments

TableSorter: Filter Results Based on Search Query

So, you want to use the TableSorter jQuery plugin and you want to be able to search the table and filter the results. Attached is a companion plugin we’ve written, called tablesorterFilter, which will extend tablesorter to provide real-time filtering of rows which match a search query!

Usage

<script type="text/javascript">
  jQuery(document).ready(function() {
    $("#myTable")
      .tablesorter({debug: false, widgets: ['zebra'], sortList: [[0,0]]})
      .tablesorterFilter({filterContainer: $("#filter-box"),
                          filterClearContainer: $("#filter-clear-button"),
                          filterColumns: [0],
                          filterCaseSensitive: false});
  });
</script>
Search: <input name="filter" id="filter-box" value="" maxlength="30" size="30" type="text">
<input id="filter-clear-button" type="submit" value="Clear"/>
 
<table id="myTable">
  <thead>
    <tr>
      <th>Last Name</th>
      <th>First Name</th>
      <th>Email</th>
      <th>Web Site</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Smith</td>
      <td>John</td>
      <td>jsmith@gmail.com</td>
      <td>http://www.jsmith.com</td>
    </tr>
    <tr>
      <td>Doe</td>
      <td>Jason</td>
      <td>jdoe@hotmail.com</td>
      <td>http://www.jdoe.com</td>
    </tr>
  </tbody>
</table>

Configuration

tablesorterFilter takes up to six parameters:

  • filterContainer – The DOM id of the input box where the user will type the search string.
  • filterClearContainer – (optional) The DOM id of the button, image, or whatever which will clear the search string and reset the table to it’s original, unfiltered state.
  • filterColumns – An array of columns, starting at 0, which will be searched.
  • filterCaseSensitive – (optional) Boolean stating whether the search string is case sensitive. The default is false.
  • filterWaitTime – (optional) Time after last key press to start filtering. The default is 500 (milliseconds).
  • filterFunction – (optional) Custom function to filter by column text. The default is the plugin function has_words.

Requirements

jQuery version 1.2.1 or higher and a slightly modified jquery.tablesorter.js version 2.0.3.

The modification to the original tablesorter plugin is the addition of a few lines which will cache all of the rows so they can be searched. You can download the modified jquery.tablesorter.js code below.

Download

jquery.tablesorter.filter.js

jquery.tablesorter.js 2.0.3 (modified for tablesorterFilter)

Updated blog post on 1-April-2009 to reflect two additional configuration parameters.

Tags: ,

Friday, August 22nd, 2008 Programming 94 Comments

Inline jQuery datePicker and Rails

This post describes how to use the jQuery datePicker plugin (found here) to display an inline calendar and post the selected date back to your Rails app. I’m simply going to put a hidden text_field on the page and use a bit of javascript to populate the field with the selected date.

Grab the datePicker plugin from http://plugins.jquery.com/project/datepicker, and include it in your Rails app.

Get an inline datePicker set up by following the author’s instructions at http://www.kelvinluck.com/assets/jquery/datePicker/v2/demo/inlineDatePicker.html. Quite simply, you only need to do two things.

One: Add this to your .html.erb page:

<div id="date-picker"></div>

Two: To the same .html.erb page, add this bit of jQuery to turn the the div into an inline datePicker:

<script type="text/javascript">
$('#date-picker').datePicker({inline:true});
</script>

At this point you should see the datePicker on your page. It should look something like:
Example of datePicker

Now, let’s put a text_field in your form to keep the date. Here’s what I’ve got:

<% form_for(@event) do |f| %>
<fieldset>
<label for="occurred_on">Date event occurred:</label>
<%= f.text_field 'occurred_on' %>

<%= f.submit "Save" %>
</fieldset>
<% end %>

We need to catch the user’s selected date and populate our text_field. To do this, we need to modify the bit of jQuery we used to set up the inline datePicker. We need to bind the action of selecting a date to a javascript function which will populate the text_field. Return to your .html.erb file and replace the jQuery with:

<script type="text/javascript">
$('#date-picker').datePicker({inline:true})
.bind(
'dateSelected',
function(e, selectedDate, $td)
{
$('#event_occurred_on').val(selectedDate.asString());
}
);
</script>

Finally, since you probably don’t want your users to see the text_field, just change it to a hidden_field.

Tags: ,

Thursday, June 26th, 2008 Programming 11 Comments

Justin has been obsessing over writing simple Web software using Ruby on Rails since 2007. He's also an entrepreneur and Lean Startup expert. Learn more

View Justin Britten's profile on LinkedIn

Subscribe to Justin Britten's blog Follow Justin Britten on Twitter Network with Justin Britten on LinkedIn
 
Prefinery: Simple, online beta management software'

Launch a private beta for your Web application in minutes. Prefinery takes care of collecting e-mail addresses, generating invitation codes, and sending invitations for your private beta. Your customers never leave your site, and e-mail invitations are sent from your address.

Justin is Founder and CEO of Prefinery.