Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails + Devise - Is there a way to BAN a user so they can't login or reset their password?

I have a lot of users thanks to devise and I want to ban a few problem makers. Does Devise have this support built in?

Thanks

like image 950
AnApprentice Avatar asked Apr 12 '11 01:04

AnApprentice


3 Answers

From the devise doku for authenticatable.rb:

Before authenticating a user and in each request, Devise checks if your model is active by calling model.active_for_authentication?. This method is overwriten by other devise modules. For instance, :confirmable overwrites .active_for_authentication? to only return true if your model was confirmed.

You overwrite this method yourself, but if you do, don't forget to call super:

def active_for_authentication?
  super && special_condition_is_valid?
end

So, when you have a flag blocked in the user database, the method in the user model looks something like this:

def active_for_authentication?
  super && !self.blocked
end
like image 66
Frank Avatar answered Nov 13 '22 15:11

Frank


I just implemented this in my project myself. What I did was similar to Kleber above, I defined this in my app/controllers/sessions_controller.rb (overriding Devise)...

class SessionsController < Devise::SessionsController

protected

  def after_sign_in_path_for(resource)
    if resource.is_a?(User) && resource.banned?
      sign_out resource
      flash[:error] = "This account has been suspended for violation of...."
      root_path
    else
      super
    end
   end

end

And then I added a boolean column to Users called 'banned,' so the moderators check the checkbox when editing the user in the backend, and the boolean will return true.

But there was one flaw...if a user was already logged in and then banned, they still had access to doing stuff on the site (comments, etc) at least until their session expired or they logged out. So I did this in the app/controllers/application_controller.rb...

class ApplicationController < ActionController::Base
  before_filter :banned?

  def banned?
    if current_user.present? && current_user.banned?
      sign_out current_user
      flash[:error] = "This account has been suspended...."
      root_path
    end
  end
end

That'll automatically log them out if a ban is detected. Anyway, not sure this whole thing is the "best" way to factor the whole thing as I'm newer to Rails, but the whole thing works for me and hope it will at least give you a good start.

like image 27
Shannon Avatar answered Nov 13 '22 14:11

Shannon


Customize user account status validation when logging in

Sometimes you want to add custom validation to the user before logging them in. In this case I needed to implement an account_active boolean (true or false) check. So if it's true it will allow the user to login and create a session, if false it will display the "account not active" error.

Overwrite the active_for_authentication? method in your model (User) and add your validation logic. You want to return super && (true or false)

  def active_for_authentication?
    # Uncomment the below debug statement to view the properties of the returned self model values.
    # logger.debug self.to_yaml

    super && account_active?
  end

In this case it checks the boolean value to account_active and it return it's value to the sign in method that called it.

The original active_for_authentication? method can be found in devise/lib/devise/models/authenticatable.rb.

Note: active_for_authentication? is called by Devise after authenticating user and in each request that follows. This means whatever code you add to the override of active_for_authentication? will be run for every request during the user's session.

Note: active_for_authentication? is not being called for each request when using database_authenticatable. It only gets called on login. Not sure if this is intended behavior.

Customize error message

If the method 'active_for_authentication?' returns false, method 'inactive_message' is invoked, user will receive notification for being inactive. We need to customize the message as well:

  def inactive_message
    account_active? ? super : :account_inactive
  end

Now this will refer the custom message for 'account_inactive' and not 'inactive', which we need to define in devise translation file.

 devise:
     failure:
       inactive: 'Your account was not activated yet.'
       account_inactive: 'Your account is not active.'
     registrations:
       signed_up_but_account_inactive: "Thanks for signing up. We'll let you know when your account is active"
like image 2
Marcelo Austria Avatar answered Nov 13 '22 15:11

Marcelo Austria