NEWS: Cod free phentermine shipping Dacarbazine: Cheapest viagra online Crestor: Beconase Tetanus. Viagra prescription uk Sertraline Xanax detoxification Protonix Amoxapine Cyclizine: Phentermine mastercard Chlorpheniramine? Valium vs xanax Propofol Ambien eminem Phytonadione? Zoloft What do xanax look like! Protriptyline Buy generic phentermine! Watson soma Phentermine next day Viagra lowest price Thiopropazate Propofol Phentermine 37 5mg shipped to kentucky Ordering phentermine Pyridium Colchicine Aldactone Drug test tramadol hydochloride Cope? Ipratropium Bepridil Cialis in the uk Prozac and phentermine? Chlorambucil Plavix Cheap phentermine no shipping Keyword prescription qoclick tramadol without Can xanax cause frontal lobe dementia Nexium Riboflavin Cheap phentermine no rx. Cheapest phentermine 90 day order Premarin? Propylthiouracil Tramadol narcotic Discounted phentermine with no prescription Alesse! Cortisol Buy cialis in uk Xanax long term effects Colon cleanse ambien? Prazosin Viagra lowest prices Diethylstilbestrol Free shipping cheap phentermine Rated online pharmacies for phentermine Cheep paris france phentermine: Viagra discussion Tramadol medication, Phentermine prescription Strattera! Online pharmacy duromine viagra international How long does viagra last Phentermine prices Order phentermine online uk Does phentermine really work Cozaar Generic viagra overnight delivery Pantoprazole! Xanax on line Lorazepam Cheap phentermine no rx How does phentermine work: Cialis lowest price Griseofulvin? How fast will phentermine work Fda approved phentermine Oxacillin Generic sales viagra! Oxycontin xanax bars perclesept and lortab wha Cialis discount online Milrinone Butalbital fioricet, Levaquin Phentermine & health risks Phentermine hoodia Cleocin! Levitra vs cialis vs viagra Picture of xanax pill Diet hcl phentermine pill Differin Piperidolate Trovafloxacin. Prescription weight loss medication phentermine Pfizer viagra: Minocin Carbidopa Tramadol withdrawal Viagra online sales Hydrocodone info Next day delivery on phentermine. Aricept Cheapest viagra price. Prevacid Phentermine delivered overnight Adipex Snorting xanax Discount fioricet Exelon Glimepiride Acyclovir! Clarinex Phentermine 37.5 cash on delivery! Tessalon Provigil. Isoxsuprine Hydrocodone online pharmacy Concerta Pink oval pill 17 xanax identification Phentermine uk Cialis comparison levitra, Glycopyrrolate Free viagra order online! Buy phentermine on line Xanax drug test Tramadol cheap overnight inexpensive Prescription free viagra! Addiction recovery xanax Is klonopin stronger than xanax Tramadol 50mg Tolbutamide. Vicodin health Order soma online Tramadol 377 Nasonex! Viagra price compare Nitrofurantoin. Toprol Tyropanoate Buy meridia Pravachol Hyzaar Disulfiram Fda us approved phentermine Alternative herbal viagra, Buy viagra pill online Isosorbide Free sample prescription for viagra Hydrocodone apap 5 500 Triprolidine Comparison viagra cialis levivia Does phentermine help weight loss Actos: Viagra price list Compare viagra prices Cefaclor Xanax pills. Noroxin Adderall Cheap viagra in uk Amlodipine, Natural alternatives to viagra Hydrocodone for ibs: Hyzaar Information phentermine shortage Codeine How to discontinue the use of phentermine. Phentermine and urine drug screen Xanax anxiety No prescription phentermine Paroxetine. Estrone Azithromycin Lawsuits involving blindness caused by viagra Phentermine 37.5mg Dulcolax Do companies sell phentermine with low dose ingredients: Bontril phentermine adipex Fluticasone Cefuroxime Next day phentermine Cheap phentermine free consult Simethicone Delavirdine Half life of xanax Xanax vs klonopin Best price phentermine Phentermine buy best Buy discount phentermine? Cinoxacin Phentermine overnight No prior perscription tramadol Hydrocodone effects Does phentermine interact with hydrocodone Effexor Phentermine drug Herbal viagra for woman Meridia coupon Cialis levivia sales viagra, Phentermine tablet Buy xanax cod Woman take viagra Aminopterin Hydromorphone Xanax next day delivery Glucophage Per day buy phentermine, Stopping xanax Chlorhexidine Buy viagra canada Information phentermine Etoposide Buy viagra prescription online 180 tramadol Triamterene Lisinopril drug interaction viagra Cialis online sales Ticarcillin Allopurinol Cheapest tramadol online Carvedilol How to inject xanax pills Droperidol Hydrocodone vicodin Phentermine shipped to tn Reliable same or next day phentermine purchase online Natural phentermine Xanax information Buy viagra online cheap Discount phentermine no prescription Monopril. 37 effects phentermine side Fast delivery phentermine Diethylpropion Buy hydrocodone Phentermine order Cilexetil: Diazoxide Tobramycin Generic viagra in canada Donepezil Chlorhexidine Bar gold xanax Phentermine lowest price Weight loss phentermine! Buspirone Epivir. Xanax and valium Cheap phentermine no prescription? Dangers of phentermine Anxiety disorder xanax xr to wean off effexor xr Generic fioricet Overdose xanax. Viagra women Phentermine interactions Buy phentermine without prescription Phentermine use Hytrin Cialis comparison levivia viagra Cialis levitra viagra vs vs Vicodin prescription Viagra samples Xanax federal express Fosinopril Itraconazole Buy tramadol cheap Liothyronine Information phentermine Tramadol cause kidney problems Get viagra online Viagra and ischemic optic neuropathy Triamcinolone How long does xanax stay in your system, Side effects of viagra Cyclandelate Weight loss oral hcg and phentermine Buying viagra online uk, Cycrimine Cheap viagra canada Loxapine Buy cheap domain online atspace com xanax Iodamide Linkdomain buy online viagra info domain buy onlin Dichloralphenazone Thyroid. Paxil and xanax interaction How to get xanax Indomethacin Cialis impotence drug eli lilly co Phentermine side effects danger Addiction tramadol Natural over counter just like viagra stores Prozac drug interaction with xanax Generic viagra canada Cheap phentermine canada Chep phentermine Diatrizoate Tramadol ingredients Cheapest prescription viagra. Phentermine and carbs Carteolol? Buy no phentermine prescription Language phentermine ru Phentermine works Oxycontin xanax bars perclesept and lortab Dothiepin Lanoxin Does xanax show up on drug tests Xanax high Elavil Phentermine ingredients Iothalamate Methoxsalen Best generic viagra Androgel Is phentermine dangerous Exelon: Online pharmacy tramadol Add link phentermine purchase suggest Free trial viagra Viagra sample pack. Elidel Buy online viagra viagra Hydrochlorothiazide Carisoprodol Dexamethasone Actonel Troleandomycin Ordering xanax online Phentermine 37.5 tablet Ambien overnight. Lowest prices viagra Phentermine np with hoodia! Cheapest phentermine diet pills Order phentermine cod online Buy cialis in canada Digoxin Ceftizoxime Online ordering viagra Adipex phentermine vs Cheap phentermine with online consultation Online viagra Phentermine weight loss pills Phentermine reviews Enalapril! Generic viagra cialis Clarinex. Buying vicodin online Buying viagra online Chlorotrianisene Augmentin? Drug screening phentermine Fexofenadine Mycostatin Phentermine result Valium and xanax Xanax online cheap Cheap soma Cialis viagra Does viagra work for women Actos Detection drug in phentermine screen urine Herbal phentermine review Cheapest place to buy phentermine online Phentermine eprescriptions Itraconazole Hydrocodone cod only, Phentermine Viagra compared to levivia? Colistimethate Ultram Trifluoperazine Diet no phentermine pill prescription Fioricet Phentermine for weight loss! Dextrothyroxine Generic money order viagra Viagra herbal alternative Phentermine free shipping 90 supply no delay Free shipping phentermine Buy viagra without prescription Free overnight phentermine shipping Phentermine cod delivery, Famvir Phentermine guaranteed overnight shipping Cheapest place to buy phentermine Purchase tramadol online Butalbital fioricet Cialis effective soft tab treatment Order viagra buying viagra uk Lipids Phentermine florida Cialis compare levivia viagra: Viagra alternatives uk Levoxyl? Belladonna Buy viagra on line Phentermine lowest price 37.5 online Beconase Xanax picture Phentermine 37 5mg and mastercard, Phentermine 37.5mg tablet Soma cube? Female use viagra Buy discount viagra online, Cialis vs viagra Phentermine forum Viagra cialis comparison Losec: Methocarbamol Viagra herbal? Xanax overnight Xanax 1mg Free viagra online 50 hcl mg tramadol Phentermine canada Good morning viagra commercial Viagra dosages Difference between cialis and viagra Ditropan Buy viagra in canada Xanax dose Apcalis cialis. Oxybutynin Buy viagra online get prescription Good morning viagra commercial Viagra testimonials? Quinidine Soma bike? 25 mg viagra Hydrocodone order Xanax info Phentermine mastercard Generic name online qoclick tramadol Herbal viagra alternative review! Dangers of viagra Dioxyline Viagra for woman study Viagra shelf life? Order phentermine cod Celebrex Bromocriptine Cod phentermine shipped Tramadol side effects Esomeprazole Cod tramadol money orders Phentermine on sale Ipodate Xanax withdrawl symptoms Viagra cialis generic Lowest phentermine Cialis generic viagra Mesoridazine Fluphenazine Cialis compared to viagra! Hydrocodone overdose Perindopril Generic soft tab cialis Cheap hydrocodone Amiloride Midodrine. No prescription needed phentermine Mastercard phentermine Online cialis Purchase xanax online Detox hgh phentermine quit smoking xenical Mifepristone Dangers of phentermine heart Overnight shipping viagra: Locoid Injecting xanax. Tramadol narcotic Dicumarol Macrodantin Estrogen Xanax 2 mg Phentermine side effects dangers! Paris cheep phentermine No overnight prescription xanax Ritalin Reviparin Price viagra viacreme Effects of viagra On line prescription viagra Ethinyl 2 mg xanax Meridia vs. Phentermine. Viagra lowest prices Ship free viagra sample Risperdal Viagra erection Online pharmacies phentermine xenical meridia Viagra price compare Xanax grapefruit juice Nizoral. Rabeprazole Discount phentermine price Colace Klonopin versus xanax Cefadroxil On line viagra Discount phentermine online Buy viagra woman: Ordering 30mg phentermine Viagra in woman! Esmolol Alprazolam xanax Effects of viagra Is phentermine discontinued Phentermine without rx Cialis softtabs! Phentermine delivered cod Lynestrenol Natural viagra alternative Plicamycin Protirelin Biaxin. Phentermine feedback Nifedipine Buy online viagra viagra Online pharmacy prescription viagra Buying xanax Opium. Cialis compared to viagra What happens when women take viagra Purchase phentermine Phentermine pill. Sofia viagra Viagra cialis levivia Beclomethasone Celexa phentermine Diltiazem Ampicilin Diet drug fenfluramine phentermine Blindness viagra? Is xanax addictive Mixing cocaine and viagra? Moricizine Hydrocortisone? Vicodin drug test Cialis price, Ambien Purchase viagra online. Pulmonary hypertension and viagra Viagra sale online Lopressor Home made viagra Uk cheapest viagra Ambien cr dosage, Ionamin phentermine yellow Differin Ambien overdose Cheap tramadol online Griseofulvin Phentermine and carbs How long does phentermine stay in your system Viagra doseage. Metharbital Cialis dose Biperiden On line prescription viagra: Molindone Mixing viagra and cialis Adipex diet phentermine pill prescription Motrin Viagra cream Effect viagra Cheapest phentermine 90 day orders Amoxicillin Levivia and viagra Ranitidine Podofilox Diet ky phentermine pill ship that Xanax look alike Nabumetone Cialis generic Herbal phentermine does it work. Buy generic xanax Bricanyl Viagra in woman Phentermine pictures Fioricet information 100 phentermine Xanax liver damage Xanax versus prozac? Adipex ionamin phentermine Cycloserine Generic ambien Buy cheap generic viagra. Xanax prescription online Echothiophate Phentermine 37.5mg Clonidine Best price for generic viagra Buy cheap tramadol online Dicloxacillin Xanax online pharmacy no prescription Blue xanax Canada viagra! Dolasetron Prescription viagra? Medrol Dichlorphenamide Pink oval pill 17 xanax identification Buy tramadol without prescription Phentermine ups delivery Cialis com Phentermine withdrawal Ambien withdrawal Diclofenac Tolazamide, Cinnarizine Phentermine diet plan Procyclidine 37.5 phentermine! Phentermine for less Eon phentermine Miconazole Hydrocodone addiction! Cialis levitra sales viagra Purchase cialis: Carbimazole No online prescription xanax! Cheapest xanax online Cialis vs levitra Genaric viagra Aldara Phentermine np Carbamazepine, Buy phentermine cheap Phentermine and sibutramine be combined Soma seed Viagra substitutes Female spray viagra Liquid cialis: Allegra Levitra: What does xanax do Lansoprazole Lanoxin Furazolidone Diovan Phentermine phendimetrazine Prozac and xanax induced mood disorder Procyclidine. Celexa Viagrafix corporation: Cialis sales uk Lysodren Lowest price on phentermine Xanax grapefruit: Phentermine result Phentermine tablet

