Building Browsergames – The Registration Page (Ruby on Rails)

Introduction

Today we’re going to tackle the same material as in this posting: The Registration Page (PHP) with some commentary on different aspects of the code and translating all the ideas to how they work in Ruby on Rails.

First off I’m going to assume that you can get Ruby on Rails and some kind of database installed on your computer without my help. There are lots of excellent resources for it: The Ruby on Rails Homepage, FiveRuns Install, BitNami’s RubyStack, etc. The original article skips over this step so I’m going to do so too.

With that out of the way, you need to run the command

> rails pbbg

which will create a directory named pbbg and put a complete project skeleton in place in the directory. This is where we will do all our work on the project:

README     config     lib        script     vendor
Rakefile   db         log        test
app        doc        public     tmp

In particular we’re going to pay attention to stuff in two directories this time around, app (where all the code and pages for our game will go) and config (where all the configuration is handled). To start with, we need a database to start our development work. Rails comes setup for SQLite, you’ll see that if you look at the config/database.yml file. It starts like this:

# SQLite version 3.x
#   gem install sqlite3-ruby (not necessary on OS X Leopard)
development:
  adapter: sqlite3
  database: db/development.sqlite3
  timeout: 5000

more configuration follows in the actual file…

Since I use MySQL I need to change this and create development and test databases I can use (I’ll skip creating the production database until later). To do that Windows users need to run the following:

> gem install mysql

and Mac or Unix users run

> sudo gem install mysql -- --include=/usr/local/lib

If you’re on the Mac and you have problem with that last command check here for what helped me fix my problem. Then I must create databases in MySQL to hold my development and test databases (I used the MySQL Administrator GUI for that) and change the config/database.yml file to read as follows:

# MySQL (default setup).  Versions 4.1 and 5.0 are recommended.
#
# Install the MySQL driver:
#   gem install mysql
# On MacOS X:
#   gem install mysql -- --include=/usr/local/lib
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
development:
  adapter: mysql
  database: pbbg_development
  username: root
  password: 
  host: localhost
 
 
# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
  adapter: mysql
  database: pbbg_test
  username: root
  password: 
  host: localhost
 
production:
  adapter: mysql
  database: pbbg_production
  username: real_username
  password: real_password
  host: localhost

OK, that probably seems like a lot of work, but hey, it’s pretty much the same for any other language. You’ve got to get the language installed, a database server installed, a database or databases created, and everything configured. But, the upside is that you only have to do once.

Getting Started

The original article gets us a registration page that lets new users sign up for the game. So we’ll do the same sort of thing but use Ruby on Rails to do it. Let’s start by creating the user:

> ruby script/generate model User username:string password:string salt:string

Ignore the part that says “salt”, I’ll explain that later. At the moment though, you can see that we’re creating a user and giving the user a username and password. Rails checked to make sure directories existed and then created a bunch of files in response to that command, like so:

    exists  app/models/
    exists  test/unit/
    exists  test/fixtures/
    create  app/models/user.rb
    create  test/unit/user_test.rb
    create  test/fixtures/users.yml
    create  db/migrate
    create  db/migrate/20080713053558_create_users.rb

In particular, let’s look at the file db/migrate/20080713053558_create_users.rb (the date and time in the name of your file will be different than mine):

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :username
      t.string :password
      t.string :salt
 
      t.timestamps
    end
  end
 
  def self.down
    drop_table :users
  end
end

What Rails has generated in this case is Ruby code to actually build the table “USERS” in the database. It is this table in which we will store all the user records for the game, it has also added fields for the username, password, salt, and timestamps (created_at and updated_at). Now by simply running a command the table we need will automatically be created:

> rake db:migrate

If you supplied the right username, password, etc. connection information in the database.yml file earlier, Rails will connect to your database and create the table and fields in that table, regardless of which brand of database you are using! That is, you could setup and use SQLite today but switch to MySQL or Oracle later and this file will not change, only the database connection settings in the database.yml file will need to change. Believe it or not, we can actually try out creating a user right now without having written a line of code yet. Just run:

> ruby script/console

The console is an interactive shell that connects to the database and loads up all the files in your project to let you try out programming code easily. Here’s a transcript of my using the console to test out the user model we created earlier (anything after a >> is something I typed):

Loading development environment (Rails 2.1.0)
>> users = User.find(:all)
=> []
>> user = User.create(:username => "john", :password => "password")
=> #<User id: 1, username: "john", password: "password", salt: nil, 
created_at: "2008-07-13 05:53:08", updated_at: "2008-07-13 05:53:08">
>> users = User.find(:all)
=> [#<User id: 1, username: "john", password: "password", salt: nil, 
created_at: "2008-07-13 05:53:08", updated_at: "2008-07-13 05:53:08">]
>> quit

