Custom actions in cancan - ruby-on-rails-3

I have cancan working fine for everything except custom actions. I had an upload_photos action that just wasn't being secured correctly. I added a test action which was really basic that looked like this in the ability.rb file:
can :test, User, :id => user.id
but for some reason it always evaluated to true so I changed it to the following
can :test, User, :email => 'a#a.com'
and again it always evaluates to true.
Anybody know why cancan isn't working correctly here?
My users controller has the following at the top:
load_and_authorize_resource
Thanks in advance.
SOLUTION:
Cancan's load_and_authorize_resource interferes with custom actions so we need to do something like
load_and_authorize_resource :except => [:test]
and then in the test action:
authorize! :test, #user

Related

Rails 3 with devise undefined method `users_url`

I'm making some small changes to devise in order to overwrite the layout used and some other things so I decided to create my custom routes with controllers. I only added this to the routes so far:
devise_for :users, :controllers => {
:sessions => "sessions/user",
:registrations => "registrations/user"
}
which is supposed to load the login page when accessed by /users/sign_in and it does it very well. When I try to ajax post to that url in order to validate the user input and login I get this error: NoMethodError (undefined method 'users_url' for #<Sessions::UserController:0x000000041bed98>):
The controller is basic and nothing was changed to overwrote devise logic:
class Sessions::UserController < Devise::SessionsController
layout "login" # The only addition. Rest is handled by devise
end
I had this working for a good while but today I decided to use devise for another model (admins) and I generated it's files using the devise command rails generate devise Admin which completely messed my working code of logging in the users.
Any ideas?

Using Devise in RoR - How to have only a few pages not require login - Override authenticate_user

For the majority of my site, I want to require login. But for two pages, I need to have a user be able to submit to the page without logging in.
Is there a way to override the
before_filter :authenticate_user!
which I put in the application controller?
In your desired controller, add:
skip_before_filter :authenticate_user!, :only => [:some_action, :another_action]
Read more about filters at Module ActionController::Filters::ClassMethods
before_filter :authenticate_user!, :except => [:action_name_1, :action_name_2]

change routes in devise

Im working with ruby 1.9.2, rails 3.1.3, devise 1.5.3, I need to change the devise routes, I already changed the default routes, like login and logout, but I need to change the others routes, for example:
http://localhost:3000/password/new
I need that, when the user click the link did you forget your password? go to:
http://localhost:3000/recovery_password
in my routes I tried:
get "recovery_password", :to => "devise/passwords#new"
get 'recovery_password' => 'devise_passwords#new', :as => :new_user_password
but don't works, please help.
thanks in advance.
try this. It should works. ;)
# change :devise_model to :users, or :admins or any name of your devise model
devise_for :devise_model do
get 'recovery_password' => "devise/passwords#new"
end
and you can use this in view like this.
link_to 'Forgot you password?', recovery_password_url
PS. if you have customize devise controller. You should tell the router first, and change devise controller to your customize controller name.

Is it possible to stop Rails from rendering views for actions not defined in controllers?

Following this question, I'd like to know if there is a way to override that new rails behaviour. I run into a security issue with a forgotten view, that was suddenly exposed although the action was not defined in my Controller. I think it'd be better to stop Rails from rendering those not-defined actions and after that cleaning my unused views.
Change your routes for that controller to not route to those actions.
resources :users, :except => [:index, :destroy]
resources :sessions, :only => [:new, :create, :destroy]
Those routes will become non-routable even if the views exists, you can then deal with the views as needed.

Rails' link_to method: GETing when it should DELETE

I'm following Michael Hartl's Rails Tutorial, and for some reason the following code:
<%= link_to 'delete', user, :method => :delete, :confirm => "You sure?",
:title => "Delete #{user.name}" %>
Issues a GET request (as I verified by checking the rails server log). I also verified that the following line is in my application view:
<%= javascript_include_tag :all %>
One thing I didn't quite understand, and it's probably the source of my problem: where is the "delete" method defined? I verified in Hartl's source code that he defines a "destroy" method in the controller, not "delete". But even if I change the link_to to :method => :destroy, it just issues a GET.
I'm using Rails 3.1. Any tips?
Also check that this is in your application.js:
//= require jquery
//= require jquery_ujs
Apparently I had the jquery without the jquery_ujs and I had the same problem until I added that.
Note that you may need to add these lines above any import statements within application.js.
Most browsers don't actually support the DELETE verb, so Rails fakes it by modifying the HTML it generates. Rails tacks on a HTML5 attribute called data-method and sets it to "delete". So when a user clicks on the link, it is actually issued as a GET request, but the data-method attribute allows for some Rails magic and means your routing code should recognize it as a DELETE request.
edit:
You can test it yourself in the console. Run bundle exec rails c to get into the console, and look at the HTML that this generates:
helper.link_to "delete", "foobar/delete", :method => 'delete'
The HTML should look like this:
delete
You should use the following code
<%= button_to "delete", #user_current, :method => "delete" %>
It will solve the problem or add this line //= require jquery_ujs to application.js and use:
<%= link_to 'delete', user, :method => :delete, data: {:confirm => "You sure?" } ,
:title => "Delete #{user.name}" %>
As browsers don't support the DELETE verb, Rails creates a workaround for this problem by simulating a DELETE request through a standard GET or POST. The way method: :delete works is with a JavaScript handler for all links with data-method="delete" which modifies the request so Rails
processes it as a DELETE. This JavaScript handler is provided by the jquery_ujs library.
There are several things that could have gone wrong:
Make sure you have both jquery and jquery_ujs included in your application.js. Without both nothing will process the data attributes.
Make sure the link in question really has the method: :delete option specified.
Make sure for some reason you haven't stopped the event propagation of the link in question, like so for example:
$( 'a' ).click( function( event ) {
event.stopPropagation()
})
As this would prevent the jquery_ujs handlers from being executed and the request will never be modified and will remain just a standard GET.
I faced the same problem with Michael's tutorial. The data-method="delete" actually works as expected - it does call the destroy action in the controller. The reason it tries to GET (and eventually fail) is the following:
You'll notice that one of the before_filter's in the controller is set to signed_in_user, and in session_helper.rb, you'll notice that signed_in_user calls store_location (private method), which updates session[:return_to] to the current URL.
So, back in your controller's destroy action, it tries to redirect_back_or which results in GET current_url. I modified the signed_in_user helper to only call store_location when user is not signed in already.
On rails 5 :
Have the same problem, all 'DELETE' posts were compromised, affecting my crud pages AND devise signout... to solve the problem all I had to do was :
//= require rails-ujs
All we need is add the below line of code
//= require jquery_ujs
It seems like a bug:))
You want to do this in Rails 6+:
link_to(
'Delete Me',
some_controller_path(model),
data: { method: :delete }
)
Use button_to instead of link_to.