Reputation: 6669
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 < ApplicationController
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.
Upvotes: 1
Views: 267
Reputation: 2669
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
Upvotes: 1