Pundit::AuthorizationNotPerformedError in Rails - ruby-on-rails

What might be causing this error in verify_authorized method and how to fix it?

Pundit adds a method to your controller called verify_authorized that ensures that the authorize method is called somewhere in your controller action. You likely setup an after_action that calls verify_authorized (https://github.com/elabs/pundit#ensuring-policies-and-scopes-are-used). Make sure you're calling authorize in each possible execution path through your controller action.
Alternatively, if you do not want to authorize that particular action, you can skip it:
class PagesControler < ApplicationController
include Pundit
after_action :verify_authorized, except: [:home]
...
end
or if you setup the after_action in an inherited controller:
class ApplicationController < ActionController::Base
include Pundit
after_action :verify_authorized
...
end
class PagesControler < ApplicationController
skip_after_action :verify_authorized, only: [:home]
...
end

Related

Skip policies from all controller actions of pundit in existing project rails

I am using existing rails application, where we currently use devise for authentication and Pundit for authorization. My requirement is to skip all policies scope authorization for all action in this existing rails application. How Can I do this?
I have tried below code but not worked:-
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!, :set_default_timezone#, :modify_search_params
before_action :set_current_user
skip_after_action :verify_policy_scoped
#.....
end
Thanks in advance :)
Pundit provides skip_authorization to skip his authorization. Executing it before all actions of the controller will make it work for your requirement.
class ApplicationController < ActionController::Base
# ...
before_action :skip_all_authorization
private
def skip_all_authorization
skip_authorization
end
# ...
end
You need to skip both action authorization with an object (which are called by using authorize(object) and with a policy scope (called with policy_scope).
You can skip the hooks on your base class:
skip_after_action :verify_policy_scoped
skip_after_action :verify_authorized
Or just add another hook to skip them on your controller (my preferred approach)
after_action :skip_all_authorization
private
def skip_all_authorization
skip_policy_scope
skip_authorization
end
But by the way, you shouldn't need this unless you're ensuring the policy is called by adding the appropriate hooks.

skip_before_action for a few controllers in Rails?

Hi I'm building an api section for an app. My all api related controllers resides inside app/controllers/api directory.
My concern is that in application_controller there is a filter before_action
:authenticate_user!, so I have to be in login mode to access the api.
My current solution: I'm adding skip_before_action :authenticate_user! in all the controllers which are in
app/controllers/api directory..
Problem: I have to write in all the controllers and I have around 80 controllers
My expectation: Is there a way where I can write in application_controller itself something like this
before_action :authenticate_user!, except: [all the controllers which are in api directory]
You will have to specify skip_before_action :authenticate_user! in every controller whose actions aren't supposed to be authenticated. You can not pass the name of controller or any thing like that as an argument to skip_before_action method.
One solution is: You can make a controller called APIController, and you can specify the skip_before_action thing there like:
class APIController < ApplicationController
skip_before_action :authenticate_user!
# rest of the code
end
And then all the controllers at app/controllers/api/ can inherit from APIController.
class OtherController < APIController
end
You can try like this if all the controllers are under API folder:
class ApplicationController < ActionController::Base
before_action :authenticate!
def authenticate!
if params[:controller].split("/").first == "api"
return true # or put code for what wherever authenticate you use for api
else
authenticate_user!
end
end
end

How do I make a before_action to run on all controllers and actions except one?

I have a rails 4.2.x app, with devise for authentication - I have several controllers.
I want the devise authenticate_user! method to be run on all controllers and actions except on the home controller index action. (Of course, authenticate_user! itself takes care that devise actions like login go through)
I can ensure that every controller action runs the before_action in application_controller.rb:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
...
end
I can also restrict a specific set of actions on all controllers:
class ApplicationController < ActionController::Base
before_action :authenticate_user!, except: [:index]
...
end
But I don't see how to make just home/index to be an exception.
I could, of course, manually add before_action :authenticate_user! to every controller, and add an exception to the home controller for the index action. But this is not very dry, and if I add new controllers, I need to remember to add this before_action to each of them.
What you have to do is to set autheticate_user! on all controllers like that :
class ApplicationController < ActionController::Base
before_action :authenticate_user!
...
end
And then on your HomeController you do that :
class HomeController < ApplicationController
skip_before_action :authenticate_user!, only: [:index]
...
end
Hope this will help you !
You can use params[:controller] to detect controller name of request.
class ApplicationController < ActionController::Base
before_action :authenticate_user!
AUTHENTICATE_USER_EXCEPT_CONTROLLERS = ['controller_names_you_want_to_exclude']
...
def authenticate_user!
unless AUTHENTICATE_USER_EXCEPT_CONTROLLERS.include?(params[:controller])
super
end
end
end

Keep an action in view post out of devise authentication

I have installed devise gem for authentication. I have created a scaffold named Members. I have put
before_filter :authenticate_user!
at the top of the Members controller. but I want to make
Member.Show
action to be out of the authentication. I mean with out signing in any one can see the Members profile.
Thanks
You can add this line in your controller (typically, at the beginning):
class MembersController < YourBaseController
# ...
skip_before_filter :authenticate_user!, only: [:show]
# ...
end
The most elegant way is to use an except filter for this:
class MembersController
...
before_filter :authenticate_user!, except: :show
..
end
This way all of your logic around the filter is contained in one place. You can also pass in an array of actions to exclude:
class MembersController
...
before_filter :authenticate_user!, except: [:show, :another_action]
..
end
For more see: http://apidock.com/rails/ActionController/Filters/ClassMethods/before_filter
You can simply do
class MembersController < ApplicationController
skip_before_filter :authenticate_user!, only: [:show]
#rest of the codes
def show
#show codes
end
end

Application Controller level validation with redirect

I'm not sure what is the best way to make sure that every user has some necessary attributes, and if they don't i would like to redirect them to 'new' page e.g.
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!, :valid_location
def valid_location
if (current_user.location.nil? || current_user.location.city.nil?)
redirect_to new_user_locations_path(current_user.id)
else
true
end
end
The above example is flawed because it creates a redirect loop. I could definetelly use some advice on creating this sort of validation. Thank you
The reason why this is creating a redirect loop is because the valid_location method is also being called on the controller responsible for the new_user_locations_path. To prevent this you need to make sure that controller does not run that filter with skip_before_filter (skip_before_action in Rails 4). A similar issue has been answered here.
class LocationsController < ApplicationController
skip_before_filter :valid_location, only: [:new, :create]
#...
end
Because valid_location returns a true/false boolean, I would recommend renaming the method to valid_location? or invalid_location? and refactoring the logic a little:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!, :redirect_invalid_locations
private
def redirect_invalid_locations
redirect_to(new_user_locations_path(current_user)) if invalid_location?
end
def invalid_location?
current_user.try(:location).try(:city).nil?
end
end
class LocationsController < ApplicationController
skip_before_filter :redirect_invalid_locations, only: [:new, :create]
end

Resources