CanCan with Devise Invitable

Has anyone used this combination before? I am curious how I am going to enforce my authorization rules beyond the view layer w/r/t invitations.

Certain groups of users will be able to invite new users, but most will not. When one group of users makes invitations, that magically sets the role of the invited user to a particular group, and locks that invited user into a Practice inherited from the person who invited them.

Since the Devise Invitable controller lives in a gem, how can I reach in there and extend it to be aware of these restrictions?

Thanks in advance,

Walter

Walter Lee Davis <waltd@...> writes:

Has anyone used this combination before? I am curious how I am going
to enforce my authorization rules beyond the view layer w/r/t
invitations.

Certain groups of users will be able to invite new users, but most
will not. When one group of users makes invitations, that magically
sets the role of the invited user to a particular group, and locks
that invited user into a Practice inherited from the person who
invited them.

Since the Devise Invitable controller lives in a gem, how can I reach
in there and extend it to be aware of these restrictions?

Thanks in advance,

Walter

Since all requests must be handled by a Controller, you can simply bake your business logic into the controller in question. For example:

load_and_authorize_resource # Be sure to specify who can create Users in ability.rb

def create   user = User.create params[:user]   user.roles << current_user.roles # Or whatever floats your boat end

Walter Lee Davis <waltd@...> writes:

Has anyone used this combination before? I am curious how I am going to enforce my authorization rules beyond the view layer w/r/t invitations.

Certain groups of users will be able to invite new users, but most will not. When one group of users makes invitations, that magically sets the role of the invited user to a particular group, and locks that invited user into a Practice inherited from the person who invited them.

Since the Devise Invitable controller lives in a gem, how can I reach in there and extend it to be aware of these restrictions?

Thanks in advance,

Walter

Since all requests must be handled by a Controller, you can simply bake your business logic into the controller in question. For example:

load_and_authorize_resource # Be sure to specify who can create Users in ability.rb

def create user = User.create params[:user] user.roles << current_user.roles # Or whatever floats your boat end

Of course, and I've done that for the few User things that happen inside the UsersController. But Devise Invitable injects its methods into the User model, so there are all sorts of controllers buried deeply in the Gem source tree that I don't have access to. In Rails 2.3, I would just freeze the gems and hack them up there, but I haven't found a way to do that in the brave new Bundler world. Freezing there is kind of like compiling the code or something.

Walter

Walter Lee Davis <waltd@...> writes:

Of course, and I've done that for the few User things that happen
inside the UsersController. But Devise Invitable injects its methods
into the User model, so there are all sorts of controllers buried
deeply in the Gem source tree that I don't have access to. In Rails
2.3, I would just freeze the gems and hack them up there, but I
haven't found a way to do that in the brave new Bundler world.
Freezing there is kind of like compiling the code or something.

Walter

I may have misunderstood your question, but I don't see the need to access or change Devise's controllers directly. You have control over your User model and the Controllers which utilise it, so you should be able to enforce your business logic as described. I would not worry too much about the methods Devise injects into your models - you control access to them.

In the brave new world of Bundler, there is no easy method of freezing gems into your app in a reliable fashion. I am sure you may be able to get something working, but I sense it will be brittle and ultimately frustrating.

Here's the thing. I can certainly keep users from seeing links to the routes that control invitations, but I don't have any practical way to enforce -- at the model level or in the controller -- the user types that are really allowed to use those. It is trivial to wrap the view layer in can? conditionals, in other words. But nothing stops someone who guesses the path from writing a URL and performing an operation their user level doesn't permit.

Because Devise Invitable internals handle the resolution of new_user_invitation_path, I can't put anything in the UsersController to belt-and-suspenders check to see if the current user *should* actually be there. Those requests simply side-step my controller altogether, and point to an Invitations model that is also inside the gem.

Walter

You can make your own controller to inherit from Devise_Invitable main controller in your rails app, and then override the methods defined in Devise_invitable as per your needs. Also change the routes too, if needed.

Or, you can come up with your Invitations controller, use ActionMailer and Devise and a bit of Rails, to get you going. That is what I did, and I suggest you do the same. IMO, pick a gem if it suits your needs, but if you have to change a lot of things the gem does, make your own or look for an alternative.

You can make your own controller to inherit from Devise_Invitable main controller in your rails app, and then override the methods defined in Devise_invitable as per your needs. Also change the routes too, if needed. Or, you can come up with your Invitations controller, use ActionMailer and Devise and a bit of Rails, to get you going. That is what I did, and I suggest you do the same. IMO, pick a gem if it suits your needs, but if you have to change a lot of things the gem does, make your own or look for an alternative.

You're absolutely right on both counts. A bit more reading in the Invitable Wiki and I was able to add this:

class Users::InvitationsController < Devise::InvitationsController    def new      if cannot?( :invite, User )        raise CanCan::AccessDenied      else        build_resource        resource.practice_id = current_practice_id        render_with_scope :new      end    end    def create      if cannot?( :invite, User )        raise CanCan::AccessDenied      else        super      end    end end

Walter

Happy to help. :slight_smile: