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 F.'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

On Wed, Mar 14, 2007 at 04:23:56AM -0000, jim wrote :

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

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.


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

Dear Pierre-Alexandre,

First my apologies for just droping in like this.

I’m looking for something similar and can’t seem to get a grasp on how
to do this. I have a proper loginsystem running and would like to
enable/disable the restricted action on per user/controller base.

I’m thinking about an extra field in de user model called areas, where 1
or more
controllers are mentioned. So a user either can or can’t CRUD the
records of a model.

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

I’ve been messing with an allowed_users list in the model.rb but I go
blank when I have to start hacking on the loginsystem code.

Your help would be highly appreciated.

Again sorry to drop in like this.

Regards,

Gerard.

On Wed, Mar 14, 2007 at 04:23:56AM -0000, jim wrote :

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

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.


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

On Wed, Mar 14, 2007 at 04:30:59PM +0100, Gerard P. wrote :

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,


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

Pierre-Alexandre,

Thanx for your quick response. replies inline …

What do you mean by ‘per user/controller base’?
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

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.
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.

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.

Hope that helps,
It definitely does.

Thanks so much,

Gerard.


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

Gerard P. [email protected] wrote:

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

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

On Wed, Mar 14, 2007 at 11:16:47AM -0700, Tyler MacDonald wrote :

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:

++


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

Tyler,

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

require_group :Admin
Then I’m only curious where the ‘rest’ of the code goes best. Would you
put this in /lib/login_system.rb?

Anyway I’ll send you an email

Thanx a lot!

Gerard.

On Wed, Mar 14, 2007 at 06:59:37PM +0100, Gerard P. wrote :

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:


,========================.
| Pierre-Alexandre M. |
| email : [email protected] |
`========================’

Gerard P. [email protected] wrote:

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

require_group :Admin
Then I’m only curious where the ‘rest’ of the code goes best. Would you
put this in /lib/login_system.rb?

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

Cheers,
Tyler

Tyler,

send me an email off-list.
Couldn’t find your userid (number) on this list to send you an email …
:-/

Would you be so kind to send me one on: [email protected]

Thanx!

Regards,

Gerard.

Cheers,
Tyler