Devise skip authentication on /assets requests - ruby-on-rails

I concede that this is more of a nuisance in development and will be less troublesome in production, but even in production, I don't want devise to be authenticating or adding any overhead whatsoever to /assets requests.
In development, we see a slew of the following (currently about 30 for each of our asset files):
Started GET "/assets/application.js?body=1" for 127.0.0.1 at 2015-09-01 11:53:40 -0500
AfCore::User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 ORDER BY `users`.`id` ASC LIMIT 1
I looked and couldn't find any options related to skipping assets, and I don't see where we explicitly added them unless something like protect_from_forgery with: :exception is causing this.
How can we have devise skip all asset requests?

I happen to have access to the source code and it is related to a rack component. Calling warden.user in a rack component is request based and will query for the user for each request. Here is the code used to diagnose:
puts 'before warden.user'
if warden.presence && warden.user.presence
security_context.user = warden.user
end
puts 'after warden.user'
and the console output:
before warden.user
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 3 ORDER BY `users`.`id` ASC LIMIT 1
after warden.user
*****EDIT*****
This can be skipped with:
def call(rack_env)
if rack_env['PATH_INFO'] =~ /^\/assets/
# avoid retrieving the session's user for unnecessary calls - assets/validators
#app.call(rack_env)
else
...
# warden.user stuff
...
#app.call(rack_env)
end

Related

after upgrading to rails 5, I'm getting an invalid CSRF token error when trying to log in

I'm using devise for authentication, but after I upgraded to rails 5 from rails 4 I cannot log in even though CSRF token is inside the request.
here is the server log I'm seeing:
Started POST "/users/sign_in" for 127.0.0.1 at 2019-04-02 15:27:09 +1100
Processing by Users::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"q3Ui2rNEIIuRcpNxpbhbIxYLWuYcfd4FxBzIHKgBvdFLUZ96gTIJSQ37kfziG82Vg77NHfdvEkIrThfG6ySpiQ==", "user"=>{"email"=>"xxx", "password"=>"[FILTERED]", "remember_me"=>"0"}}
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'xxx' ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.3ms) UPDATE `users` SET `current_sign_in_at` = '2019-04-02 03:46:49', `sign_in_count` = 16, `updated_at` = '2019-04-02 03:46:49' WHERE `users`.`id` = 2
(2.2ms) COMMIT
Can't verify CSRF token authenticity.
(0.2ms) BEGIN
(0.1ms) COMMIT
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'xxx' ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.4ms) UPDATE `users` SET `last_sign_in_at` = '2019-04-02 03:46:49', `sign_in_count` = 17 WHERE `users`.`id` = 2
(0.3ms) COMMIT
Obviously, when I skip the authenticity token verification the problem is going away.
After digging around a bit I found this this thread and specifically this:
For Rails 5, note that protect_from_forgery is no longer prepended to the before_action chain, so if you have set authenticate_user before protect_from_forgery, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or use protect_from_forgery prepend: true.
so the problem solved :)
(thanks Mark Merritt who pointed out to the same issue as well)

Default Devise::SessionsController 'DELETE /users/sign_out' not signing out users

I have an application with a log out link: <%= link_to('Logout', destroy_user_session_path, method: :delete) %>
Upon clicking the link, I get the following in my dev server logs:
Started DELETE "/users/sign_out" for 172.30.0.1 at 2019-03-06 14:52:49 +1300
Processing by Users::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"JLLEb2GSjGuYx+oBhsAkB0jcP0qZCJEUBvuH5VDmCS9Xbwe/hw085gumBPqJmWTtjyFeW1Io81n32NGxDuKjyQ=="}
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
(0.4ms) COMMIT
Redirected to https://webdev.test:3000/
Completed 302 Found in 28ms (ActiveRecord: 0.9ms)
And the root page then loads again, but the user has not signed out.
I opened up a tab in incognito mode in Chrome and tried logging in and logging out, and it worked fine, so it appears to be something specific to my normal-mode browser. I've tried restarting the browser and it doesn't make a difference.
The user account I'm signed in as in Devise is not increasing its sign-in count each time I do this, nor is the updated_at timestamp changing.
I then put byebug in my root page controller method and ran the following:
1: class HomeController < ApplicationController
2: def index
3: byebug
=> 4: # Omitted for brevity, nothing which touches the current_user object
5: end
6: end
(byebug) Devise.sign_out_all_scopes
true
(byebug) sign_out
(0.8ms) BEGIN
(0.4ms) COMMIT
true
(byebug) continue
And confirmed after that, the user still was not signed out.
Restarting the Rails application and trying all the above again resulted in the same outcome, I still can't sign that user out.
Has anyone seen this before and have any ideas on what's getting stuck and how I can sign out?
Edit
As requested by a commenter, the routes relating to sign_out:
$ rails routes | grep sign_out
destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy
$
The output of rails routes | grep destroy_user_session are identical to the above as well, so it shows there's not a named route directing requests to a different controller action.
And the controller at app/controllers/users/sessions_controller.rb (which only overrides the create method, not the destroy method)
class Users::SessionsController < Devise::SessionsController
def create
# Omitted for brevity
end
end
I tried and created user controller to check and seem everything is good. I have only one strange problem, which I am not sure why it is changed. It is about SQL Query. Your response is getting following query.
SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 ORDER BY `users`.`id` ASC LIMIT 1
How come it has 4 id as hard coded, normally it should be prepared statement like following
SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 1]]
So to investigate I will try to use different users to see to login and logout and check if ID is changing or not. If it is not changing then I will investigate where this ID is coming from and why it is hard coded.
Changing to a button instead of link fixed this issue for me <%= button_to 'Exit', destroy_user_session_path, method: :delete %>

