views:

161

answers:

3

I have a Rails app where Users have Memberships to Projects (and other things, polymorphically). Users also have Roles. I want User#projects to work like a normal ActiveRecord find, but I also want administrators to have access to every project.

For a while I've been doing this:

class User < ActiveRecord::Base
   has_many :memberships, :dependent => :destroy

   def projects
     if has_role?(:admin)
       Project.find(:all)
     else
       Project.find(:all, :include => :memberships, :conditions => ["memberships.user_id = ?", id])
     end
   end
end

class Project < ActiveRecord::Base
  has_many :memberships, :as => :member, :dependent => :destroy
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :member, :polymorphic => :true
end

But I'd really rather do something like this:

class User < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy
  has_many :projects, :through => :memberships, :source => :member, :source_type => "Project"
end

so that I can use named_scope more regularly (e.g. 'alfred.projects.recent.active'). The

This works if you add new Memberships for admins automatically, but it quickly gets out of hand.

I want to keep the User#projects interface. What's the right track here?

Many thanks.

+1  A: 

Take a look at activefx's restfull authenticaion tutorial: (restful authentication tutorial) It is a rails app with the following features:

CURRENT FEATURES

- Login / Logout
- Restful, with the exception of the "activate" action
- Namespaced admin and user sections
- OpenID Authentication with support for incomplete OpenID    profiles
- Roles and permissions
- Administrative user controller
- Set roles, activate, enable / disable users  
- Login, permission, and access denied redirection system
- Member list and public profiles for logged in users
- Activation, with option to resend activation code
- Beta invitation system 
- easy on/off functionality, add/remove invites, send emails to   
- pending users
- Forgot Password / Reset Password
- Change Password 
- Failed login attempts database logging
- Recaptcha displayed for more than 5 failed logins
- Helper methods (link_to_user, if_admin?, etc.)

This thread will explain how you give owner and administrator access. #28.

Good luck.

atmorell
Your github link returns the fail octopus: That page doesn't exist!http://github.com/activefx/restful_authentication_tutorial/tree/masterThe underscores got borked somehow
srboisvert
Hmmm you are right :/ I have updated the link. Why can't I use underscores?
atmorell
Thanks atmorell. "has_<object>" is a good way to abstract this for single resources, and will help to refactor some other code I'm using. My only concern is that it's identical to my current User#projects method with respect to something like current_user.projects.find(:all). Thanks for answering!
scottburton11
A: 

I personally don't like to spread authorization concerns across models in the way you are trying to do it.

There are several reasons for this (the main being that in the end it usually leads to a non-RESTful design). The other one lies in the problem of ambivalent methods.

Think about it.

What does user.projects mean in your system? In your model it has two meanings: it means

  1. all the projects that the user is involved in,
  2. all projects that exist.

If you modeled your system this way, you would necessary end up with a bunch of methods with many different meanings, ie. purposes (think about a case in which you have three, four or more roles), which is not a good practice. There are more reasons, though. Eg. what if some user had more than one role? Admins are usually normal users, too.

What is the "right" solution then? Well, there are maybe more pf them, but the one I like and use right now is the one used by rails-authorization-plugin. Define roles in models and implement the authorization logic in controllers and views. Check out the plugin to see more.

Hope that helps! If not, just leave a comment.

Milan Novota
Thanks for the response.I'm trying to keep ownership and authorization separate, and this is the only instance where they cross. I agree it'll probably sprawl into other methods, but I'm not sure if it warrants mixing them fully just yet. I'll give rails-authorization-plugin a try though.Thanks!
scottburton11
A: 

I'll insert a shameless plug for RESTful_ACL: http://github.com/mdarby/restful_acl/tree/master

Matt Darby