Simple authorization setup

I'm using single table inheritance to handle several types/roles of users within my application.

I want to be able to authorize certain actions based on user type.

I've seen Chad Fowler's recipe for users|roles|rights, but I like the simplicity of STI and don't really want to introduce any other tables/ models if I can help it.

Could anyone suggest some ideas for authorizing users by type within controllers?

Thanks.

Jim,

Don't know if this is what you're looking for but Rails supports a type of single inheritance that you could use Here's an example (this goes in the model file, so in this case user.rb):

    class User < ActiveRecord::Base

      def self.inheritance_column         'usertype'       end

    end

    class Visitor < User; end     class Editor < User; end     class Admin < User; end

So, in your 'users' table you'd define a field (a varchar - make it as big as necessary) called 'usertype'. The above code defines a class called User which declares that it's inheritance_column is 'usertype'. When I create users I don't create them from the User class, but rather from one of the subclasses (Visitor, Editor and Admin - you can define as many as you like).

So instead of saying something like

    @this_user = User.new(params[:user])

I'd do something like

    @this_user = Visitor.new(params[:user])

Which creates an entry in the 'users' table and puts 'Visitor' in the 'usertype' field. At some later point I could do something like

  check_user = User.find(params[:id])

  if check_user.usertype == 'Admin'     etc...   end

Notice here that I have used the User class again, rather than Visitor, that's because it's the parent class and I don't know what type of user it will return.

Hope this helps some.

Dale

You could just add a row 'rights' in your user table? In your user model, you could add such methods:

def is_admin?   self.rights == 'admin' end

So, in your controllers, you'll be able to add easily before_filter methods based on rights.

If you like STI, add an Admin<User model and write methods like:

def self.find_all(id) find(:all, :conditions => ['rights = admin']) end

and add a before_save:

before_save :admin_rights

def admin_rights   self.rights = "admin" end

So, you'll be able to do Admin.create(:nom => 'pam') whithout think of the row 'rights' in your controller.

My $0.02.

I have a proper loginsystem running and would like to enable/disable the restricted action on per user/controller base.

What do you mean by 'per user/controller base'?

If you just want to restrict access of some actions (may be in several controllers), just add in your controllers:

before_filter :authorize, :only => [my_restricted_actions]

That is to say, it will call the method 'authorize' before each action in [my_restricted_actions].

The method authorize could be similar to (put it in application.rb):

def authorize if User.find(session[:user_id]).rights == 'admin'   return true else   flash[:warning]="Hey! You're not admin!"   redirect_to :controller => "user", :action => "show"   return false end end

If the User.find(session[:user_id]) user is not an admin, the called method (destroy, update, ... whatever you want) won't be executed.

I'm thinking about an extra field in de user model called areas, where 1 or more controllers are mentioned.

You don't have to think about controllers in your tables. Just add an extra row 'rights' in your users table and play with the snippet above.

I would like to do this with as little tampering on the existing code as possible, to keep things tidy.

So I guess my method will suit to you :slight_smile:

Hope that helps,

Well, if you are going to have multiple areas, you probably want a separate table to list them, then do something like

  group = Group.find_by_name("Book")   if users.groups.include?(group)     # user can edit books     ...

  Or, with the "do" block I was emailing about last night, it just becomes:

  if users.groups.include?("Book")     # ...

  I hammered this out last night... I've got it to the point where a controller can say:

  require_group :Admin

  , etc, to restrict access. it's not ready for "release" yet, but if anybody wants to take a look at the models/migrations/authenticatedSystem, send me an email off-list.

  Cheers,     Tyler

That the, password protected, editing can be enabled for the users per controller (well model actually). e.g.

User: mark Areas: Book CD

So in this case the user mark is allowed to edit the models Book and CD

Ok, it's clear now. You want to define areas in you app in which you can manage rights for your users.

Looks clear! I'll play with it. Still wondering how ik can restrict this to a specific model. Use something like this maybe?

.... User.find(session[:user_id]).rights == 'modelname_admin'

Use the model name for the 'rights' value.

You could handle your rights like this. But what happens if your modelname change tomorrows? By the way, mark is admin of the book shop AND the CD shop. How will you handle it?

Hmm ... maybe just check for the value book (in the field area) with the method "authorize" and then it's based on the available models.

I suggest you to create a table for your areas:

create_table "admin_areas", :force => true do |t|       t.column "name", :string       t.column "user_id", :integer end

class User < ActiveRecord::Base has_many :admin_areas

def is_admin_for?(area)   AdminArea.find_by_name_and_user_id(area,self.id) end

end

The second method return true if it finds something or nil if it doesn't.

Assuming you have @area='book' in your controller book_controller.rb, you can define in your application.rb:

def authorize if User.find(session[:user_id]).is_admin_for?(@area)   return true else   flash[:warning]="Hey! You're not admin for area #{@area}!"   redirect_to :controller => "user", :action => "show"   return false end end

It definitely does.

Cool :slight_smile:

  group = Group.find_by_name("Book")   if users.groups.include?(group)     # user can edit books     ...   Or, with the "do" block I was emailing about last night, it just becomes:

  if users.groups.include?("Book")     # ...

The point is, what if the variable 'users' change one day? Or if you want to change 'Book' to 'bookshelf'? The code is much cleaner and easier to improve if you don't put such logic in each method in your controllers.

  I hammered this out last night... I've got it to the point where a controller can say:

  require_group :Admin

  , etc, to restrict access. it's not ready for "release" yet, but if anybody wants to take a look at the models/migrations/authenticatedSystem, send me an email off-list.

Great! I'm gonna send you an email right now :slight_smile:

Yep, it's in my authenticated_system.rb, which is included by the application controller.

  Cheers,     Tyler