Rails check if Devise has any errors? - ruby-on-rails

I am looking for a way to check if Devise has any errors (invalid credentials etc.) for a before_action method in my ApplicationController. There is code in that method that I need only to run if Devise has no errors.
class ApplicationController < ActionController::Base
before_action :foo
def foo
if !devise_errors?
end
end

You can check credential errors like this:
class ApplicationController < ActionController::Base
before_action :foo
def foo
if !devise_errors?
end
..
private
def devise_errors?
login_params = devise_parameter_sanitizer.sanitize(:sign_in)
email = login_params.dig(:email)
password = login_params.dig(:password)
user = User.find_by(email: email)
return true if user.blank?
!user.valid_password?(password)
end
..
end

Do you mean sign in errors? Wouldn't you only need this in the session controller?
You could check the flash messages...
But you might be better off checking in Warden:
Warden::Manager.warden_callback do |user, auth, opts|
# code
end

Related

Pundit::AuthorizationNotPerformedError

This is what I get going on the recipe show page:
My Controller looks like that:
class RecipesController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show]
def index
if params[:query].present?
#recipes = policy_scope(Recipe).search_by_title_and_description(params[:query]).order(created_at: :desc)
else
#recipes = policy_scope(Recipe).order(created_at: :desc)
end
end
def show
#recipe = Recipe.find(params[:id])
#recipes = Recipe.first(5)
end
end
My policy.rb:
class RecipePolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
def index?
false
end
def show?
false
end
end
end
And this is the Error message when adding a 'authorize #recipe' to the show action:
I need Pundit Authorization for the comments for each recipe but not for the recipe show-action itself. What do I do wrong? Thanks for helping !!
authenticate_user! (which you haven't shown/explained to us, but is probably a method from devise or similar?) is presumably to do with signing in -- that's authentication, not authorization, and is therefore outside the scope of what Pundit tries to solve.
Authentication is all about checking "are you signed in?". If this check fails, then the server responds with a 401 status.
Authorization is about checking "are you allowed to perform this action (potentially as a guest)?". If this check fails, then the server responds with a 403 status
Now presumably, you have also added some code like this in your application:
class ApplicationController < ActionController::Base
include Pundit
after_action :verify_authorized, except: :index # !!!!!
end
This after_action check is a safety net; it's there to ensure that you never forget to authorize an endpoint -- as that would allow the action to be performable by any user, by default! The presence of this check is what's causing your error above.
So. With that explained, let's look how you can implement this.
Should RecipesController#show be accessible by guests, or only by logged-in users?
If, and only if, the answer is "guests" then add this:
skip_before_action :authenticate_user!, only: :show
Assuming you've already performed any necessary authentication, you want to let any user see any recipe. How can you implement that?
Option 1 (recommended):
class RecipePolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end ## WARNING!! NOTICE THAT THE `Scope` CLASS ENDS HERE!!!
def show?
true # !!!!
end
end
class RecipesController < ApplicationController
# ...
def show
#recipe = Recipe.find(params[:id])
authorize(#recipe) # !!!
# ...
end
end
Option 2 (also works, but worse practice as it means you can't rely on unit tests for the policy class):
class RecipesController < ApplicationController
def show
skip_authorization # !!!
#recipe = Recipe.find(params[:id])
# ...
end
end

ActiveAdmin set custom redirect path if not authorized

I have a pritty simple custom Authorization but when it fails the user get redirected to what is set in active_admin.rb
config.root_to = 'dashboard#index'
and I have a redirect loop.
My only solution was to monkeypatch this line ActiveAdmin::BaseController::Authorization
with
module ActiveAdmin
class BaseController < ::InheritedResources::Base
module Authorization
def redirect_backwards_or_to_root
ActiveAdmin::Dependency.rails.redirect_back self, my_custom_authorize_failure_path
end
end
end
end
is there a prettier solution?
regards Kai
config.on_unauthorized_access = :access_denied
then in application_controller
class ApplicationController < ActionController::Base
protect_from_forgery
def access_denied(exception)
redirect_to define_your_path_here, alert: exception.message
end
end

In Rails 4 with devise gem, overriding after_sign_in_path_for is not effective

In ApplicationController, according to devise docs, How To: Redirect to a specific page on successful sign in and sign out, the case switch when can not be reached, even in pry debugging console, it shows 'resource.class == User is true'. I don't know what part of Rails processing I missed, any hint will be appreciated!
# ApplicationController.rb
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
protected
def after_sign_in_path_for(resource)
# check for the class of the object to determine what type it is
binding.pry
case resource.class
when User
puts "user redirect ==== "
return session.delete(:return_to) || current_user_path
else
puts "super call ....."
super
end
end
end
You are pretty close. Just need to get the resource class name using resource.class.name , so that you can compare it with a string such as 'User' which is nothing but your class name.
def after_sign_in_path_for(resource)
# check for the class of the object to determine what type it is
binding.pry
case resource.class.name #=>this would return the class name i.e 'User'
when 'User'
puts "user redirect ==== "
return session.delete(:return_to) || current_user_path
else
puts "super call ....."
super
end
end
You can make workaround by creating SessionsController that inherits from Devise::SessionsController.
class SessionsController < Devise::SessionsController
skip_before_filter :authenticate_user!
def create
user = User.find_for_database_authentication(email: params[:session][:email])
if user && user.valid_password?(params[:session][:password])
sign_in user
redirect_to session.delete(:return_to) || '/authorized'
else
redirect_to '/sign_in'
end
end
def destroy
sign_out :user
redirect_to '/signed_out'
end
end
Point to it inside your routes.rb like this:
devise_for :users, controllers: {sessions: 'sessions'}

Authenticate two different devise classes in same controller in Rails

I have this view called Intranet where only authenticated "devise clients" can access.
class IntranetController < ApplicationController
before_action :authenticate_client!
def index
end
end
On the other side, I also have other "devise admin", this devise admin requires to access the same view. How can I handle this situation?
Try this:
class IntranetController < ApplicationController
before_action :authenticate_all!
def index
end
def authenticate_all!
if admin_signed_in?
true
else
authenticate_client!
end
end
end

Devise SessionsController#create signs user in after return

I'm using devise for authentication. I want to override the Devise::SessionsController#create action to prevent the user from being signed in when they haven't activated their account:
class SessionsController < Devise::SessionsController
before_action :set_user, only: :create
def create
if #user && !#user.activated?
return redirect_to(new_activation_token_path) # prevent the user from logging in
else
super # default behavior
end
end
private
def set_user
#user = (params[:user].blank?) ?
User.new :
User.find_by(email: params[:user][:email])
end
end
It's my understanding that return redirect_to(some_path) would cause the action to return early, but it seems like it's not—the user is still being signed in. What am I missing here? How do I prevent the user from being signed in based on some condition?
In the User model, override #active_for_authentication? (Taken from here)
# Devise model
class User < ActiveRecord::Base
def active_for_authentication?
activated? && super
end
end

Resources