Rails Project Story: 2. User Authentication

Here comes the second part of our story, last time in part one we met rails directory structure, designed a three tables database, created it in MySQL and linked it to our application then we let rails db schema create those tables for us and finally we generated a modest scaffold for our Books table.

If you remember last week we added another table in our db schema file which was the Users table, today we will talk about users in our application and how to make our application a more social one.

The options we have in order to have a user management setup for our applications are mainly three:

Before we choose between these three let’s see what are the main differences between them:

If we write our code from zero we will definitely learn more, we will have a better understanding of the whole process but at the same time we will be spending too much time on something became very regular and common in every web application and in some way we will be doing some not required extra work.

Acts As Authenticated and Salted Hash Login Generator are almost the same from some functionality point of view but the main difference is that the act is more dynamic and customizable because it is simpler, it let us take care about few things that we should really be responsible of while the generator take care about everything in some more advance way, so personally I prefer to go for Acts As Authenticated for its simplicity and core functionality that will help us understand how things work and allowing us to add some extra functionalities by ourselves.

Acts in rails are some sort of plugins and extensions to our framework extending it with few extra features, there are many great acts we will use in this application, some of them are completely independent from rails and some like Acts As Taggable became so common to the level they got packaged with rails itself.

Acts As Authenticated

It’s a simple system that helps us deal with users in our applications, it generate the required code to support user authentication and management starting from the regular MVC code ending with migration and tests.

