Monday, October 10, 2011

Rails 3.1 Simple Custom Authentication and CanCan Authorization

After adding a simple blog to my Rails 3.1 app, I needed to add security to lock it all down. I'm writing this app for someone else, so a degree of user-friendliness was desired.

My 'requirements' were login with admin account. Anyone can read posts, read comments, and add comments. Admins can add, update, and delete posts. Admins can also remove comments. For usability, the links for these actions should be hidden unless the user is an admin. Of course the urls and actions should be locked down as well.

I decided to write the authentication part from scratch following railscasts Authentication in Rails 3.1 and Authentication from Scratch. For my purposes, I just need one admin account. I don't need to sign up users or any other ui for managing users. I figure, I can create the admin account from the command line, and that'd be about it. So, I skipped the parts from railscasts above that created views for User. I did create a login page of course (no logout).

Here are the authentication commands and code:

User
rails g model user email:string password_digest:string

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email
      t.string :password_digest

      t.timestamps
    end
  end

end

class User < ActiveRecord::Base
  has_secure_password
end
Sessions
rails g controller sessions new

class SessionsController < ApplicationController
  def new
  end
 
  def create
    user = User.find_by_email(params[:email])
    if user && user.authenticate(params[:password])
      session[:user_id] = user.id
      redirect_to posts_path
    else
      flash.now.alert = "Invalid username or password"
      render "new"
    end
  end

end
views/sessions/new.html.erb

<h1>Login</h1>
<%= form_tag sessions_path do %>
<div class="field">
  <%= label_tag "Username" %>
  <%= text_field_tag :email, params[:email] %>
</div>
<div class="field">
  <%= label_tag :password %>
  <%= password_field_tag :password %>
</div>
<div class="actions">
  <%= submit_tag "Login" %>
</div>
<% end %>
BlankRailsMysql::Application.routes.draw do
  resources :sessions
  resources :posts do
    resources :comments
  end

  get "login" => "sessions#new", :as => "login"
end
At this point, a user (if one existed) could go to localhost:3000/login and it would authenticate them and save their user id to the session. By the way, here's the command to add a user:
rails c
User.create(:email => "admin", :password => "password", :password_confirmation => "password")

If you need to run this in your heroku environment, just do:
heroku run rails c --app your_app

Sunday, October 9, 2011

Rails 3.1 Asset Pipeline on Heroku

After updating my Rails 3.0.4 app to 3.1 on Heroku, I wanted to use the built in SASS functionality.  What I didn't realize was that you have to also use the asset pipeline to get the built in support.  I had skipped this in my previous step.  Using the same railscast, I converted my app to use the asset pipeline.  This did not work on heroku.

Long story short, you need to use the cedar stack on heroku for the asset pipeline to work well.  Here are the commands I saved for creating new apps on heroku in the future:

heroku create app_name --addons custom_domains -r git_remote -s cedar
heroku run rake db:migrate --app app_name
heroku domains:add www.your_domain.com --app app_name
change dns entry to point to app_name.heroku.com

Heroku References:
http://devcenter.heroku.com/articles/custom-domains
http://devcenter.heroku.com/articles/rails31_heroku_cedar