I have this
class ScriptsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource
def run
#script.update(script_params)
Delayed::Job.enqueue ScriptRunnerJob.new(#script)
render head :ok
end
end
I get a DoubleRenderError in run.
Any idea ?
render and head are both methods that perform a render; you only want to call one of them.
# This is all you need
head :ok
Related
I am working on rails and trying to make a simple blog site and its working the way i want to on my local machine but when pushed to production its being blocked by the callback functions.
My before_action :authorized_user? callback is being called and it prompts for logging if not logged in for performing any method on the blog , and if logged in all methods create, update and destroy methods are working perfectly in my development environment but in production even after the user is logged in also and when the create method is being called it asks for to log in . I am unable to understand from where or what code is causing this to happen because the same is working perfectly fine on local machine.
Any help will he highly appreciated.
My blog_controller.rb file is
class BlogsController < ApplicationController
before_action :set_blog, only: [:show, :update, :destroy, :lock_blog, :pin_blog]
before_action :authorized_user?, except: [:index, :show]
def index
#blogs = Blog.all
render json: { blogs: #blogs },status: :ok
end
def show
comments = #blog.comments.select("comments.*, users.username").joins(:user).by_created_at
render status: :ok, json: { blog: #blog, blog_creator: #blog.user, comments: comments }
end
def create
#blog = Blog.new(blog_params.merge(user_id: #current_user.id))
if authorized?
if #blog.save
render status: :ok,
json: {blog: #blog , notice: "Blog Successfully created"}
else
errors = #blog.errors.full_messages.to_sentence
render status: :unprocessable_entity, json: {error:errors}
end
end
end
def update
if authorized?
if #blog.update(blog_params)
render status: :ok,
json: {blog: #blog, notice:"Blog successfully updated"}
else
render status: :unprocessable_entity,
json: {errors: #blog.errors.full_messages.to_sentence}
end
else
handle_unauthorized
end
end
def destroy
if authorized?
if #blog.destroy
render status: :ok,
json: {notice:'Blog deleted'}
else
render status: :unprocessable_entity,
json: {errors: #blog.errors.full_messages.to_sentence}
end
else
handle_unauthorized
end
end
private
def set_blog
#blog = Blog.find(params[:id])
end
def blog_params
params.require(:blog).permit(:title,:body,:image,:is_pinned, :is_locked)
end
def authorized?
#blog.user_id == #current_user.id || #current_user.admin_level >= 1
end
def handle_unauthorized
unless authorized?
render json:{notice:"Not authorized to perform this task"}, status:401
end
end
end
and application_controller.rb file is
class ApplicationController < ActionController::Base
skip_before_action :verify_authenticity_token
include CurrentUserConcern
include ExceptionHandlerConcern
include TokenGenerator
def authorized_user?
render json: { notice: 'Please log in to continue' }, status: :unauthorized unless #current_user
end
def authorized_admin?
authorized_user?
render json: {errors: 'Insufficient Administrative Rights'}, status: 401
end
private
end
current_user_concern.rb file
module CurrentUserConcern
extend ActiveSupport::Concern
included do
before_action :set_current_user
end
def set_current_user
if session[:token]
#current_user = User.find_by(token: session[:token])
end
end
end
Its generally recommended to use libraries for authentication and authorization instead of reinventing the wheel unless its for learning purposes. They have many eyes looking for bugs and insecurites and are battle hardened by tons of users. Home-rolled authentication systems are a very common source of security breaches which could lead to very expensive consequences.
If you're going to roll your own authorization and authentication solution I would suggest you take a page from the libraries like Devise, Pundit and CanCanCan and raise an error when a user is not authorized or authenticated so that you immediately halt whatever the controller is doing and stop the callback chain from executing further.
# app/errors/authentication_error.rb
class AuthenticationError < StandardError; end
# app/errors/authorization_error.rb
class AuthorizationError < StandardError; end
# app/controllers/concerns/
module Authenticable
extend ActiveSupport::Concern
included do
helper_method :current_user, :user_signed_in?
before_action :authenticate_user
rescue_from AuthenticationError, with: :handle_unauthorized
end
def current_user
#current_user ||= find_user_from_token if session[:token].present?
end
def find_user_from_token
User.find_by(token: session[:token])
end
def user_signed_in?
current_user.present?
end
def authenticate_user
raise AuthenticationError.new('Please log in to continue') unless user_signed_in?
end
def handle_unauthenticated(error)
render json: {
notice: error.message
},
status: :unauthorized
end
end
end
# app/controllers/concerns/authorizable.rb
module Authorizable
extend ActiveSupport::Concern
included do
rescue_from AuthenticationError, with: :handle_unauthorized
end
def authorize_admin
raise UserAuthenticationError.new('Insufficient Administrative Rights') unless current_user.admin?
end
def handle_unauthorized(error)
render json:{
notice: error.message
}, status: :unauthorized
end
end
class ApplicationController < ActionController::Base
skip_before_action :verify_authenticity_token
include Authenticable
include Authorizable
# Should you really be mixing this into the controller? Seperate the responsibilites!
include TokenGenerator
end
It also makes debugging much easier as you can disable rescue_from in testing so that you get an exception instead of just a cryptic failure message.
You should also setup your authorization system so that it always authenticates (and authorizes) unless you explicitly opt out. This is a best practice that reduces the possible of security breaches simply due to programmer omission. You opt out by calling skip_before_action :authorize_user.
Instead of your set_current_user use a memoized getter method (current_user) to remove issues caused by the ordering of callbacks. ||= is conditional assignment and will prevent it from querying the database again if you have already fetched the user. This should be the ONLY method in the system that knows how the user is stored. Do not access #current_user directly to avoid leaking the implementation details into the rest of the application.
Methods ending with ? are by convention predicate methods in Ruby and should be expected to return a boolean. Name your modules by what their responsibility is and not what code they contain - avoid the postfix Concern as it tells you nothing about what it does.
I have this following controller for my application:
class Api::BaseApiController< ApplicationController
before_action :parse_request, :authenticate_member_from_token!
def index
render nothing: true, status: 200
end
protected
def authenticate_member_from_token!
if !request.headers[:escambo_token]
#member = Member.find_by_valid_token(:activate, request.headers['escambo_token'])
if !#member
render nothing: true, status: :unauthorized
end
end
end
Then, I have another controller that inherits from that Controller:
class Api::CategoryController < Api::BaseApiController
before_action :find_category, except: [:index]
def index
#category = Category.all
puts(#category)
render json: #category
end
But the controller is allowing requests without the token.
EDIT 1: for some reason the index action started to working normally. But still not doing the validation for the token.
EDIT 2: fixing method from private to protected
Your code needs to render :unauthorized if the token is missing, OR invalid. In other words, you need the code to be along the lines of:
def authenticate_member_from_token!
unless Member.find_by_valid_token(:activate, request.headers['escambo_token'])
render nothing: true, status: :unauthorized
end
end
However, with this code you may find yourself double-rendering in the controller. A cleaner approach could be to instead raise an exception, then rescue from it and render appropriately - e.g.
EscamboTokenInvalid = Class.new(StandardError)
rescue_from EscamboTokenInvalid, with: :escambo_unauthorized
def authenticate_member_from_token!
unless Member.find_by_valid_token(:activate, request.headers['escambo_token'])
raise EscamboTokenInvalid
end
end
def escambo_unauthorized
render nothing: true, status: :unauthorized
end
I have a link in my application.html.erb file
<%= link_to 'Sprzedaż', sell_path, remote: true %>
In the controller I authenticate user with before_action :authenticate_user!. Below is my authenticate_user! method.
protected
def authenticate_user!
if user_signed_in?
super
else
respond_to do |format|
format.js { render nothing: true, status: :unauthorized }
format.html { redirect_to root_path, :alert => 'Aby przejść do tej strony, musisz być zalogowany.' }
end
end
end
Basically it works correctly if the user isn't authorized. If the user has enabled Javascript it shows nice notification, and if the user hasn't enabled Javascript it shows alert and redirect to root_path which is good. The problem is that when the user is signed in and click the link nothing happens. It should redirect to the sell_path.
This is my ItemsController
class ItemsController < ApplicationController
before_action :authenticate_user!
def sell
#user = current_user
#items = JSON.parse(HTTParty.get("http://steamcommunity.com/profiles/#{#user.uid}/inventory/json/730/2?l=polish").body)
end
end
This is my ApplicationController
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :steam_informations
def steam_informations
#steam = session[:steam]
end
protected
def authenticate_user!
if user_signed_in?
super
else
respond_to do |format|
format.js { render nothing: true, status: :unauthorized }
format.html { redirect_to root_path, :alert => 'Aby przejść do tej strony, musisz być zalogowany.' }
end
end
end
end
You are trying to override helpers which defined in runtime. This is not how you must to do it.
In your case I recommend you to define for example authenticate! method like this:
def authenticate!
return true if user_signed_in?
respond_to do |format|
format.js { render nothing: true, status: :unauthorized }
format.html { redirect_to root_path, :alert => 'Aby przejść do tej strony, musisz być zalogowany.' }
end
end
This method will do nothing if user signed in and redirect to root page if user not signed in. Just define this method in ApplicationController and then use before_filter :authenticate! hook to execute it.
When before_filter \ before_action method returns false - rails won't execute your action. And if return true or just return - rails will execute action after hooks like authenticate! and render your views. To make it clear I'll show you some examples.
class FooController << ApplicationController
before_filter :dead_hook, only: :index
before_filter :nice_hook, only: :show
def index
# this action will be never executed because dead_hook method returns false.
end
def show
# this action will be executed right after nice_hook method because of 'return true' command in nice_hook method
end
def dead_hook
return false
end
def nice_hook
return true
end
end
Another way to do just like you trying to do - monkey-patch devise helper. You can do it like this:
module Devise
module Controllers
module Helpers
def authenticate_user!
# implement your logic here
end
end
end
end
Here you can check out whats going on in devise helpers:
Github Devise Helpers source code
Just for clarification: there is no difference between before_filter and before_action. Feel free to use any of them. before_action newer but before_filter not deprecated.
I guess you can consider this a continuation from my previous question. Basically I'm rendering a js.erb partial which enables ajax functionality to like/dislike a restaurant dish. I have four actions that render this partial:
class DishesController < ApplicationController
def like
#dish.liked_by current_user
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
def unlike
#dish.unliked_by current_user
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
...
end
To DRY this up I planned to put the respond_to method inside of a before_action callback:
class DishesController < ApplicationController
before_action :render_vote_partial
...
private
...
def render_vote_partial
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
end
Unfortunately this doesn't render the partial at all. What am I doing wrong?
If you want to dry up the respond_to, just stick it in a method (like you have already) and call that method after each action.
Instead of
before_action :render_vote_partial
just do do:
def like
# do your work here ...
render_vote_partial
end
def unlike
# do your work here ...
render_vote_partial
end
I am wondering why there is no double-render when there is a redirect_to or render in before_action. Consider this example:
class SomeController < ApplicationController
before_action :callback
def new
callback2
render 'new'
end
def callback
render 'new'
end
def callback2
render 'new'
end
end
I see that before_action will be useless if it can't redirect but how it is made? If I comment the before_action it will throw exception.
How is before_action implemented to not cause double-render?
See the Rails Guide on controllers :
If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.