NoMethodError users_url with devise (ajax) - ruby-on-rails

I use devise 2.2.2 with rails 3.2.11
I use devise with ajax requests
I changed the following configuration in initializers/devise.rb
config.navigational_formats = [:json, :html]
config.http_authenticatable_on_xhr = false
when I submit an empty sign in request, I expect to get a json response with errors hash, but i get a 500 instead (see below for the trace) (it works fine with sign up request)
here are my routes (nothing special)
devise_for :users
the trace:
Started POST "/users/sign_in.json" for 127.0.0.1 at 2013-01-27 13:33:45 +0100
Processing by Devise::SessionsController#create as JSON
Parameters: {"user"=>{"email"=>"", "password"=>"[FILTERED]"}}
Completed 401 Unauthorized in 1ms
Processing by Devise::SessionsController#new as JSON
Parameters: {"user"=>{"email"=>"", "password"=>"[FILTERED]"}}
Completed 500 Internal Server Error in 40ms
NoMethodError (undefined method `users_url' for #<Devise::SessionsController:0x007fe88ddd9550>):

You are probably overriding after_sign_in_path_for and have a code path in there that returns nil.
This causes devise to fall back to its default behaviour and call users_url to get the path to redirect to.
Why do I think this? Because you are having the same error I had (and lost some hair over) and also this bug report contains the github usernames of many other people who have been humbled by this particular issue.

Related

Rails ActionMailer previews not working

I have a mailer set up within the Rails Tutorial application which works fine.
The previews of the emails was working too when visiting the default path within c9.io: https://app-name.c9users.io/rails/mailers/user_mailer
Previously, this gave me the option to select which outbound email and which format, html or txt, I wanted to view a preview of.
Now, I get a response, as shown, on the page:
And in the logs, I have a 404 with a mention about IP addresses which I don't understand:
Started GET "/rails/mailers/user_mailer" for 88.210.160.9 at 2017-11-15 16:43:20 +0000
Cannot render console from 88.210.160.9! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by Rails::MailersController#preview as HTML
Parameters: {"path"=>"user_mailer"}
Completed 404 Not Found in 252ms (ActiveRecord: 0.0ms)
The log implies I can't access the preview unless I'm local to the server, although this was working fine previously. If I try to access a specific mailer method, I get the same error on the page:
And similar in the trace:
Started GET "/rails/mailers/user_mailer/account_activation" for 88.210.160.9 at 2017-11-15 16:57:41 +0000
Cannot render console from 88.210.160.9! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by Rails::MailersController#preview as HTML
Parameters: {"path"=>"user_mailer/account_activation"}
Completed 404 Not Found in 1ms (ActiveRecord: 0.0ms)
I have views associated with the html & text versions of the emails and a working mailer - this all works as expect, but the previews won't display:
class UserMailer < ApplicationMailer
def account_activation(user)
#user = user
mail to: #user.email, subject: "Account activation"
end
def password_reset(user)
#user = user
mail to: #user.email, subject: "Password reset"
end
end
Any thoughts would be appreciated.
Check if your mailer files bear a .html between their name and their .erb or .slim extension. I had the same issue.

Duplicate method-delete on rails using axios

Due to a previous issue, I have to remove //= require jquery_ujs from application.js
Now, I need to replace the /users/sign_out method with an ajax using axios. The following is my code:
axios.delete("users/sign_out", {
headers: {
"X-CSRF-Token": $('meta[name="csrf-token"]').attr('content') },
params: {
"authenticity_token": $('meta[name="csrf-token"]').attr('content')
}
})
.then(function(response) {
alert(response)
})
.catch(function(error) {
alert(error)
})
The server log shows that there is a DELETE "/" right after the delete "/users/sign_out". This is not correct.
Started DELETE "/users/sign_out?authenticity_token=mHQ3d4lJzDNS5TSWEFkDZ%2F3fI0vTDFxW6CabEffaNk6h2JRYNk8kkgCSBOXFdHmgDKcVtY8e29aGU%2F3q9gajWA%3D%3D" for 127.0.0.1 at 2017-08-01 20:59:55 +0800
Processing by Devise::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"mHQ3d4lJzDNS5TSWEFkDZ/3fI0vTDFxW6CabEffaNk6h2JRYNk8kkgCSBOXFdHmgDKcVtY8e29aGU/3q9gajWA=="}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.2ms) COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 8ms (ActiveRecord: 0.9ms)
**Started DELETE "/" for 127.0.0.1 at 2017-08-01 20:59:55 +0800**
ActionController::RoutingError (No route matches [DELETE] "/"):
TL;DR - set config.sign_out_via = :get in initializers/devise.rb
Devise is responding from the server with a redirect_to when DELETE /users/sign_out is requested. The default HTTP status code used by rails for redirect_to is 302 Found. The 302 status was originally meant to indicate that the browser should retry the same request with the same method, but this was subverted early on by many browsers which were changing the request method to GET automatically (see the RFC 1945 Note regarding 302 Moved Temporarily)
The Rails docs for redirect_to also have a note about this specifically as it relates to non-standard HTTP request methods via AJAX:
If you are using XHR requests other than GET or POST and redirecting after the request then some browsers will follow the redirect using the original request method. This may lead to undesirable behavior such as a double DELETE.
The solution is to return a 303 See Other like this: redirect_to resource_path, status: 303. I have looked for a way to set the HTTP status for the Devise::SessionsController#destroy and it doesn't seem to exist at this time in the Devise API.
However, you can tell Devise to use GET as the request method for sign out in the Devise initializer:
# in initializers/devise.rb
Devise.setup do |config|
. . .
config.sign_out_via = :get
. . .
end
Now when you visit the sign out link you should see GET /users/sign_out in your server logs and the browser redirect should also use GET.
Some other resources I used to research this issue:
This Stack Overflow response gave me the first big clue
HTTP/1.1 RFC 7231
W3 HTTP status code docs
You can also solve this by ensuring the axios request has an Accept header with 'application/json'. As it can be seen in the devise source code for the sessions controller:
def respond_to_on_destroy
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.all { head :no_content }
format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name) }
end
end
Which means it should respond with head :no_content for any non-navigational format, avoiding the redirect.

rails routing duplicates path

I have a simple enough problem of taking a rails 2 route and making it working with rails 4. The rails 2 version is this:
map.token '/sessions/token', :controller => 'sessions', :action => 'token'
I've changed it to:
get '/sessions/token', to: 'sessions#token', as: '/token'
for rails 4. The problem is when I go to /sessions/token it immediately redirects to /sessions/token/sessions/token and gives a 404
I added byebug to the token method but the request never makes it there before redirecting. I added it early in the application controller but I reach a point where the next function just stops working.
The log produces this when requesting https://delete-me.domain.org/sessions/token:
[delete-me] Started GET "/sessions/token" for 127.0.0.1 at 2015-10-02 05:44:27 -0500
[delete-me] Processing by SessionsController#token as HTML
[delete-me] Redirected to http://delete-me.domain.org/sessions/token/sessions/token
[delete-me] Filter chain halted as :ensure_proper_protocol rendered or redirected
[delete-me] Completed 302 Found in 114ms (ActiveRecord: 5.8ms)
I think it gives the :ensure_proper_protocol error because it redirects to http from https. I don't know why it does that. Maybe someone has had a similar problem and they can enlighten me.
#config/routes.rb
resources :sessions do
get :token, on: :collection #-> /sessions/token (sessions#token controller action)
end

Additional quotes in parameters

i'm trying to learn simple stuff a make HTTP request to rails app.
the problem is, when i try to make HTTP post request to my rails app, code is :
uri = URI.parse("http://localhost:4000/posts")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data( :post=> {"name" => "My query", "title" => "50"} )
response = http.request(request)
when i look into console for incoming post request, the output is:
Started POST "/posts" for 127.0.0.1 at 2015-02-02 16:14:12 +0100
Processing by PostsController#create as */*
Parameters: {"post"=>"{\"name\"=>\"My query\", \"title\"=>\"50\"}"}
Completed 500 Internal Server Error in 1ms
NoMethodError (undefined method `permit' for "{\"name\"=>\"My query\", \"title\"=>\"50\"}":String):
app/controllers/posts_controller.rb:72:in `post_params'
app/controllers/posts_controller.rb:27:in `create'
Look closely on parameters. Why does ist add additional quotation marks? how to fix this problem?
Because you are sending a stringified JSON object. I don't know what set_form_data is doing but you should be converting your request .to_json.
Look at this so question for reference here.

rails 3.1.0 devise with cancan getting unauthorized with invalid username and/or password

I have a fairly simple app using devise and cancan for authentication and authorization. Everything works great except when users try signing in with invalid usernames and/or passwords. When this happens, we get an error loading page with the following exception in the logs:
Started POST "/users/sign_in" for 127.0.0.1 at 2012-02-09 22:23:22 -0600
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"blahblahblahblah", "user"=>{"login"=>"asdasd", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE (lower(username) = 'asdasd' OR lower(email) = 'asdasd') LIMIT 1
Completed 401 Unauthorized in 74ms
I'm not sure what I need to set to allow the authorization and/or how to get more detailed logs to see exactly what is not authorized? If I enter valid credentials I can access the application and all other pieces of the app work as expected.
I know this question has been posted a couple months ago but I hot the same issue, and after fighting it for a few hours, overwriting Devise SessionController, Devise Custom Failure, debugging through, etc.., I finally found out what was going on and I decided to share the solution so you guys won't have to go through that.
The error 'Completed 401 Unauthorized in XXms' happens within the create method of SessionController at the line:
resource = build_resource(...)
And the resource cannot be built since the resource is not passed to the server. Now the problem resides in WHY the resource isn't passed to the server? I'm using JQUERY mobile which post the sign_in as an AJAX call, which JQUERY cannot upload data like that through AJAX.
You need to add to your signin form the data-ajax=false:
in Devise/sessions/new.html.erb modify the form to look like this:
form_for(resource, :as => resource_name, :url => user_session_url, html: {data: {ajax: false}}) do |f|
Hope that helps someone down the road.

Resources