I'm trying to view my new action in my blogs controller, but I keep getting the following error message:
NameError in BlogsController#new
undefined local variable or method `authenticate_admin'
In my blogs controller, I want to restrict the new action to admins only (admins and users are two different models). I was able to get this to work in another model. If I'm not mistaken, helpers are open to all classes. I also tried to add the code from my admins helper to the blogs helper, but that didn't work.
Why can't my blogs controller use my authenticate_admin method?
Thanks for lookign :)
Here are relevant files:
blogs_controller.rb
class BlogsController < ApplicationController
before_filter :authenticate_admin, :only => [:new]
def new
#blog = Blog.new
#title = "New Article"
end
end
admins_helper.rb
def authenticate_admin
deny_admin_access unless admin_signed_in?
end
def deny_admin_access
redirect_to admin_login_url, :notice => "Please sign in as admin to access this page."
end
def admin_signed_in?
!current_admin.nil?
end
def current_admin
#current_admin ||= Admin.find(session[:admin_id]) if session[:admin_id]
end
In this case Helpers are accessible in your Views not in Controllers.
Solution is to move your methods from admins_helper.rb to ApplicationController and set them as helper_methods. You will be able to access them in your Controllers and Views.
Example:
class ApplicationController < ActionController::Base
# Helpers
helper_method :authenticate_admin
def authenticate_admin
deny_admin_access unless admin_signed_in?
end
end
Read documentation about helper_method:
http://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method
Related
I have a controller that looks something like this:
module Guardians
class StudentsController < ApplicationController
def show
#student = Student.find params[:id]
authorize #student, policy_class: StudentPolicy
end
end
end
Because the controller is within a module, the policy class which is used is Guardians::StudentPolicy, which is what I want.
However I now have another controller:
module Teachers
class StudentsController < ApplicationController
def show
#student = Student.find params[:id]
authorize #student, policy_class: StudentPolicy
end
end
end
Here the policy_class used is Teachers::StudentPolicy
But because the show method itself is identical, ideally I would like to dry this up with a concern. However if I cannot seem to do this, as
authorize #student, policy_class: StudentPolicy
will no longer automatically call the namespaced policy class when it is called from inside the concern.
What is the DRYest way to achieve this?
If you create a concern like this:
module StudentsConcern
extend ActiveSupport::Concern
included do
before_action :find_and_authorize_student, only:[:show]
end
private
def find_student_and_authorize
#student = Student.find(params[:id])
if self.class.name.deconstantize == 'Teachers'
authorize #student, policy_class: Teachers::StudentPolicy
else
authorize #student, policy_class: Guardians::StudentPolicy
end
end
end
And then include this in both of the controllers and clear the show actions.
Relatively new to rails, I've got a simple web app using Devise for user authentication. One attribute is an :admin boolean, set nil for most users, and I will change to true manually in the console for the few users who will need to have administrative access.
My question is: How should I restrict access to a particular page to those who have admin access marked as true?
I've attempted some of that logic in my pages_controller, but it doesn't seem to redirect me as desired (referring to the user_list section):
class PagesController < ApplicationController
before_action :authenticate_user!, :except => [:welcome]
def welcome
#code removed for brevity's sake
end
def dashboard
#ditto
end
def user_list
unless
current_user.admin == true
redirect_to pages_dashboard_path
else
#users = Users.all
end
end
end
Any suggestions on my goal of redirecting or otherwise restricting access to my user_list page would be greatly appreciated.
in your controller you can do something like this
class PagesController < ApplicationController
...
def user_list
if current_user.admin == true
#users = Users.all
else
render :not_an_admin
end
end
end
You can not send them to the same page that they dont have access
You can choose to render a new view
In your user_list method, model name should be singular.
def user_list
unless
current_user.admin == true
redirect_to pages_dashboard_path
else
#users = User.all
end
end
I have a user model which uses Devise for authentication and also have an administrator model, which also uses Devise.
I want administrators to be able to edit users profile via administrators/users/{user.id}/edit, however I want this process to be done through Devise Controllers, therefore I tried to inherit from the Users::RegistrationsController as shown below:
class Administrators::UsersController < Users::RegistrationsController
before_action :set_user, only: [:show,:edit,:update,:destroy]
def index
#users=User.all
end
def show
end
def new
super
end
def update
#user.update(user_params)
redirect_to [:administrators,:users]
end
but I get the following error:
Could not find devise mapping for path "/administrators/users". This may happen for two reasons: 1) You forgot to wrap your route inside the scope block. For example: devise_scope :user do get "/some/route" => "some_devise_controller" end 2) You are testing a Devise controller bypassing the router. If so, you can explicitly tell Devise which mapping to use: #request.env["devise.mapping"] = Devise.mappings[:user]
I tried to change the routes but I still get the same error.
Could you please help me?
Inheriting from Devise::RegistrationsController may initially seem like a good idea from a code reuse standpoint but it really not a very good idea.
The intent of the controllers is very different - Devise::RegistrationsController partially deals with an un-authenicated user - and the Devise controllers are scary beasts due to the amount of flexibility built in Devise.
Instead you should just setup a plain old CRUD controller as the task at hand is not very complex compared to clobbering over half of Devise::RegistrationsController.
# config/routes.rb
namespace :administrators do
resources :users
end
# app/controllers/administrators/base_controller.rb
module Administrators
class AuthorizationError < StandardError; end
class BaseController
respond_to :html
before_action :authenticate_user!
# Replace with the lib of your choice such as Pundit or CanCanCan
before_action :authorize_user!
rescue_from AuthorizationError, with: :unauthorized
private
def authorize_user!
raise AuthorizationError and return unless current_user.admin?
end
def unauthorized
redirect_to new_session_path, alert: 'You are not authorized.'
end
end
end
class Administrators::UsersController < Administrators::BaseController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def show
end
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create(user_params)
respond_with(:administrators, #user)
end
def edit
end
def update
#user.update(user_params)
respond_with(:administrators, #user)
end
def destroy
#user.destroy
respond_with(:administrators, #user)
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Instead you may want to focus on reusing the views through partials for example.
See:
ActionController::Responder
Pundit
CanCanCan
I installed devise and I've added to the user table a record or column (I'm not sure how to call it) a admin:boolean and by default it's false
In my routes.rb I have created this /admin link
get 'admin' => 'admin#index'
and I'm not sure how to show it only for admin
class AdminController < ApplicationController
before_action: I have no idea what to write here
def index
end
end
Try like this in your controller:
class AdminController < ApplicationController
before_action :is_admin?
def index
end
# it will call before every action on this controller
def is_admin?
# check if user is a admin
# if not admin then redirect to where ever you want
redirect_to root_path unless current_user.admin?
end
end
In Rails 4, I use before_action :require_login for my UserController. This is supposed to redirect if the user is not logged in. However the filter doesn't seem to be used. Why?
require_login is defined in app/helpers/sessions_helper.rb. So it is not a helper for my controller UsersController.
How does UserController know to use the function from sessions_helper.rb? It seems like I should be more specific: before_action "session/require_login".
The controller in users_controller.rb:
class UsersController < ApplicationController
before_action :signed_in_user
def index
end
end
The helper function in sessions_helper.rb:
def require_login
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
end
You're following Michael Hartl's tutorial? Helpers in /app/helpers designed for view logic, not for controller code.
However, to get the helper included into your controllers you can use include SessionsHelper.
Reference: http://ruby.railstutorial.org/chapters/sign-in-sign-out#code-sessions_helper_include
before_filter can only call controller method, not helper.
You need to move the helper require_login from helper to UsesController or its parent say ApplicationController.
If you still want to use require_login as a helper in view, you can expose it from controller by helper_method
class UsersController < ApplicationController
before_fitler :require_login
helper_method :require_login
def require_login
# code
end
end