It might seem a little cryptic at first but all I’m doing is performing three commands here. The first one tries to get all the users in the database (there aren’t any yet), the second creates a new user, the third one again tries to get all the users in the system (this time there’s one user in the list). Notice that we’ve gotten user creation, searching, quite a bit of code in general here for free somehow?!? That’s due to a part of Rails called ActiveRecord. It is what is known as an Object Relational Mapper (ORM) and it automatically handles mapping classes in your program to tables in the relational database you are using. In fact, it is smart enough to write a great deal of our SQL queries for us without our having to type anything out. To see what I mean, let’s look at app/models/user.rb which holds our user model:

class User < ActiveRecord::Base
end

That’s it. That’s the whole file. All of the finding and creating, etc. were handled for the User class by code inherited from ActiveRecord::Base. Here’s an example of some more capabilities we got for free from ActiveRecord (again I’m using the console to try this out):

>> user = User.find_by_username("john")
=> #<User id: 1, username: "john", password: "password", salt: nil, 
created_at: "2008-07-13 05:53:08", updated_at: "2008-07-13 05:53:08">
>> user.username
=> "john"
>> user.password
=> "password"
>> user.salt
=> nil
>> user.password = "porcupine"
=> "porcupine"
>> user.save
=> true
>> user
=> #<User id: 1, username: "john", password: "porcupine", salt: nil, 
created_at: "2008-07-13 05:53:08", updated_at: "2008-07-13 12:32:36">

Finding a record by a specific field, looking up values within the object (often called “getters”), assigning new values to fields (”setters”), and saving an updated record… all free. So let’s continue with the rest of the code to build the registration page now that we’ve got users we can work with. The app/models directory holds all of the code that really makes up our game. It will have classes for users, monsters, items, etc. in it eventually. It will contain the code to handle all the rules of the game, but it won’t contain anything related to creating the HTML the user sees on the page, that goes into the app/views directory. Nor will it contain any of the glue code which connects the views to the models, that goes into the app/controllers directory. What we need now is a controller and a view to handle the actual registration page. We’ll get that by again using one of the built-in code generators in Rails:

> ruby script/generate controller Account index registration

In particular this got us a controller called Account with a built in views called index and registration. To try it out we’ll fire up our Ruby on Rails (RoR) server and take a look at the page. To do that, run the command:

> ruby script/server

It should launch a server (it might be Mongrel, it might be WEBrick, you don’t care which). We can now point our browser at the server and see what it looks like by going to http://localhost:3000:

Default Rails page at localhost:3000

and if we go to the page we just added (i.e. localhost:3000/account/registration):

A newly added view has virtually nothing in it

there is basically nothing there. We need a form and some code to hook everything up. It’s high time we wrote a little code! To do so we’ll edit the account controller in app/controllers/account_controller.rb. Whenever the user requests a page that starts with /account/ it will go through this controller. In this case we also specified registration so it will call the registration method in the AccountController. If we look at the file now we see that there’s nothing there yet:

class AccountController < ApplicationController
  def index
  end
 
  def registration
  end
end

That’s why it sent us automatically to the view in the file apps/views/account/registration.html.erb:

<h1>Account#registration</h1>
<p>Find me in app/views/account/registration.html.erb</p>

Building Our Registration

By filling in new code in this controller, view and model, we’ll get our form and with very little effort on our part. Here’s the new version of registration.html.erb, change yours to match this:

<% form_for :user, @user do |f| %>
  <%= error_messages_for :user %>
 
  Username: <%= f.text_field :username %><br/>
  Password: <%= f.password_field :password %><br/>
  Confirm Password: <%= f.password_field :password_confirmation %><br/>
  <%= submit_tag "Register!"  %>
<% end %>

That’s pretty similar to what we saw in the PHP code but trust me, it’ll do more of the work for us in this case. That’s because we aren’t writing raw HTML, we’re calling Rails functions and it will format everything in such a way that it will handle some things for us later. In addition we need to add the following lines to the user.rb (in app/models) so we can get some validation going on the input:

class User < ActiveRecord::Base
  validates_uniqueness_of :username
  validates_presence_of :password
  validates_confirmation_of :password
end

These three validations will now automatically be tested any time we try to save the user. We don’t have to do it, ActiveRecord does it for us. Two more files to modify and we’re done. The first is app/controllers/account_controller.rb:

class AccountController < ApplicationController
  def index
  end
 
  def registration
    if request.post? and params[:user]
      @user = User.new(params[:user])
 
      if @user.save
        flash[:notice] = "User created."
        redirect_to :action => "index"
      end
    end
  end
end

The last file to update is the app/views/account/index.html.erb. It is where we end up after a successful registration. The redirect_to command in the account_controller.rb file sends us there after we register:

<% if flash[:notice] %>
  <%= h flash[:notice] %>
<% end %>
 
<h1>Welcome To The Game!</h1>

All it does is display any notice generated by the controller method that directed us to this page and then a nice welcome message. At this point we’ve replicated all the functionality in the original article with one notable exception, hashing the password. I consider that to be a little bit advanced so you can skip it now and come back to it later if you like. In the meantime, let’s play with what we just built. For example, let’s try creating a user with the same name as an already existing user, no password, and a confirmation password that is different from the password, notice how Rails automatically catches the errors, immediately redirects back to the page (filling in all the values the user had typed in), and displays the errors to the user:

