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: