MerbAuth is Ready

June 17, 2008 | Author: hassox | Publisher: hassox

MerbfulAuthentication was a great testbed. It got plenty of people up and running with a basic user system on their site. It is however, not updatable since it's a code generator. MerbAuth is the shiny new version of MerbfulAuthentication... In slice form!

As soon as I saw slices, I knew MerbfulAuthentcation had to be one. I have been unhappy for a while with merbful_authentication.

  • It’s large and difficult to maintain.
  • Improvements are only available on new projects.
  • It’s difficult for people to contribute enhancements.
  • It’s difficult to spec

At this stage it’s basically the same functionality, with the addition of forgotten password management. Like MerbfulAuthentication, MerbAuth supports both ActiveRecord and DataMapper

Grab It

The first thing to do is grab the source and install the gem.

git clone git://github.com/hassox/merb-auth.git
cd merb-auth
sudo rake install

This will get you the slice gem. Once you’ve got that. You need to tell your application to use it :)

Application Setup

There’s a couple of things you need to do to get going.

In your config/init.rb

dependency "merb-slices" 
dependency "merb-auth" 

Merb::BootLoader.before_app_loads do
  Merb::Slices::config[:merb_auth][:layout] = :application
end

In config/router.rb

Merb::Router.prepare do |r|
  r.add_slice(:MerbAuth, :path => "", :default_routes => false)
end

This will mount the slice at the sites root. So you will have /login /logout /signup.

Now you’ll need to install your slice.

rake slices:merb_auth:install

User Model Setup

We’ve taken care with MerbAuth to make sure that you’re not stuck with a particular user class like “User” or “Person”. Like MerbfulAuthentication you can call your class whatever you like. The user model for MerbAuth is just a mixin, so you can pull it down into your own user type model. Here’s an example user model.

class User
  include DataMapper::Resource
  include MerbAuth::Adapter::DataMapper
  include MerbAuth::Adapter::DataMapper::DefaultModelSetup

end
  • The first include is optional and just includes the datamapper resource for a datamapper model.
  • include MerbAuth::Adapter::DataMapper includes the methods into the User model so that it works as required. (use MerbAuth::Adapter::ActiveRecord for AR)
  • include MerbAuth::Adapter::DataMapper::DefaultModelSetup just sets up the properties and default validations that are needed by the user model.

If setting up the properties automatically is a bit too much magic for you though, you can put them into your model. Instead of including the DefaultModelSetup, there’s a handy rake task to print out the default properties so you can copy them into your application. That way in the future, if we add more default properties, you just run the rake task again, and compare the results and still tweak the validations and properties yourself :) This is just a normal class so use it like you use any other. It just has some extra behaviour is all ;) To get these handy printouts or properties:

rake slices:merb_auth:dm:model_setup
# Or for ActiveRecord
rake slices:merb_auth:ar:model_setup

Controller Helpers

The usual controller helpers are there. The most used ones are still setup the same as they were before.

  • logged_in?
  • current_<model>
  • login_required

An example controller might look like

class Post < Application
  before :login_required

  def index
    #....
  end
end

Setting the before hook on the controller will require that there is a login for the actions of the controller. To find out who is currently logged in, use the current_<model> helper.

Configuring The Slice

MerbAuth comes with a number of options to set it up. Set these in the before_app_loads block in the init.rb as above. For convenience MerbAuth creates a handy constant MA because I hate having to type MerbAuth everywhere. You can use it in your before_app_loads block, or in your application.

Behavior Options

  • MA[:use_activation] = true #=> Uses activation emails to confirm user is human. Default false
  • MA[:forgotten_password] = true #=> Enables forgotten password usage. Default false
  • MA[:login_field] = :my_field #=> Sets the login field to use for users. Default :email. Please note that this must be a field in the database.

Router Options

  • MA[:route_path_model] ||= plural downcase class. eg :users
  • MA[:route_path_session] ||= :sessions #=> The path for logins. You wont’ generally see this.
Named Routes. You can customize these to whatever you see fit.
MA[:routes][:user][:new]        ||= :"new_#{single_model_name}" 
MA[:routes][:user][:show]      ||= :"#{single_model_name}" 
MA[:routes][:user][:edit]         ||= :"edit_#{single_model_name}" 
MA[:routes][:user][:delete]     ||= :"delete_#{single_model_name}" 
MA[:routes][:user][:index]      ||= :"#{plural_model_path}" 
MA[:routes][:user][:activate]  ||= :"#{single_model_name}_activation"

Customizing Behavior

