Question

I am new to Rails and have been developing an app in rails 3 after following a Lynda.com tutorial where Kevin Skoglund showed us a way to authenticate a user using SHA1 Digest. I used that in my app and there is a need now to put in some Authorization. When I searched around, I found CanCan to be one of the better ones for authorization in rails. However, CanCan seems to be mostly implemented using Devise or Authlogic authentication and not custom authentication.

  1. I wanted to know if it is at all possible to use CanCan if we use custom authentication, like I did. Is so, how to go about getting CanCan to work ?
  2. It looks like CanCan needs some 'create_user' to be present but I am not sure how/where to create it.
  3. Another alternative that I thought would be to put in my custom check on every page to check the user role and redirect them to an error page if they are unauthorized but that seems like a bad way to approach this problem...Your views on this please.
  4. Please let me know if you need any additional information. I am using Ruby 1.9.3 and Rails 3.2.1.

Below is the way I have my current authentication set up. Any help would be greatly appreciated.

access_controller.rb

class AccessController < ApplicationController
  before_filter :confirm_logged_in, :except => [:login, :attempt_login, :logout]

  def attempt_login
    authorized_user = User.authenticate(params[:username], params[:password])
    if authorized_user
      session[:user_id] = authorized_user.id
      flash[:notice] = "You are logged in"
      redirect_to(:controller => 'orders', :action => 'list')
    else
      flash[:notice] = "Invalid Username/password combination"
      redirect_to(:action => 'login')
    end
  end

  def logout
    session[:user_id] = nil
    flash[:notice] = "You have been logged out"
    redirect_to(:action => 'login')
  end
end

user.rb (User Model)

require 'digest/sha1'

    class User < ActiveRecord::Base
      has_one :profile
      has_many :user_roles
      has_many :roles, :through => :user_roles

      attr_accessor :password
      attr_protected :hashed_password, :salt

      def self.authenticate(username="", password="")
        user = User.find_by_username(username)
        if user && user.password_match(password)
          return user
        else
          return false
        end
      end

      def password_match(password="")
        hashed_password == User.hash_with_salt(password, salt)
      end

      validates_length_of :password, :within => 4..25, :on => :create
      before_save :create_hashed_password
      after_save :clear_password

      def self.make_salt(username="")
        Digest::SHA1.hexdigest("Use #{username} with #{Time.now} to make salt")
      end

      def self.hash_with_salt(password="", salt="")
        Digest::SHA1.hexdigest("Put #{salt} on the #{password}" )
      end

      private
      def create_hashed_password
        unless password.blank?
          self.salt = User.make_salt(username) if salt.blank?
          self.hashed_password = User.hash_with_salt(password, salt)
        end
      end

      def clear_password
        self.password = nil
      end
    end

ApplicationController.rb

class ApplicationController < ActionController::Base
  protect_from_forgery

  private

  def confirm_logged_in
    unless session[:user_id]
      flash[:notice] = "Please Log In"
      redirect_to(:controller => 'access', :action => 'login')
      return false
    else
      return true
    end
  end
end
Was it helpful?

Solution

I recommend first reading or watching the Railscast about CanCan. It is produced by the author of this gem and therefore very informative:

http://railscasts.com/episodes/192-authorization-with-cancan

You can also get help on the Github page:

https://github.com/ryanb/cancan

Somehow, you need to fetch the currently logged in user. This is what the current_user method does, and it needs to be defined on the users controller. Try something like this:

class UsersController < ApplicationController
  # your other actions here

  def current_user
    User.find(session[:user_id])
  end
end

Then, you should be able to use CanCan as described in the resources above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top