Rails is routing to Page not found instead of 404 error - ruby-on-rails

I recently created an errors_controller.rb file which basically will route all non-existing page even those unathorized page by non-admin users to a 404 page:
class ErrorsController < ApplicationController
def page_not_found
show_404
end
end
And then on my routes.rb file:
match '*path', via: :all, to: 'errors#page_not_found'
I am using Pundit gem for my authorization and here's my users policy:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def index?
user.admin?
end
end
Now the problem is whenever I visits a page that is non existing it shows me this:
But if a non-admin user visit a page that is unathorized to access (for instance localhost:3000/users-list), it's showing this:
How do I make sure to show the 404 page or a specific page on my public folder whenever an unathorized user visits a page that is can only access by an admin?
UPDATE: Here's the content of my application_controller.rb file:
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :show_404
def after_sign_in_path_for(resource)
stored_location_for(resource) || contacts_path
end
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:user_avatar, :name, :email, :password, :password_confirmation)}
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:user_avatar, :name, :email, :password, :current_password)}
end
private
def user_not_authorized
flash[:danger] = "You are not authorized to perform this action."
redirect_to action: :index
end
def show_404
render template: "errors/404", status: 404
end
end

You should rewrite user_not_authorized on controller, that you call when a non-admin user visit a page that is unathorized to access
def user_not_authorized
show_404
end

Related

destroy session method on rails API for react front end

I'm building the token authentication for the rails side of the project project. which uses devise and JWT gems. I need to write a method(in session controller) to destroy the user session. Does anyone know how to go about doing this? in the front end the token is held in sessions when the user is logged in.
class SessionsController < Devise::SessionsController
# protect_from_forgery with: :null_session, if: ->{request.format.json?}
# skip_before_action :verify_authenticity_token
def create
user = User.find_by_email(params[:email])
if user && user.valid_password?(params[:password])
#current_user = user
else
render json: { errors: { 'email or password' => ['is invalid'] } }, status: :unprocessable_entity
end
end
def destroy
# stuck here
end
end
here's the application controller too
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
respond_to :json
before_action :underscore_params!
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
def authenticate_user
if request.headers['Authorization'].present?
authenticate_or_request_with_http_token do |token|
begin
jwt_payload = JWT.decode(token, Rails.application.secrets.secret_key_base).first
#current_user_id = jwt_payload['id']
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
end
end
def underscore_params!
params.deep_transform_keys!(&:underscore)
end
def authenticate_user!(options = {})
head :unauthorized unless signed_in?
end
def current_user
#current_user ||= super || User.find(#current_user_id)
end
def signed_in?
#current_user_id.present?
end
end

Session[:user_return_to] getting nil in redirecting back to current page after sign in

I want to redirect back to current page after sign in I have devise already in my rails application. I have followed this tutorial of devise https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update#storelocation-to-the-rescue.
What is the problem after implementing this is explained below:
I enter URL directly without logging in /tasks/1 at this point this url get saved in session[:user_return_to]
then I am redirected to /users/sign_in at this point value in session[:user_return_to] gets nil because of this I cant redirect to previous page after logging in the application.
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 :store_user_location!, if: :storable_location?
before_action :authenticate_user!
before_filter :configure_permitted_parameters, if: :devise_controller?
before_filter :prepare_exception_notifier
rescue_from ActionController::RoutingError, with: :controller_error
rescue_from ActiveRecord::RecordNotFound, with: :active_record_error
rescue_from CanCan::AccessDenied do |exception|
render file: "#{Rails.root}/app/views/pages/not_authorized.html.erb", status: 403
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role_id, :username, :first_name, :last_name, :address, :organization_name , :hours_in_day, :organization_id, :job_title])
devise_parameter_sanitizer.permit(:account_update, keys: [:role_id, :username, :first_name, :last_name,:organization_id, :division_id, :department_id, :email, :password, :password_confirmation, :current_password, :job_title, :avatar, :building_id, :location_id])
end
private
def storable_location?
request.get? && is_navigational_format? && !devise_controller? && !request.xhr?
end
def store_user_location!
store_location_for(:user, request.fullpath)
end
def after_sign_in_path_for(resource)
stored_location_for(resource) || root_path
end
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
def prepare_exception_notifier
request.env["exception_notifier.exception_data"] = {
current_user: current_user
}
end
def active_record_error(exception)
respond_to do |f|
f.html{ render file: "errors/404", status: 404 }
f.html{ render file: "errors/500", status: 500 }
f.js{ render partial: "errors/ajax_404", status: 404 }
f.js{ render partial: "errors/ajax_500", status: 500 }
end
end
end
Try the following in the application_controller
class ApplicationController < ActionController::Base
after_filter :store_location
before_action :authenticate_user!
private
def store_location
# store last url as long as it isn't a /users path
session[:previous_url] = request.fullpath unless request.fullpath =~ /\/users/
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
end
it's working on my hand