Devise token auth gem querying database twice for authenticating user

I am using devise token auth gem in my Rails 5 API for authentication.
class V1::HuntsController < V1::MainController
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :authenticate_user!
def index
end
end
But when I look at my logs, I see that there is two database queries to find the user. One from before_action :authenticate_user and the other from update_auth_header from after_action added by DeviseTokenAuth module.
Started POST "/v1/hunts" for 192.168.0.103 at 2016-12-07 15:41:03 +0530
Processing by V1::HuntsController#create as JSON
Parameters: {"title"=>"dddd", "clue"=>"dd", "hunt"=>{"title"=>"dddd", "clue"=>"dd"}}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."uid" = ? LIMIT ? [["uid", "raj#email.com"], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 15], ["LIMIT", 1]]
(0.1ms) begin transaction
(0.0ms) commit transaction
Completed 204 No Content in 102ms (ActiveRecord: 1.0ms)
Why is it firing another database query for user when current_user is already available? The database query doesnt seem to be cached also as it takes the same time to load.
I doubt if this is due to the default behavior in which token changes with each request. I have disabled this in my configuration.

Active Model Serializer 10 caching. It doesn't seem to be working. Why?

My toy app has a couple of models, rental_units and users. You can find the repo here
I am running Rails 5 and AMS 10.
active_model_serializers (0.10.0.rc4)
...
rails (5.0.0.beta3)
actioncable (= 5.0.0.beta3)
actionmailer (= 5.0.0.beta3)
actionpack (= 5.0.0.beta3)
...
I have a RentalUnitSerializer that looks like this:
class RentalUnitSerializer < ActiveModel::Serializer
cache key: 'rental_unit', expires_in: 3.hours
attributes :id, :rooms, :bathrooms, :price, :price_cents
belongs_to :user
end
This is my UserSerializer:
class UserSerializer < ActiveModel::Serializer
cache key: 'user'
attributes :id, :name, :email
has_many :rental_units
def name
names = object.name.split(" ")
"#{names[0].first}. #{names[1]}"
end
end
This is part of my Gemfile:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '>= 5.0.0.beta3', '< 5.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma'
gem 'active_model_serializers', '~> 0.10.0.rc1'
gem "dalli"
gem "memcachier"
And this is my config/environments/development.rb file:
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=172800'
}
else
config.action_controller.perform_caching = true
config.action_mailer.perform_caching = false
config.cache_store = :memory_store
end
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
Here is the strange behavior
When I visit http://localhost:3000/users, this is my logs with no sign of caching:
Started GET "/users" for ::1 at 2016-03-04 15:18:12 -0500
Processing by UsersController#index as HTML
User Load (0.1ms) SELECT "users".* FROM "users"
[active_model_serializers] RentalUnit Load (0.2ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 1]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 2]]
[active_model_serializers] RentalUnit Load (0.1ms) SELECT "rental_units".* FROM "rental_units" WHERE "rental_units"."user_id" = ? [["user_id", 3]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (11.0ms)
Completed 200 OK in 13ms (Views: 12.5ms | ActiveRecord: 0.5ms)
When I visit http://localhost:3000/rental_units, there is some caching?
Started GET "/rental_units" for ::1 at 2016-03-04 15:18:37 -0500
Processing by RentalUnitsController#index as HTML
RentalUnit Load (0.4ms) SELECT "rental_units".* FROM "rental_units"
[active_model_serializers] User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (16.1ms)
Completed 200 OK in 21ms (Views: 19.1ms | ActiveRecord: 1.4ms)
What is going on? It looks like in the latter logs, the users are being cached? I believe that since 3 different rental units belong to User 1, AMS has some default caching going on where identical SQL queries just hit a cache of some kind. So the first query for User1 hits the server's database but subsequent queries for User1 do not. Where is this cache? Is it on the server? Or some webserver?
But I think my caching strategy in my Serializer isn't working at all. Why?
To help with debugging, try setting a logger for your cache. The Rails cache supports setting a logger, but the MemoryStore doesn't set it by default.
In an initializer, try the following:
# config/initializers/cache.rb
Rails.cache.logger = Logger.new(STDOUT)
# or Rails.cache.logger = Rails.logger
Restart your Rails server and you should see logging of cache hits/misses. I suspect it is working but possibly not in a way you were expecting.
Since you asked, there's actually a couple layers of caching your question touches on: fragment (or view) caching and query caching.
You've enabled the cache as :memory_store which will create an instance of ActiveSupport::Cache::MemoryStore accessible in your application and in the Rails console as Rails.cache.
You've also set cache options for your serializer. From the ActiveModelSerializer guides for your version, it looks like you've set this up correctly. So far so good.
Typically, in a Rails controller, the Rails cache doesn't get invoked until you go to render a view. In your case, you're rendering a json view (through AMS); it'd work the same with an HTML template.
The first time this request is made, the cache is cold, the DB query is made, Rails asks the cache for a representation of the JSON string(s) using a cache key (or keys). Since the cache is empty for this set of resources, the JSON string must be generated, then it is cached in Rails.cache, then the response is returned.
The next time this request is made (within the expiration window), the DB query is made - Rails needs to know what for which resources to ask the cache right? - and this time, the cache hits, and the string is returned from cache. No new JSON needs to be generated. Notice that this still incurs a DB hit to request the original resource(s). These requests are necessary for AMS to look up the cache key(s) for your JSON.
You're also seeing CACHE select ... queries in your logs as well. As was mentioned in another answer, this may indicate you're making the same DB query over and over (often an indication of an N+1 query), hence the suggestion to "eager load" those relations. Your RentalUnit belongs to :user, so for each rental unit, a separate request is being made for each user - you may be able to grab all those users in one query with eager loading.
Rails provides some ability to cache the results of SQL queries. This is separate from the view caching we've been discussing which you've enabled for AMS.
I tried all the above answers to no avail. In the end I just ended up rm -rf ./tmp/cache in the root of my project.

Ajax Delete links log out current_user

The title pretty much explains it. I'm having an odd situation where views that allow users to delete notifications using Ajax cause the current_user to be logged out. I don't even know where to begin debugging this...
Here's the controller
class NotificationsController < ApplicationController
def destroy
#notification = Notification.find(params[:id])
#notification.destroy
respond_to do |format|
format.js
end
end
end
This is the entire controller, nothing is abridged. The notifications are generated by the system, so the only action a user can take is to "dismiss" (ie. delete) them.
I also tried this using the newer respond_with syntax and had the same effect.
I'm using Devise, and Rails 3.0.9. Any idea what could be going on -- or suggestions on how to debug??
-- EDIT 1 --
Routes.rb
resources :notifications, :only => [:destroy]
Delete link
%span.delete= link_to( 'dismiss', notification_path(notification), :method => :delete, :remote => true )
-- EDIT 2 --
Well, I noticed something new in the logs -- see **** below.
Started DELETE "/notifications/10" for 127.0.0.1 at 2011-06-21 21:47:15 -0500
Processing by NotificationsController#destroy as JS
Parameters: {"id"=>"10"}
SQL (0.4ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL (0.3ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Slug Load (0.4ms) SELECT "slugs".* FROM "slugs" WHERE ("slugs".sluggable_id = 1 AND "slugs".sluggable_type = 'User') ORDER BY id DESC LIMIT 1
****AREL (0.3ms) UPDATE "users" SET "remember_token" = NULL, "remember_created_at" = NULL, "updated_at" = '2011-06-22 02:47:15.913839', "preferences" = '---
:email_notifications: ''true''
' WHERE "users"."id" = 1
Notification Load (0.2ms) SELECT "notifications".* FROM "notifications" WHERE "notifications"."id" = 10 LIMIT 1
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
AREL (0.3ms) UPDATE "users" SET "notifications_count" = COALESCE("notifications_count", 0) - 1 WHERE "users"."id" = 1
AREL (0.1ms) DELETE FROM "notifications" WHERE "notifications"."id" = 10
Rendered notifications/destroy.js.erb (0.7ms)
Completed 200 OK in 6416ms (Views: 9.6ms | ActiveRecord: 4.1ms)
So, there it is, it looks like part of the users table is getting set to null, particularly the remember_token which I suspect is triggering Devise to end the session, or maybe this is done by Devise after the session is destroyed. But how do I track that down?
The only thing I can think of that causes notifications to interact with Users is there's a counter_cache on Users for notifications_count.
I appreciate thoughts and suggestions on how to debug!
-- EDIT 3 --
After digging with ruby-debug it looks like the issue is related to Devise and changes to the rails.js script. See:
https://github.com/plataformatec/devise/issues/913
https://github.com/ryanb/cancan/issues/280
I'm trying out some of the suggestions on those threads and will post if I find a solution.
I had a similar problem. Solution was as simple as adding
<%= csrf_meta_tag %>
to the layout.
It turns out this had to do with changes to the Rails jQuery UJS driver and Devise. I had updated Devise without updating jQuery UJS -- and Devise was expecting the CSRF token to be handled differently, so it was processing the ajax request as unauthorized which meant destroying the current user's session. Upgrading to the latest jQuery Rails driver fixed the problem.
Are you sure this controller and action are the ones that are triggered by the request? It sounds like the path you are DELETEing isnt right and you are hitting your sessions path instead.
Is it possible that the destroy notification path is redirecting to the session destroy path through JS?
Do you have destroy.js template in your notifications views? try adding one that is empty see if you get a different result.

Resources