1

I am using Devise with two user types, Employer and Candidate which are largely different from each other. There is no STI, and one model + Devise authentication for each, currently. When implementing CanCan for authorization, I found that an alias of current_candidate or current_employer to current_user was needed. It appears that only one alias can be used to current_user so the non aliased user type cannot be authorized for any actions.

class ApplicationController < ActionController::Base
  alias_method :current_user, :current_candidate
  #alias_method :current_user, :current_employer
  #^ can't use both simultaneously!
end

The above trips up authorization of employer actions since only the current_candidate can be aliased to current_user which is used in CanCan's current_ability method.

def current_ability
  @current_ability ||= ::Ability.new(current_user)
end

Is there a way to effectively alias both user types to current_user for CanCan? Or is there some type of before method that can set current_user without the alias? Happy to add more code if needed.

UserDuser
  • 461
  • 4
  • 18

1 Answers1

3

You could define the current_user method yourself. It could, for example, go in ApplicationController:

def current_user
  if current_candidate    # You could use candidate_signed_in? instead.
    current_candidate
  else
    current_employer
  end
end

Then it should be available in all your controllers and will be used by CanCan's current_ability method. If you want it available in views too, one option is to then add the line helper_method :current_user to ApplicationController.

Another option is to override the CanCan current_ability method to give the same effect as the above code by adding this to your ApplicationController:

def current_ability
  @current_ability ||= ::Ability.new((candidate_signed_in?) ? currrent_candidate : current_employer)
end
Tim
  • 2,903
  • 2
  • 18
  • 16
  • The first option seems to have worked, although I added an elsif condition to check if current_employer. Is there a way to initialize a non-model user type when a visitor hasn't signed in/up? – UserDuser May 08 '14 at 02:12
  • I think I resolved the non-model user situation by setting current_candidate to Candidate.new without saving it. – UserDuser May 08 '14 at 03:46
  • Ah, I didn't know you wanted it to return a non-nil value if nobody was logged in. In that case, yes, an elsif for the `current_employer` and a new object for the non-logged-in case could do the trick. I guess a new `Candidate` object doesn't then give the user abilities you don't want them to have?! Otherwise you could use some other class representing a Guest or something, or just have it return nil as in my answer and cope with that case (current_user == nil) in your controllers/views. You could also define a `user_signed_in?` method which checked candidate/employer_signed_in? methods. – Tim May 08 '14 at 08:06
  • The abilities are restricted for Candidates based on their id, so it's not a risk to set the guest users to a Candidate object. Good thought. – UserDuser May 14 '14 at 02:35
  • Note that this new `current_user` won't be accessible in your views, you need to declare it as a `helper_method` - http://stackoverflow.com/a/18659606/1533054 – Sheharyar Aug 26 '14 at 22:29
  • Indeed, as I had mentioned in my answer :) – Tim Aug 27 '14 at 08:00