agusgambina agusgambina - 3 months ago 16
Ruby Question

Devise define routes and before_filter for users with admin attribute

I have an small app where I implemented devise. The model where I added Devise is

consultant
instead of
user
.

class Consultant < ApplicationRecord
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :confirmable

def admin?
self.admin == true
end

end


I also added an attribute like it suggest here the option 2 to identify admin users.

My goal is to achieve that only admin consultants have access everywhere. Consultants that are not admins should only have access to
resources :tasks
and
get '/tasks/consultants/:id/worked', to: 'tasks#worked'
. Visitors should be redirected to the sign_in.

For that purpose I am adding a
before_filter
in
ApplicationController
like this

class ApplicationController < ActionController::Base

before_filter :authenticate_admin!
skip_before_filter :authenticate_admin!, only: [:tasks]

private
def authenticate_admin!
current_consultant.try(:admin?)
end

end


and in
tasks_controller.rb
I added this
before_filter


class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]

before_filter :authenticate_consultant!

...


and my
routes.rb
is defined like this

Rails.application.routes.draw do

devise_for :consultants
devise_scope :consultant do
authenticated :consultant do
root 'tasks#index'
end

root to: "devise/sessions#new"
end

get '/home', to: 'static_pages#home'
get '/help', to: 'static_pages#help'

get '/tasks/consultants/:id/worked', to: 'tasks#worked'

resources :tasks
resources :consultants

end


I don't understand why is not validating the users, any user can access anywhere




Update 1

just an small note, I changed every
before_filter
for
before_action
because
before_filter
is deprecated

As @Raffael suggested I updated my
application_controller.rb
as

class ApplicationController < ActionController::Base

before_action :authenticate_admin!

def authenticate_admin!
unless current_consultant.try(:admin?)
flash[:error] = 'Shoo, this is not for you'
redirect_to root_path
end
end

end


But I get an error


localhost redirected you too many times.


I think this is happening because I am redirecting from
routes.rb
and
application_controller.rb
. When it tries to access
devise/sessions#new
needs to be authenticaded.

I tried to avoid this by adding the following to the before_action in the
application_controller.rb


before_action :authenticate_admin!, :except => ['devise/sessions#new']





Update 2: Solution

Finally this is what I have done, first I created a new file called
admin_controller.rb


class AdminController < ActionController::Base

before_action :authenticate_admin!

protect_from_forgery with: :null_session

def authenticate_admin!
unless current_consultant.try(:admin?)
flash[:error] = 'Shoo, this is not for you'
redirect_to root_path
end
end

end


Second for controllers that need admin privileges I extend from the new class like this

class AnyController < AdminController


The
application_controller.rb


class ApplicationController < ActionController::Base

before_action :authenticate_consultant!, :except => ['devise/sessions#new']

protect_from_forgery with: :null_session

end


and in
routes.rb


Rails.application.routes.draw do

devise_for :consultants

root to: 'tasks#index'
...


Is not an elegant solution, but it works. for example it would be better to have all the admin controllers in its own namespace.

Answer

Returning a falsey value form a filter won't halt the filter chain any more (it did in previous versions of Rails).

If you want your before_filter to prevent the controller action from being executed, you need to redirect or render.

For example something along the lines of:

def authenticate_admin!
  unless current_consultant.try(:admin?)
    flash[:error] = 'Shoo, this is not for you'
    redirect_to root_path
  end
end

or:

def authenticate_admin!
  unless current_consultant.try(:admin?)
    flash[:error] = 'You need to be logged in as an admin to use this resource'
    render 'login_dialog'
  end
end