Sometimes you’ll need to customize the behavior, and almost certainly you’ll want to customize the views for your application. When you did the rake slices:merb_auth:install there was a directory created in your application. “slices” In there there is some blank directories for controllers and views that are associated with the slice.

Customizing Views

To customize your views. First freeze them. rake slices:merb_auth:freeze:views This will put your view files in “slices/merb-auth/app/views” where you can modify them as much as you like.

Customizing Controllers

To customize your controllers you just need to monkey patch them. The slice sets them up initially so you just need to re-open them.

In “slices/merb-auth/app/controllers/sessions.rb”
class MerbAuth::Sessions < MerbAuth::Application
  # Your stuff here
end
In “slices/merb-auth/app/controllers/users.rb”
class MerbAuth::Users < MerbAuth::Application
  # Your stuff in here  
end

Spec Your Controller

To spec your controller, use the dispatch_to helper and use a block to get at the controller.

controller = dispatch_to(MyController, :index) do |c| 
  c.stub!(:current_user).and_return(@mock_user)
end

I think that’s about all there really is to using it and getting it into your application. Now that MerbAuth is a gem, it’s worth contributing improvements to, since it’s not cut and pasted code anymore, everyone can benefit :) Thanx to all the people who helped me kick the tires on it and iron out the wrinkles :)

For up to date information, please keep an eye on the README. I’m going to keep this up to date with the latest information.

Comments

On June 17, 2008 at 10:06 uzytkownik says:

I have to admit that it really impressed me. When I remember the hard-to-read files generated by rails RestfulAuthentication…

Two questions only: 1. In what format the password are stored (MD5, SHA1?) – I hope not in clear text… ;) (ok – SHA1 – I found in code) 2. Is support for OpenID planned?

On June 17, 2008 at 10:36 rubenfonseca says:

I wanted to try your code, installed the latest merb-* gems, clone the repo, but rake install produces:

                no such file to load -- merb-core/tasks/merb_rake_helper
                /

On June 17, 2008 at 14:47 robin kaarsgaard says:

You’ll need to get merb 0.9.4 for this to work, which again means that you’ll have to get merb-core (and presumably -more and -plugins as well) from GitHub. It’s easy though, just clone the repos and, run a sudo rake install in each, and you’re golden.

Also, MerbAuth requires merb_has_flash as well, although that again can be fixed by doing a ‘gem install -i

On June 17, 2008 at 14:49 robin kaarsgaard says:

Hrmm… something seems awkward. Anyway, what I meant to say was that the merb_has_flash issue can be fixed by running this command after you’ve installed merb from GitHub

gem install -i YOURPROJECT/gems merb_has_flash—ignore-dependencies

On June 17, 2008 at 15:56 hassox says:

@uzytkownik yes it’s based on restful_authentication. The plan moving forward is to allow custom authentication.

@rubenfonseca sorryI should have mentioned that this requires merb_has_flash and the latest merb gems.

On June 22, 2008 at 00:43 dkubb says:

@hassox would you consider adding bcrypt-ruby support for storing the passwords? I generally prefer that to using MD5/SHA1 + a salt because it only requires a single field, is more secure than MD5 or SHA1, and is used by OpenBSD for password storage. Most importantly it was designed to allow the user to configure the complexity of the algorithm on the fly, so it can become more secure over time to match increases in CPU speeds.

On June 22, 2008 at 13:07 brianh says:

Any plans on building Sequel support into MA? You mention that adding additional ORMs is easy, just wondering what the plans are. If there are no plans, any pointers on where to start monkey-patching?

On June 23, 2008 at 17:11 hassox says:

@dkubb I’m planning on allowing user defined password support. SHA1 is only for backwards compatibility atm

@brianh I would gladly accept a patch to bring sequel in :)

On June 25, 2008 at 14:52 keymone says:

hi hassox, nice work! is there plans of supporting OpenID authentication? i guess it could be just another adapter?

On June 26, 2008 at 10:06 hassox says:

@keymone yes I’d like to support openid… Hopefully I’ll get a weekend soon to be able to get it in :)

On June 29, 2008 at 08:54 sidonath says:

Just a hint to all those who still stick with ActiveRecord. To automatically generate migration, you first need to create your user class with the MerbAuth::Adapter::ActiveRecord mixin included, and then execute:

MERB_ENV=development rake slices:merb_auth:generate_migration

Database environment needs to be specified, because the default one is “rake”. Chances are that you don’t have the database configured for that one :)

On July 16, 2008 at 11:13 dkubb says:

