Rails: CanCanCan - AccessDenied error not displaying - ruby-on-rails

I'm using CanCanCan in my rails app for authorization. The routes and redirects work fine but I don't manage to display the AccessDenied error message (it worked before but I must have messed something up on the way and now it just won't show).
Here's my code:
controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, error: exception.message
end
...
end
(I changed the default :alert to :error as otherwise I was ending up with an alert from devise ('You are already logged in').
views/index.html.haml (root)
= flash[:error] if flash[:error]
...
Any ideas on how to get it to work again? Thanks in advance.
---EDIT---
Some more fixes I tried (without success):
Replacing error with message and notice
Removing the rescue_from CanCan::AccessDenied method completely - it brought me to the standard CanCan error page (so I know the method itself is working).
Replacing the exception.message with a regular string.
Adding a puts statement before redirection - it gets printed in the console as one would expect.
As #Anand Shah suggest in an answer to this question, I added the following in my view:
- if flash[:error].blank?
%p flash hash is blank
The hash was indeed empty, so it seems like the flash is not being saved at all. Then I added
flash.keep(:error)
in my controller but that didn't change anything.

The reason for this issue turned out to be my routes.rb. In addition to defining the root for devise scope:
devise_scope :user do
root to: "devise/sessions#new"
end
authenticated :user do
root 'application#show', as: :authenticated_root
end
I also had an extra root defined (which basically caused double redirection and therefore loss of the alert):
root to: 'application#show'
The code from the documentation works just fine after the necessary modification (note I could bring it back from error to alert without breaking things):
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, alert: exception.message
end

Have you tried it through a private browser in case the cookies have messed up?
Can you try this format?
redirect_to(root_url, {:flash => { :error => exception.message }})

Have you tried not handling this inline but rather something like:
rescue_from CanCan::AccessDenied do |exception|
flash[:error] = exception.message
redirect_to root_url
end
The reason it was working before is Rails understands :alert and :notice as flash messages during the redirect_to otherwise you should use the "general purpose flash bucket" as they call it via flash: {error: 'your message'}
However the Hash passed to redirect_to can also contain the http status code (:status) and url query parameters (anything that is not :alert, :notice, :status, or :flash and it all just seems like too much noise (IMO) to put a flash message in there to save 1 line of explicit code.

Related

Rails error: No HTTP_REFERER was set in the request to this action

I am developing a new rails app. but found a error:
ActionController::RedirectBackError in UsersController#show
No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].
my code:
rescue_from CanCan::AccessDenied do |exception|
redirect_to :back
end
I have some question for this error:
what is HTTP_REFERER?
why redirect to back will trigger this error?
Anyone has good idea?
what is HTTP_REFERER?
The HTTP referer is an HTTP header field that identifies the address of the webpage (i.e. the URI or IRI) that linked to the resource being requested. This is set by ActionController::Request object.
why redirect to back will trigger this error?
This usually happens when request.env["HTTP_REFERER"] is not set. (I also wonder to know in which case it is set and not set)
You could refer this answer to fix your issue.
(OR) I would highly prefer to define a custom page for access denied and redirect to it instead of redirecting :back (which I think bad idea)
For example from cancan gem docs,
rescue_from CanCan::AccessDenied do |exception|
render :file => "#{Rails.root}/public/403.html", :status => 403, :layout => false
## to avoid deprecation warnings with Rails 3.2.x (and incidentally using Ruby 1.9.3 hash syntax)
## this render call should be:
# render file: "#{Rails.root}/public/403", formats: [:html], status: 403, layout: false
end
Hope this helps!

Flash message not being displayed while rescuing from ActionController::InvalidAuthenticityToken

Looks like a bug with rails but want to make sure I didn't miss anything before reporting a bug.
I am trying to rescue from ActionController::InvalidAuthenticityToken in the application controller with the following code:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken, with: :show_errors
....
private
def show_errors
redirect_to root_path, alert: "Cookies are disabled"
end
end
The redirect works fine, but the alert message does not show. The flash hash is empty in the new page: #<ActionDispatch::Flash::FlashHash:0x007f9dbdb5c1d0 #discard=#<Set: {}>, #flashes={}, #now=nil>
There are no other filters in the application controller that could be affecting the hash / causing another redirect. The logs show only one redirect as expected.
Tried flash.keep[:alert] = .. and flash.now[:alert] = .. as well; no luck.
Getting this behavior on two different rails apps, one with 4.2.0 and another with 4.1.6.
Try
def show_errors
flash[:error] = "Cookies are disabled"
redirect_to root_path
end
Andrew White gave an explanation on what is happening on the issue I created:
Since flash is dependent on the session and the session is dependent
on cookies working then it's never going to work if you think about
it, :-)
I'd suggest redirecting to a custom url or add a query param to the
url that triggers the display of the message.
I am redirecting to a custom url as suggested.

flash notice is lost on redirect, how to find out what's removing it?

There are many posts on SO about this ( respond_with redirect with notice flash message not working Why is :notice not showing after redirect in Rails 3, among others) , I've read at least 4 and still can't solve this issue.
I've got a portion of my site that lets people do some things before they create an account. I prefer this from a UX perspective. So they're allowed to do X and Y then they get redirected to the "Create account" page (uses Devise).
The redirect looks like:
if userIsNew
... stow information in a cookie to be retrieved later ...
redirect_to "/flash", flash[:notice]
=> "Ok, we'll get right on that after you sign up (we need your email)."
and return # this has to be here, since I'm terminating the action early
end
So "/flash" is a plain page that I made to test this. It doesn't do anything, has no markup of its own, just has the basic html from the application.html, which has this line in the body:
<% if flash[:notice] %>
<p><%= notice %></p>
<% else %>
No notice!
<% end %>
It says 'No notice' every time.
I have tried:
adding in a flash.keep to my before_filter in the static controller
using :notice => instead of flash[:notice] =>
putting the notice in a cookie and pulling that text out of the cookie and into a flash in the before_filter of my application controller
redirect_to :back with the flash[:notice] =>
It's either
flash[:notice] = 'blablabla'
redirect_to foo_url
or
redirect_to foo_url, notice: 'blablabla'
I'm overriding ApplicationController#redirect_to to call flash.keep so that any messages are persisted on redirect without having to explicitly call flash.keep in my controller actions. Works well so far. Haven't had a scenario yet where unwanted messages are persisted.
class ApplicationController < ActionController::Base
def redirect_to(*args)
flash.keep
super
end
end
Let me know if there are any scenarios where this isn't a good solution.
I have been fighting with the same problem for some time and none of the posts seemed to help.
It turns out that - like usually it happens - the the problem was in my code. I did have a "redirect_to" that I forgot about, which was clearing the flash.
Namely, "root_path" for me was served by the StaticPagesController's home method. "home" was doing some checks and then redirecting you to the user_path.
In my code I had in numerous places
redirect_to root_path, :flash => {error: #error}
These redirects were never displaying the flash because my hidden "home" controller serving the "root_path" was making another redirect that cleared the flash.
Therefore my problem was solved when i added the "flash.keep" in my "home" controller method
def home
if current_user
#user = current_user
flash.keep
redirect_to #user unless #user.no_role?
end
end
Faced the same problem, flash just disappeared after any redirect, nothing helped, then, I found that it was switched off...
Check your /config/application.rb for this:
config.middleware.delete ActionDispatch::Flash

Handling ActiveRecord::RecordNotFound in Ruby on Rails

In my edit action, I have
#item = current_user.shop.items.find(params[:id])
So that the user can only edit items that belong to their shop. If they try to edit an item that does not belong to their shop, then they get an ActiveRecord::RecordNotFound error.
What is the best way of handling this error in situations like this? should i raise an exception? should i redirect somewhere and set the flash (if so, how do i do that), should i just leave it as is? Any advice is appreciated.
Thanks
Add something like the following to your controller:
rescue_from ActiveRecord::RecordNotFound do
flash[:notice] = 'The object you tried to access does not exist'
render :not_found # or e.g. redirect_to :action => :index
end
+1 for the solution of #Koraktor. If you want to avoid ActiveRecord::RecordNotFound, you can use find_by method instead of find.
#item = current_user.shop.items.find_by_id(params[:id])
if #item.nil?
flash[:error] = "Item not found"
else
# Process the #item...
end
Additionally you should set the http status while rendering the template :
rescue_from ActiveRecord::RecordNotFound do
render :not_found, :status => :not_found
end
Check the Rails guide (section 2.2.13.4) for the status list.
As stated somewhere else (for example here ) you should never make a redirection when sending a 40x status, only 30x statuses should allow redirecting. So you may encounter a weird "You are being redirected" page whenever trying to make a redirect with a :not_found status.

Redirect on catching an exception in a method in the model

I am using Authlogic-connect to connect various service providers. There is a method in user.rb
def complete_oauth_transaction
token = token_class.new(oauth_token_and_secret)
old_token = token_class.find_by_key_or_token(token.key, token.token)
token = old_token if old_token
if has_token?(oauth_provider)
self.errors.add(:tokens, "you have already created an account using your #{token_class.service_name} account, so it")
else
self.access_tokens << token
end
end
When a service provider is already added it gives the error as stated in the has_token? method and the page breaks. I need to redirect the app to the same page and flash the error. How do i do this? I have overridden the method in my own user.rb so that I can change the code.
Hmm, well you could put a method that handles the error that has_token? throws, and tell your controller to redirect that exact error. something like this in your controller:
rescue_from OauthError::RecordNotFound, :with => :deny_access
then you can put
def deny_access
redirect_to your_view_path, :alert => "Too bad sucker" #some flash message
end
Or you could do something like this in the controller:
if complete_oauth_transaction.errors.present?
redirect_to your_view_path
else
# continue on with the normal code here
end
This is how you could generically handle errors. Your exact code will vary, as this is all we have to go off of.

Resources