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