log in user right away after the user signs up

I am using devise token authentication(devise_token_auth) for user login and signup based on token because it is only for developing an api. I need to make the user logged in soon after the user registers his/her account. How can i do it so? I have tried but could not succeed so i am here with the hope of help.
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
prepend_before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update, keys: [:id, :first_name, :last_name, :phone, :status])
devise_parameter_sanitizer.permit(:sign_up, keys: [:confirm_success_url])
end
private
def user_params
params.permit.all #(:id, :email, :firstname, :lastname, :phone, :status)
end
end
Rails.application.routes.draw do
# devise_for :users, controllers: { confirmations: 'confirmations' }
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
# confirmations: 'confirmations',
registrations: 'users/registrations',
passwords: 'users/passwords'
}
class ApplicationController < ActionController::API
before_action :authenticate_user!
include DeviseTokenAuth::Concerns::SetUserByToken
def authenticate_current_user
head :unauthorized if get_current_user.nil?
end
def get_current_user
return nil unless cookies[:auth_headers]
auth_headers = JSON.parse cookies[:auth_headers]
puts('################################')
puts('auth_headers', auth_headers)
expiration_datetime = DateTime.strptime(auth_headers["expiry"], "%s")
current_user = User.find_by(uid: auth_headers["uid"])
if current_user &&
current_user.tokens.has_key?(auth_headers["client"]) &&
expiration_datetime > DateTime.now
#current_user = current_user
end
#current_user
end
end
I tried this below code
def after_sign_up_path_for(resource)
puts('it should be shown')
puts('################################')
puts('resource', resource)
puts('header', request.headers['client'])
client_id = request.headers['client']
new_auth_header = #resource.create_new_auth_token(client_id)
response.headers.merge!(new_auth_header)
end
in the Users::RegistrationController but it is not executed at all after successfully signing up.
Not too complicated. After creating the user, just call sign_in and pass the resource (user).
https://github.com/plataformatec/devise/wiki/How-To:-Sign-in-from-a-controller
Example:
sign_in #current_user
The Devise::RegistrationController in fact, already does this.
Furthermore, since you're using the DeviseTokenAuth gem and the DeviseTokenAuth::RegistrationsController controller inherits from Devise's own base controller, you (should) have access to all the helpers that Devise controllers do.
One implementation might look like this.
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
def create
super do |resource|
sign_in(resource)
end
end
end