@hassox: is there a way to use this with more than one type of user classes? For example, in one app I’m working on I have a User and an Affiliate. They are similar in some ways, but still different enough that they can’t be the same model. The User can log into /admin/ and the Affiliate can log into /affiliates/. While a User can have many Accounts, an Affiliate belongs to only one Account.

I want to use MerbAuth with both classes, but it appears that when I include MerbAuth::Adapter::DataMapper into User it automatically sets MA[:user], which it uses globally throughout the code as the base.

What I’d like to do is supply a method in my controller that I can use to specify which class to use as a base. It would also allow me to use things like “Account.get(account_id).users” as the base, so I can scope the User to those belonging to a specific Account, for example.

On July 18, 2008 at 02:53 namelessjon says:

It seems that to spec out merb-auth correctly in a controller, one needs to stub both current_user and current_ma_user.

If current_user isn’t stubbed, a call to current_user (which seems to work in the template, if not in the test environment) returns false, but if current_ma_user isn’t stubbed, then the app behaves as though no user is logged in.

Is this a bug, or am I missing something?

On August 06, 2008 at 03:54 yossarian says:

Hi, I’m getting an error when trying to run merb after installing the MerbAuth slice: /usr/lib/ruby/gems/1.8/gems/merb-auth-0.1.0/lib/merb-auth.rb:96:in `alias_method’: undefined method `current_ma_user’ for class `Merb::Controller’ (NameError) from /usr/lib/ruby/gems/1.8/gems/merb-auth-0.1.0/lib/merb-auth.rb:96:in `activate’ from /usr/lib/ruby/gems/1.8/gems/merb-auth-0.1.0/lib/merb-auth.rb:95:in `module_eval’ from /usr/lib/ruby/gems/1.8/gems/merb-auth-0.1.0/lib/merb-auth.rb:95:in `activate’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices.rb:120:in `run’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices/module.rb:248:in `call’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices/module.rb:248:in `each_slice’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices/module.rb:246:in `each’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices/module.rb:246:in `each_slice’ from /usr/lib/ruby/gems/1.8/gems/merb-slices-0.9.4/lib/merb-slices.rb:118:in `run’ from /usr/lib/ruby/gems/1.8/gems/merb-core-0.9.4/lib/merb-core/bootloader.rb:65:in `run’ from /usr/lib/ruby/gems/1.8/gems/merb-core-0.9.4/lib/merb-core/server.rb:51:in `start’ from /usr/lib/ruby/gems/1.8/gems/merb-core-0.9.4/lib/merb-core.rb:87:in `start’ from /usr/lib/ruby/gems/1.8/gems/merb-core-0.9.4/bin/merb:12 from /usr/bin/merb:19:in `load’ from /usr/bin/merb:19

~ Activating slice 'MerbAuth' ...

On August 06, 2008 at 03:55 yossarian says:

Whoops, sorry about the formatting. Basically the error is:

/usr/lib/ruby/gems/1.8/gems/merb-auth-0.1.0/lib/merb-auth.rb:96:in `alias_method’: undefined method `current_ma_user’ for class `Merb::Controller’ (NameError)

On August 06, 2008 at 04:01 yossarian says:

Aha, ok, I see, the trouble was that I hadn’t yet defined a User class before starting the application – it’s necessary to have a User model (or Person or whatever) before starting the app or MerbAuth doesn’t have anywhere to hang its methods. I started off by doing ‘merb-gen model User’ to see what would happen but this failed with the stack trace given above.

All is good now!

On August 13, 2008 at 13:24 tripdragon says:

If it helps others since I could not get the stupid generator to work class ThntntoeutiMigration < 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 t.column :activation_code, :string, :limit => 40 t.column :activated_at, :datetime end end end

def self.down
                  drop_table "user" 
                end

On August 13, 2008 at 13:25 tripdragon says:

Sorry about that. Try two Here or below http://pastie.org/252488 class ThntntoeutiMigration &lt; 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 t.column :activation_code, :string, :limit => 40 t.column :activated_at, :datetime end end def self.down drop_table "user" end end

On September 01, 2008 at 03:55 carpeliam says:

Any plans to make a rake slices:merb_auth:freeze:mailers rake task? I’d like to mix things up a bit, and also I can’t seem to figure out how to get absolute urls instead of relative ones in the activation email. (Funny, I just signed up for merbunity.com to post this, and the activation email they sent me didn’t have an absolute url either, so I guess I’m not the only one.)

Sign in to make your voice heard