Installation

To start with we have first to install the plugin on our system, to do this we have first to update our repositories index and then install the plugin.

D:\\Projects\\Book Swap\\bookswap>ruby script/plugin source http://svn.techno-weenie.net/projects/plugins
Added 1 repositories.

D:\\Projects\\Book Swap\\bookswap>ruby script/plugin install acts_as_authenticated
+ ./acts_as_authenticated/CHANGELOG
+ ./acts_as_authenticated/README
.
.
.
Consult the Acts As Authenticated wiki for more: http://technoweenie.stikipad.com/plugins/show/Acts+as+Authenticated

The Generator

After installing the plugin now we are able to generate all the required files, acts_as_authenticated have mainly two generators, one for the authentication system itself, the core functionalities and their corresponding model, views and controller. And the other generator is the mailer, the one responsible for sending notification and verification emails to our registered users.

To use:

  ./script/generate authenticated user account

This generates a basic user model, a controller, some basic views, and tests.

So to generate the authentication system we have first to decide the controller name to be generated and remember here that the controller is the one that normally appear in our URL (to deal with the books controller we had to call http://localhost:3000/books) so what will be the best controller name for our users? Account? Readers? Owners? Well personally I prefer to stick with the defaults and the classics, at the end these are our applications Users, and this is what we called the database table earlier so let’s stick to it and name it “users”:

D:\\Projects\\Book Swap\\bookswap >ruby script/generate authenticated user users
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/users
      exists  test/functional/
      exists  test/unit/
      create  app/models/user.rb
      create  app/controllers/users_controller.rb
      create  lib/authenticated_system.rb
      create  lib/authenticated_test_helper.rb
      create  test/functional/users_controller_test.rb
      create  app/helpers/users_helper.rb
      create  test/unit/user_test.rb
      create  test/fixtures/users.yml
      create  app/views/users/index.rhtml
      create  app/views/users/login.rhtml
      create  app/views/users/signup.rhtml
      create  db/migrate
      create  db/migrate/001_create_users.rb

If we examine the generated files we can see that acts_as_authenticated has generated for us the user model, users controller and its corresponding views, a helper, some unit and fixture tests and finally a migration file, as we have all these files generated it seems that BookSwap is ready now to have users, let’s see if this is true http://localhost:3000/users

users_signup.PNG

It seems that it works, it already directed us to the signup method http://localhost:3000/users/signup, ok let’s give it a try and see what will happen (click to enlarge):

User Signup Error

Oops, we have a problem, but at least we have some information that we can work with to understand what we have done wrong.

Fixing The Conflict

With the user friendly error page we should be able to catch what we’ve done wrong, it’s saying: “undefined local variable or method `crypted_password’ for #<User:0×39499b8>”, so basically the problem is in “crypted_password” and it is in the model User, we remember that the Model is the one that deals with the database, if you remember from our first part in the db schema definition we named the password column “salted_password”:

# This file is autogenerated. Instead of editing this file, please use the
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
 
ActiveRecord::Schema.define() do
 
  create_table "users" do |t|
    t.column "fullname", :string, :limit => 80
    t.column "login", :string, :limit => 80
    t.column "email", :string, :limit => 60
    t.column "salted_password", :string, :limit => 40
  end
 
  create_table "books" do |t|
    t.column "title", :string
    t.column "author", :string
    t.column "publisher", :string
    t.column "isbn", :string
  end
 
  create_table "users_books" do |t|
    t.column "user_id", :integer, :default => 0
    t.column "book_id", :integer, :default => 0
  end
 
end

So from where did this “crypted_password” come from?

Back to Migrations

Maybe it’s time to go back and check what’s inside the migration file that acts_as_authenticated generated for us and and check if it has anything realted:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table "users", :force => true do |t|
      t.column :login,                     :string
      t.column :email,                     :string
      t.column :crypted_password,          :string, :limit => 40
      t.column :salt,                      :string, :limit => 40
      t.column :created_at,                :datetime
      t.column :updated_at,                :datetime
      t.column :remember_token,            :string
      t.column :remember_token_expires_at, :datetime
    end
  end
 
  def self.down
    drop_table "users"
  end
end

First let’s understand the migration process more, the migration is made to allow us move up and down in our application with a decent control over our database modification, in this case we can understand that acts_as_authenticated did create the migration file for us so we migrate to it.

Any migration file is separated into two parts, up and down. From the names “up” is the actions which will take place when we move forward in our migration like migrating from 4 to 5 for example, and “down” will hold the actions that will be executed when we move backward in our migrations like migrating from 6 to 5 for example.

The 001_create_users.rb migration file creates a table in its up method and drop it in its down method, the table it creates is the Users table which we already created earlier in our schema, it almost have all the columns, few more and some with different names, here we can find the mysterious “crypted_password” we saw in the error page, it’s the column we named as “salted_password”, so we found what’s wrong, now how to fix this conflict?

We have to write our own migration instead of using the default migration generated by acts_as_authenticated, we will tweak it to suit our needs. First we don’t need to create the Users table as it is already created, we don’t need to recreate the fields we have already created as well but we need the new fields required by acts_as_authenticated and sure we have to fix and rename the field salted_password into crypted_password. This will make “up” method like this:

  def self.up
    #renaming the previously created column in schema.rb
    rename_column :users, :salted_password, :crypted_password
 
    #adding the new columns required by acts_as_authenticated.
    add_column :users, :salt,                      :string, :limit => 40
    add_column :users, :created_at,                :datetime
    add_column :users, :updated_at,                :datetime
    add_column :users, :remember_token,            :string
    add_column :users, :remember_token_expires_at, :datetime
  end

Instead of the create_table method we used some individual methods that deals with an existing table, add_column, remove_column and rename_column are simple methods that takes the table name as their first parameter and the column name as their second, only the rename_column method takes an extra parameter which is the new column name.

As this is the first migration file we have it got the 001 numbers at the beginning of its name automatically, from now on any new migration will get a serial prefix to its name that indicate its place among other migrations.

Whenever we do any changes or modifications to our database we have to support going back by defining the opposite actions in the “down” method of our migration as follows:

  def self.down
    #renaming the column back.
    rename_column :users, :crypted_password, :salted_password
 
    #removing all the created columns in this migrations.
    remove_column :users, :salt,                      :string, :limin => 40
    remove_column :users, :created_at,                :datetime
    remove_column :users, :updated_at,                :datetime
    remove_column :users, :remember_token,            :string
    remove_column :users, :remember_token_expires_at, :datetime
  end

So at the end our modified 001_create_users.rb file will end up like this:

class CreateUsers < ActiveRecord::Migration
  def self.up
    #renaming the previously created column in schema.rb
    rename_column :users, :salted_password, :crypted_password
 
    #adding the new columns required by acts_as_authenticated.
    add_column :users, :salt,                      :string, :limit => 40
    add_column :users, :created_at,                :datetime
    add_column :users, :updated_at,                :datetime
    add_column :users, :remember_token,            :string
    add_column :users, :remember_token_expires_at, :datetime
  end
 
  def self.down
    #renaming the column back.
    rename_column :users, :crypted_password, :salted_password
 
    #removing all the created columns in this migrations.
    remove_column :users, :salt
    remove_column :users, :created_at
    remove_column :users, :updated_at
    remove_column :users, :remember_token
    remove_column :users, :remember_token_expires_at
  end
end

Logically we have solved the conflict error but there is still one step left which is migrating our application to the latest migration available and for now it is 001, to migrate our application we have to call the command “rake migrate”, if we called it without any parameter it will automatically migrate our database to the latest migration available, if we specified the “VERSION=X parameter it will migrate the application to migration X whether it is newer or older from our current migration level.

D:\\Projects\\Book Swap\\bookswap>rake migrate
(in G:/Projects/Book Swap/bookswap)
== CreateUsers: migrating =====================================================
-- rename_column(:users, :salted_password, :crypted_password)
   -> 0.3590s
-- add_column(:users, :salt, :string, {:limit=>40})
   -> 0.3280s
-- add_column(:users, :created_at, :datetime)
   -> 0.3600s
-- add_column(:users, :updated_at, :datetime)
   -> 0.3430s
-- add_column(:users, :remember_token, :string)
   -> 0.3440s
-- add_column(:users, :remember_token_expires_at, :datetime)
   -> 0.3600s
== CreateUsers: migrated (2.0940s) ============================================

Back to our application checking if the user system is working now, let’s try to sign up again and see what we will get (http://localhost:3000/users/signup):

users_after_successful_login.PNG

“Train delayed? and what’s to say?” nice poetry after such a cool process, so we are signed up and logged in now, this is what acts_as_authenticated can give us so far and the rest is on us.

Have you noticed that the column fullname didn’t show up in the signup page? That’s because the templates generated by acts_as_authenticated plugin don’t know about it YET.

Tailoring

To be able to tailor and customize the authenticated system we have to understand the generated files and know how to deal with them and we will start with the views:

The generated templates are three, login, signup and index, they are all placed under the “users” subdirectory in our app/views directory, what we want is to allow users to add their fullnames when they signup so let’s have a quick look at the signup.rhtml template:

<%= error_messages_for :user %>
<% form_for :user do |f| -%>
<p><label for="login">Login</label><br/>
<%= f.text_field :login %></p>
 
<p><label for="email">Email</label><br/>
<%= f.text_field :email %></p>
 
<p><label for="password">Password</label><br/>
<%= f.password_field :password %></p>
 
<p><label for="password_confirmation">Confirm Password</label><br/>
<%= f.password_field :password_confirmation %></p>
 
<p><%= submit_tag 'Sign up' %></p>
<% end -%>

It looks that we have a pattern for every property of the user object to generate its corresponding form input, if we followed the same pattern to generate the required input for the user fullname our template will look like this:

<%= error_messages_for :user %>
<% form_for :user do |f| -%>
<p><label for="login">Login</label><br/>
<%= f.text_field :login %></p>
 
<p><label for="email">Email</label><br/>
<%= f.text_field :email %></p>
 
<p><label for="fullname">Full Name</label><br/>
<%= f.text_field :fullname %></p>
 
<p><label for="password">Password</label><br/>
<%= f.password_field :password %></p>
 
<p><label for="password_confirmation">Confirm Password</label><br/>
<%= f.password_field :password_confirmation %></p>
 
<p><%= submit_tag 'Sign up' %></p>
<% end -%>

This template like any other rhtml view template contains two types of code, the basic xhtml/html code and an embedded ruby code which we call ERb. To embed a ruby code in our views we have to surround it with <% and %> this will execute the ruby code but it will not show any output so it’s called “evaluation ERb block”, if we want to show the output of this code we have to add an equal sign after the introductory signs so it will be surrounded by <%= and %> and this is called “output ERb block”.

In any of the ERb block types we will deal mainly with two things, first is the ActionController methods and actions, these will help us generate the required xhtml/html, js and xml code in a dynamic smooth way.

The second thing we will deal with in our views is the instance variable received from the related action in the related controller, each view will be parsed after its corresponding action in its related controller is requested, in that particular action we set some instance variables that we can manipulate in our views, we will see that in more details soon.

How is it working?

In order to understand the code and the way it is generated in signup.rhtml let’s have a look at the parsed version of it, the html source code of the page we get in our browser when we call http://localhost:3000/users/signup:

<form action="/users/signup" method="post"><p><label for="login">Login</label><br/>
<input id="user_login" name="user[login]" size="30" type="text" /></p>
 
<p><label for="email">Email</label><br/>
<input id="user_email" name="user[email]" size="30" type="text" /></p>
 
<p><label for="fullname">Full Name</label><br/>
<input id="user_fullname" name="user[fullname]" size="30" type="text" /></p>
 
<p><label for="password">Password</label><br/>
<input id="user_password" name="user[password]" size="30" type="password" /></p>
 
<p><label for="password_confirmation">Confirm Password</label><br/>
 
<input id="user_password_confirmation" name="user[password_confirmation]" size="30" type="password" /></p>
 
<p><input name="commit" type="submit" value="Sign up" /></p>
</form>

Back to our rhtml template we notice that it starts with an output ERb block with the method error_messages_for which will output all the error messages of the user object in this form if there is any, as we don’t have any errors here we didn’t find any related html code in the parsed version.

Then comes an evaluation ERb block that opens a block by its turn, the form_for method do the required preparation to output a form fully customized to hold the information of a specific object and by ready I mean setting this form inputs names and ids to form a set of the object’s data in the post parameters. So what we do is to specify the object name we are preparing this form for, which in our case “user”, we can also specify the action this form will be directed to when it’s submitted, if we don’t specify any (like in our case) it will be submitted to itself so our form will be submitted back to the signup action of the users controller.

The form_for method prepared a form helper object for the “user” object and name it “f”, then the generating of the required inputs for each column started by simply calling their corresponding helper methods, notice that only two methods are used so far: text_field and password_field and they are called as methods of the form helper object called “f”, notice as well that these methods are called in an output ERb block so they can return their output in the parsed template.

Let me try to simplify this code by trying to write it in plain English: “Start a form for a new user then generate the required text fields and password fields for this user and then post them back to the same current page.”

The main idea of setting the names and ids of the generated inputs is to simplify the way we deal with them in our actions as post parameters, rails has a standard for this and therefore it has the ready tools to generate what’s required to follow these standards and get things faster.

After we are done with our form fields we find a submit_tag named “Sign up” then the form_for block is simply closed by an “end” and we are done. When someone signup and fill all these fields and hit the sign up button the form data will be posted to the same action “signup” where it will be handled to add a new user to the Users table accordingly, if it faced any errors it will return to the same form and show the errors at the top (as the results of the error_messages_for method), if not it will simply login to our poetry page.

Are We Logged In?

Now let’s change this literal poetry in the index.rhtml template to some code poetry, how about we change the output of this view depending on the status of the user, if he/she is logged in we show a link to logout, if not we show links to either signup or to login. I don’t think this is difficult it’s just a quick if statement:

<% if logged_in? %>
You are logged in, you can now <%= link_to "log out", :action => 'logout' %>
<% else %>
You are not logged in please either <%= link_to "login", :action => 'login' %> or <%= link_to "signup", :action => 'signup' %>
<% end %>

I guess the code is self-explanatory now, I just want to note that we used the method logged_in?, in ruby whenever there is a method with a question mark “?” at the end of it will return a Boolean, True or False, so in English it’s like a simple question, is the current user logged in? this method is a part of the acts_as_authenticated plugin.

The other thing I wanted to talk about here is the link_to method, it is an ActionView helper method, it generates an anchor, takes the first parameter as the anchor name and the second as the action name we want to link to, it will automatically understand and generate the corresponding URL of the action we specify, for example when we specify the “logout” action it will know automatically that its URL is http://localhost:3000/users/logout.

So we have changed our index.rhtml file, now let’s see if it’s working and how it will look like, remember that our last state where logged in after we signed up and saw the lovely poetry:

Logged in users index page

We are logged in, that’s true, it seems that everything is really working fine and we don’t have any problems so far, how about the link to “logout”? is it working? let’s check out:

Logged out users index page

Yes it is working, we are logged out now and we have one of two options, either to login again or to signup for a new account, try logging in and out again to see how it’s working and then let’s try a new signup to see if the fullname field we have added is present or not:

New users signup page

Wonderful, we have a simple user authentication system now that we modified a little, so far it works for signup, login and logout, it doesn’t do anything more yet but at least it helped us understand few concepts and how things work at the backend.

More Security by Filtering

To care more about security and to get the most benefit out of the latest rails 1.1.6 security release let’s use the new method of ActionController (released in rails 1.1.6) “filter_parameter_logging”:

ActionController#filter_parameter_logging lets you filter form data that you don’t want saved in the log. This is useful for preventing sensitive data like passwords and credit card numbers from being logged in the clear, for keeping huge pieces of data from clogging the log file, and so on.” from Filtered Parameter Logging of the Ruby on Rails official Blog.

This method job –as clarified in rails weblog- is to filter fields from being logged, in our case what we want to filter is the password fields and we will do that by adding the filter_parameter_logging line to our application controller in controllers/application.rb:

# Filters added to this controller will be run for all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
class ApplicationController < ActionController::Base
  filter_parameter_logging :password
end

By doing this the password and password_confirmation fields will never be logged anymore and will be replaced by “[FILTERED]” in the log files, to check that yourself you can check your log/development.log file and see how the passwords of the users added to the system earlier were plainly logged while the passwords of any new users will be replaced with “[FILTERED]“.

Summary

In this part we have implemented the Acts As Authenticated plugin into our application, modified it a little and understood few backend behaviors specifically the way the view templates handle ERb code, we also had a better look at Migrations and understood how they help us have a better control over our database modifications in a very dynamic way.

During the next week I will release a sidenote post on how to checkout the project files from the subversion repository I’m using for it, I’m also working on creating packaged files for each part of the tutorial and will release it on RubyForge soon too.

In the next part we will talk about relationships, as for now we have a user system and a simple books system, we will link them together using some magic relationship mapping from ActiveRecord maybe we will do some testing as well to ensure things are working properly, so… see you next week.

Comments

25

  1. Rida Al Barazi » Blog Archive » Rails Project Story said..

    [...] User Authentication: In this part we have implemented the Acts As Authenticated plugin into our application, modified it a little and understood few backend behaviors specifically the way the view templates handle ERb code, we also had a better look at the migration process and understood how they help us have a better control over our database modifications in a very dynamic way. [...]

  2. Rails Project Story: 2. User Authentication « COLD CASE said..

    [...] read more | digg story [...]

  3. genericpenguin said..

    Nice article. If the acts_as_authenticated site had something similar, it would be a little more friendly. Especially when things are broken.

  4. Kevin said..

    If anyone is using InstantRails, I couldn’t get filter_parameter_logging to work with it. I moved the project to a manual install of ruby/rails/mysql and it worked fine. I submitted a bug to the InstantRails Trac.

  5. Rida said..

    @Kevin: First I’d like to welcome you to ridaalbarazi.com and I hope you will repeat your visits :)

    I don’t know about InstantRails as I personally prefer the Unix style programming with a regular editor (I use Scite) and not any IDE of any type, I’m sure the guys at InstantRails Trac will help you out with this.

  6. jun said..

    nice article… i was looking for the plugin acts_as_version. ng my queries lead me to this site.. somehow cud post a article that uses act_as_versioned plug_in.. thankz….

  7. MatthewVB said..

    Rida,

    I’m struggling with part of your code. I have the script working fine, except when I try to login.

    When I login, it is redirecting me right back to the login screen. Any thoughts?

    Thanks,
    -matthewvb

  8. Rida said..

    @MatthewVB, That’s strange, have you done the logged_in? part? Try restarting WEBrick.. and check your browser’s cookies.

    Please share with us your solution here when you work it out so others can benefit too.

    Cheers

  9. MatthewVB said..

    Thanks for your help Rida. I’ve done the logged_in? Here’s the code I’m using:

    users_controller.rb

    class UsersController 'signup') unless logged_in? || User.count > 0
    end

    def login
    return unless request.post?
    self.current_user = User.authenticate(params[:login], params[:password])
    if current_user
    if params[:remember_me] == “1″
    self.current_user.remember_me
    cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
    end
    redirect_back_or_default(:controller => ‘/users’, :action => ‘index’)
    flash[:notice] = “Logged in successfully”
    end
    end

    def signup
    @user = User.new(params[:user])
    return unless request.post?
    @user.save!
    self.current_user = @user
    redirect_back_or_default(:controller => ‘/users’, :action => ‘index’)
    flash[:notice] = “Thanks for signing up!”
    rescue ActiveRecord::RecordInvalid
    render :action => ’signup’
    end

    def logout
    self.current_user.forget_me if logged_in?
    cookies.delete :auth_token
    reset_session
    flash[:notice] = “You have been logged out.”
    redirect_back_or_default(:controller => ‘/users’, :action => ‘index’)
    end
    end

    user.rb

    require 'digest/sha1'
    class User :password_required?
    validates_presence_of :password_confirmation, :if => :password_required?
    validates_length_of :password, :within => 4..40, :if => :password_required?
    validates_confirmation_of :password, :if => :password_required?
    validates_length_of :login, :within => 3..40
    validates_length_of :email, :within => 3..100
    validates_uniqueness_of :login, :email, :case_sensitive => false
    before_save :encrypt_password

    # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
    def self.authenticate(login, password)
    u = find_by_login(login) # need to get the salt
    u && u.authenticated?(password) ? u : nil
    end

    # Encrypts some data with the salt.
    def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
    end

    # Encrypts the password with the user salt
    def encrypt(password)
    self.class.encrypt(password, salt)
    end

    def authenticated?(password)
    crypted_password == encrypt(password)
    end

    def remember_token?
    remember_token_expires_at && Time.now.utc

    index.rhtml

    You are logged in, you can now 'logout' %>

    You are not logged in please either 'login' %> or 'signup' %>

    login.rhtml

    Login

    Password

    Remember me:

    –>

    signup.rhtml

    Login

    Email

    Full Name

    Password

    Confirm Password

    Thanks for the help!!

  10. MatthewVB said..

    well, it doesn’t look like the code held-up in the posting.

    I zipped it all at:
    usermod.zip

    Thanks again!

  11. john butler said..

    Nice article, simple and easy to follow, wish most others were like this!

  12. renuka said..

    nice article. its really helpful

  13. Wiingnut said..

    is there an open source site that i can use to set an intranet based book swap site?

  14. Rida said..

    Yes it will be available on RubyForge soon at: http://rubyforge.org/projects/bookswap/

  15. yassin said..

    hello every one…
    iam copmputer engeneering student, can i get these users dealing ain sql/oracle language….
    and thanks

  16. John said..

    I’ve been trying to get the authentication piece incorporated into my new site with no luck. I started out using PERL/CGI on Apache and have been trying to move it to Rails as we speak. What I need to do is allow users to sign up, create an account and when they login, pass their username to the Model which will use it in the db query.
    Everything except this last piece is working and I can really use some help. If interested, please email me at john”at sign”fundwatch.net

    thanks

  17. John said..

    I got past the last bunch of errors by upgrading Ruby to the latest 1.1 version. I read somewhere that version 1.0 will not work well with act_as_authenticated, in my case, I was getting and undefined method for form_for.
    At the present moment, I’m sorta working, except for my login page which keeps coming up blank. I have no errors, it’s just blank. I’ll do some more research now.

  18. prash said..

    thanks!
    works like a dream. coming from a java world i cannot describe to you the pleasure of creating a full-fledged and persisted user sign-up/login/logout/encrpted-password in a couple of hours.
    -prash.

  19. Daniel said..

    Hey Rida,
    thanks for your excellent tutorial! One of the best I have ever used. Like prash I come from the java world, but after this tutorial ruby on rails might get my first choice. I hope you will release part 3 of your tutorial soon! Greets Daniel

  20. aymen said..

    Hi Rida i wanna thank you about ur good tuto it was nice from you to help begginer of Ruy On Rails.

  21. morganusvitus said..

    The site looks great ! Thanks for all your help ( past, present and future !)

  22. voip said..

    pagine piuttosto informative, piacevoli =)

  23. yousra said..

    hi
    I wanna know how to save in 2 tables in the same time some informations publised in one formulaire
    exp:
    I will add a user who have as info name, job and country but when I add him , I want that he add name and country in table named users and job in table named jobs.
    of course when he add name and country he will add the id_job that was saved in the jobs table

  24. Saravanan said..

    hi
    can anyone give me the ruby code for changing the password. i am a biggner. and i tried it by using some codes given in some websites. but its not working properly..
    by
    Saravanan

  25. db.networx » Very nice links for Ruby on Rails-JSF, Java Server Face, Ruby, Ruby on Rails, Glassfish ... said..

    [...] Very goog tutorial about user authentication [...]

Leave a Reply

you can use these xhtml tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Articles Permanent Link

is cialis or levitra better suggested dose in cialis what colors do valiums come in ex tramadol cialis drug levitra viagra phentermine with online docter consultation viagra news edinburgh comment search ambien no online pharmacy prescription us real valium phentermine for sale without perscription tramadol plus valium plus somas ultram home gym phentermine geberic viagra 50mg valium child custody drug generic generic viagra dose viagra cialis versus regalis valium while pregnant 37.5 card master phentermine amazing blonde fucked cialis order generic ambien 40mg dose of cialis cialis pharmacy direct viagra keyword viagra partial dose meridia online pharmacy phentermine umaxppc xenica viagra treat childhood pulmonary hypertension cialis and adverse effects information about cialis and livetra adipex online sales phentermine purchasing viagra effexor valium contradictions adipex phentermine treetop generic viagra us licensed online pharmacy viagra interaction with doxazosin da li viagra radi description of tramadol hcl-acetaminophen par overnight phentermine huge discounts online pharmacy viagra cialis levitra manufactures ptnrs searchfor viagra viagra impotence pill cialis usa mail crohns phentermine tramadol tramadol a a target blank mexico ambien viagra lawsuit settlements intel ambien modem drivers cialis side effect 0a aan cheapest shops selling phentermine phentermine diet online pharmacy viagra free sites find search pages pcp in urine valium tramadol next day delivery cheap phentermine withouta rx pfizer japan viagra insta phentermine lowest price for phentermine overnight cialis ingredient cialis get viagra pakistanian phentermine ambien on line fed ex german remedies cialis taking elavil and ambien viagra triangle cleveland ohio ambien and celexa drugs mailorder valium adipex meridia online phentermine prescription viagra cialis dizziness cialis liver problems hiv drugs and interactions with cialis weight loss philadelphia phentermine mixing valium with xanax cbs ambien bush fetches george porn viagra girl in viagra commercial gel tab viagra tramadol longterm use eon labs phentermine without a script tramadol meningitis buy phentermine adipex p 37.5mg phentermine no doctor roche valium no prescrption comparing cialis and levitra wine ambien price of valium on the street ambien shipped cod tramadol dosage for dog cheap price on phentermine viagra on-line tinnitus and ambien cr phentermine pharm in stock former senator who did viagra commercials phentermine tetracycline