pundit_user: undefined method `current_user' for #<User:0x007fcefbc2b150>

I have two layouts Admin and Domain. And I don't need any extra configuration in Admin layout. but if user tries to access Domain layout they must be in their valid domain.
This means that, I need to customize all of my Domain policy to include both current_user as well as current_domain. I found this can be done with UserContext and pundit_user... so here is what I have done:
application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def pundit_user
UserContext.new(current_user, current_domain)
end
def after_sign_out_path_for(resource)
root_path
end
def current_domain
#current_domain ||= Domain.where(name: requested_domain).first
end
helper_method :current_domain
private
def requested_domain
return request.env["SERVER_NAME"]
end
def user_not_authorized
# reset_session
flash[:alert] = "You are not authorized to perform this action"
redirect_to(request.referrer || root_path)
end
end
Note that, when I access Admin layout, current_domain will be nil and if I visit any routes of Domain layout, then current_domain will set to currently accessing domain.
user_context.rb
class UserContext
attr_reader :current_user, :current_domain
def initialize(current_user, current_domain)
#current_user = current_user
#current_domain = current_domain
end
end
PROBLEM
Suppose I have this policy:
user_policy.rb
class UserPolicy < ApplicationPolicy
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def index?
binding.pry # debugging
current_user.admin? ||
current_user.domain == current_domain
end
private
def current_user
# return user.is_a?(User) ? user : user.current_user
user.current_user
end
def current_domain
# return user.is_a?(User) ? nil : user.current_domain
user.current_domain
end
end
when application runs current_user and current_domain must available in UserPolicy as per documentation(https://github.com/elabs/pundit#additional-context).
But I am getting
undefined method `current_user' for #<User:0x007fcefbc2b150>
That means, still I have user object in it, not user.current_user and user.current_domain
Please let me know, if you need further description. What am I missing here?
It was my own dumb mistake.
PROBLEM
I had a before_filter call in domain/base_controller.rb something like:
class Domain::BaseController < ApplicationController
before_action :authenticate_user!
before_action :domain_exists?
before_action :verify_domain!
private
def verify_domain!
# PROBLEM: this line was updating pundit_user again to user object
raise Pundit::NotAuthorizedError unless DomainConsolePolicy.new(current_user, current_domain).authorized?
end
def domain_exists?
if current_domain.blank?
redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
end
end
end
SOLUTION:
I have used headless policy for this because now I have both current_user and current_domain set with pundit_user in application_controller
domain/base_controller.rb
class Domain::BaseController < ApplicationController
before_action :authenticate_user!
before_action :domain_exists?
before_action :verify_domain!
private
def verify_domain!
# SOLUTION
authorize :domain_console, :has_access?
end
def domain_exists?
if current_domain.blank?
redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
end
end
end
policy/domain_console_policy.rb
class DomainConsolePolicy < Struct.new(:user, :domain_console)
def has_access?
user.current_user.admin? ||
user.current_user.domain_id == user.current_domain.id
end
end
Thanks

Pundit::AuthorizationNotPerformedError with Devise controller

I am fairly new to using this Pundit gem but seem to be having trouble understanding the policy system. I have added checks that Pundit has been called for authorization (verify_authorized) and for scoping (verfify_policy_scoped). However, this causes errors when I visit the Devise sessions controller.
Application Controller
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery
before_filter :authenticate_person!
# Verify that controller actions are authorized. Optional, but good.
after_filter :verify_authorized, except: :index
after_filter :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def pundit_user
Person.find_by_id(current_person)
end
def user_not_authorized
flash[:alert] = "You are not authorized to perform this action."
# redirect_to(request.referrer || root_path)
end
end
Application Policy
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
Error Messages
Pundit::AuthorizationNotPerformedError in
Devise::SessionsController#new
Pundit::PolicyScopingNotPerformedError in
...
The answer to this is to check if it's a devise controller.
after_action :verify_authorized, :except => :index, unless: :devise_controller?
From: https://github.com/elabs/pundit/issues/113 and https://gorails.com/forum/using-pundit-with-activeadmin
You probably need to check this section from Pundit's readme.
It basically says, that when using verify_authorized is used in after_action, it will check if authorized was actually called.
Pundit adds a method called verify_authorized to your controllers. This method will raise an exception if authorize has not yet been called. You should run this method in an after_action to ensure that you haven't forgotten to authorize the action.
The same is true for verify_policy_scoped, but for policy_scope:
Likewise, Pundit also adds verify_policy_scoped to your controller. This will raise an exception in the vein of verify_authorized. However, it tracks if policy_scope is used instead of authorize. This is mostly useful for controller actions like index which find collections with a scope and don't authorize individual instances.
In your case exception is caused by the fact that you didn't called authorize in Devise::SessionsController#new action.
I think, the best way to deal with it, is to remove after_action checks from ApplicationController and move them to a subclass.

Resources