Total failure of all the validations

And after a successful registration (username: frodo, password: ring, confirm password: ring):

Success!

All About Salt

I promised earlier I would explain what the field named salt was for. It has to do with the MD5 hash of the password that was done in the original (PHP) version of this page. Before the password was saved to the database it was hashed using the MD5 hash algorithm. It produces a pretty horrible looking mangling of anything that keeps us from knowing what the original password was even if you have access to the data. Nobody could gain access to your account because they couldn’t guess what word to put in to match that hash. Whenever a password is submitted for login, it is hashed again and then compared against the saved hash. The only problem, it doesn’t work. It’s the same false sense of security you get from going to the airport these days. Let me show you an example:

a7ec1fbce15b4d4086c6943ef2dc1aa1

Wow! That’s pretty awful, I could never guess what word that was. The problem is, lots of people use words from the dictionary, foreign words, or names as their passwords, in this case I can go to a site with a large hash dictionary called GData and enter in this hash and it will tell me that the password that hashes to that value is the common word “porcupine”. Oops.

So how do we protect against that? Salt! Salt is a random string a few letters and/or numbers long that we stick in front of the password before we hash it. Then we save both the salt value and the hash for future password validation. Whenever we need to check an incoming password again, we stick the salt on the beginning and hash it, then compare the hashes. It is the small random string at the beginning of each password that makes the hash dictionaries useless.

Here’s how to change the code to use salt and hashed passwords. The change has to do with the data saved so it stays entirely within the user model (app/models/user.rb):

require "digest"
 
class User < ActiveRecord::Base
  validates_uniqueness_of :username
  validates_presence_of :password
  validates_confirmation_of :password
 
  # Setup the salt value and hash the password before we save everything to the
  # database.
  def before_save
    if (self.salt == nil)
      self.salt = random_numbers(5)
      self.password = Digest::MD5.hexdigest(self.salt + self.password)
    end
  end
 
  private
 
  # A sequence of random numbers with no skewing of range in any particular
  # direction and leading zeros preserved.
  def random_numbers(len)
    numbers = ("0".."9").to_a
    newrand = ""
    1.upto(len) { |i| newrand << numbers[rand(numbers.size - 1)] }
    return newrand
  end
end

We added the require command to pull in the digest code we need, a function to generate the random salt, and a “before_save” function. ActiveRecord will automatically call that function before the object is saved and since we check to see if we have already got a salt, it will only change the salt and password the very first time the user is saved to the database. Altering something else on the user and then saving won’t keep changing the salt and re-hashing the password.

Note: The method of saving a different random salt with each user is different from the method that appeared later in the Building Browsergames update Securing Our Hashes (PHP). That one used a single secret salt value (not stored in the database) which is used for all users. The two systems are different but both should be adequately secure for any persistent browser based game.

Extra Credit

  1. Add some more advanced validation to the User model. ActiveRecord can make sure the lengths of the username and password are within certain ranges (i.e. no one character passwords or 200 character usernames). It can also make sure that only certain characters are used as well. See this link for more info: http://www.noobkit.com/show/ruby/rails/rails-edge/activerecord-edge/activerecord/validations/classmethods.html
  2. Visit some handy places for Ruby on Rails developers:

Wish there was more?

I'm considering writing an ebook - click here.

.

John Munsch is a professional software developer with over 20 years experience. He created a series of game development sites (XPlus and DevGames.com) on his own before co-founding GameDev.net in 1999. The blog for his PBBG work is located at MadGamesLab.com.

Thursday, August 14th, 2008 buildingbrowsergames, code, rubyonrails
  • itsamirzia

    Thanks a lot for this nice work. Its really appreciable.

  • The Rails version of the tutorials is awesome!

    I've written pretty similar functionality using devise, with the main difference being that I don't add anything to the users table, I have support for multiple characters instead so the stats and attributes are stored independently of the user.

    authlogic is good too but I found devise to be more actively developed and it works great with Rails 3.0.

  • Tom

    use the game authlogic it is easier

  • Marc

    This is an absolutely fantastic resource for gamers with little programming experience that would like to learn more. Really looking forward to the rest of the tutorial. Plus I learned some MySql on top of the Rails. Really a great read.

  • meera

    well This is an absolutely fantastic resource for gamers with little programming experience that would like to learn more....!!!

  • Thanks a lot for translating the same thing for Ruby on rails I was waiting for this. You really describe every step clearly and I didn’t face any problem. Thanks a lot.

  • Thanks a lot for such a nice work. This all work great .. keep it up

blog comments powered by Disqus

About

Building Browsergames is a blog about browsergames(also known as PBBG's). It's geared towards the beginner to intermediate developer who has an interest in building their own browsergame.

Sponsors

Got Something to Say?

Send an e-mail to luke@buildingbrowsergames.com, or get in touch through Twitter at http://twitter.com/bbrowsergames