Tuesday, July 21, 2009

Rails: authlogic and acl_system2 under Rails 2.3.2

Updated: Documented my migration.

I got authlogic and acl_system2 to work together under Rails 2.3.2. authlogic provides authentication. acl_system2 provide authorization, i.e. ACLs. authlogic is very up-to-date, but acl_system2 is a bit dated. That's okay, though, because it's not the sort of thing that should need to change much.

Let me cover some of the stumbling blocks I encountered after I followed the authlogic tutorial and the acl_system2 documentation.

acl_system2 is not available as a gem. Hence, you need to install it via:
script/plugin install git://github.com/ezmobius/acl_system2.git
If you followed the authlogic tutorial, you'll end up with the ApplicationController#current_user method being private. To work with acl_system2, it should instead be protected. Otherwise, you'll end up with this:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.roles (NoMethodError)
.../vendor/plugins/acl_system2/lib/caboose/role_handler.rb:15:in `check'
.../vendor/plugins/acl_system2/lib/caboose/logic_parser.rb:43:in `process'
.../vendor/plugins/acl_system2/lib/caboose/access_control.rb:101:in `allowed?'
.../vendor/plugins/acl_system2/lib/caboose/access_control.rb:28:in `access_control'
Here's what my migration looks like:
class CreateRoles < ActiveRecord::Migration
def self.up
create_table :roles do |t|
t.string :title, :null => false

t.timestamps
end

add_index :roles, :title, :unique => true

create_table :roles_users, :id => false do |t|
t.integer :role_id, :null => false, :options => "CONSTRAINT fk_role_id_roles REFERENCES roles(id)"
t.integer :user_id, :null => false, :options => "CONSTRAINT fk_user_id_users REFERENCES users(id)"
end

Role.delete_all
Role.create :title => "admin"
end

def self.down
Role.delete_all
drop_table :roles_users
drop_table :roles
end
end
Each of my controllers has something like:
before_filter :require_user
access_control :DEFAULT => 'admin'
I decided to add the following to ApplicationController as protected methods:
def permission_denied
render :text => "Forbidden", :status => "403 Forbidden"
end

def permission_granted
end
That way the HTTP status gets set for "Permission denied".

In order to test the above using Cucumber and Webrat, I added a feature step like:
And the HTTP status should be "403 Forbidden"
Then, I added a step definition:
Then /^the HTTP status should be "([^\"]*)"$/ do |status|
response.status.should == status
end
So far, I'm pleased :)

2 comments:

Victor said...

There is also something else that caused me to spend some time debugging, and that is that acl_system2 makes the assumption that there is a method called "current_user". In access_control.rb:

@default_access_context[:user] = send(:current_user) if respond_to?(:current_user)

Now that is fine and dandy but my method was not called current_user, and because of this it was throwing the nil.roles error (and there was no obvious stack trace).

The reason I had to change the (I guess "standard") naming of current_user is very non-obvious (and I also spent a long time debugging this). I use ActiveScaffold and that is also defining its own version (and for different purposes) of "current_user".

I hope to be able to patch acl_system2 in a way that makes this naming convention more obvious.

Thanks for your post, as it helped me determine the problem.

Shannon -jj Behrens said...

Good luck with that.