I wanted to build API for my existing application. The special authentication token was generated and added to my database. The problem is that when it comes to comparing between token sent by user application with the one defined in the database, I get such error:
NameError (uninitialized constant ActiveSupport::SecurityUtils):
app/controllers/api/v1/base_controller.rb:64:in `authenticate_user!'
Rendered
/home/snow/.rvm/gems/ruby-2.0.0-p643/gems/actionpack-4.0.2/lib/action_dispatch/middleware/templates/rescues/_source.erb
(25.4ms)
Rendered
/home/snow/.rvm/gems/ruby-2.0.0-p643/gems/actionpack-4.0.2/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(0.8ms)
Rendered
/home/snow/.rvm/gems/ruby-2.0.0-p643/gems/actionpack-4.0.2/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(29.9ms)
Rendered
/home/snow/.rvm/gems/ruby-2.0.0-p643/gems/actionpack-4.0.2/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (74.0ms)
Or you can see the response from postman:
Searching the Web for answer, it appeared that it may be caused by the incompatibility of Rails version and secure_compare method. (My application is built on Rails 4.0.2 while it is needed to use Rails 4.2.0.) Is rails upgrading the only solution for my problem, or is there any other way to securely compare tokens without using ActiveSupport::SecurityUtils ?
Authentication code is here:
def authenticate_user!
token, options = ActionController::HttpAuthentication::Token.token_and_options(request)
user_phone_number = options.blank?? nil : options[:phone_number]
user = user_phone_number && User.find_by(phone_number: user_phone_number)
if user && ActiveSupport::SecurityUtils.secure_compare(user.authentication_token, token)
#current_user = user
else
return unauthenticated!
end
end
Related
I am trying to use my rails console to call a public post method in my controller.
rails c
app.post '/servers/important_method'
This obviously gives me:
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
Is there any way to create a session and call that method?
EDIT: I could create a view and restart the production environment, but I'd like to avoid that.
The key here is recognising app is an instance of ActionDispatch::Integration::Session, which includes ActionDispatch::TestProcess and therefore has a #session method that'll supply the authenticity token, once you've woken it up with a request.
We can then use this via the app::post helper, itself a simple wrapper for app's own #process method, which documents the calling parameters.
Putting all of those pieces together, in Rails 5 and later, we might write:
app.get ''
token = app.session[:_csrf_token]
app.post '/servers/important_method', params: { authenticity_token: token }
Note for older versions of Rails:
Since we're talking to the integration test API here, you may use similar forms in integration tests. When upgrading older Rails apps (v4 and earlier), you may find the following test code failing:
post '/servers/important_method', authenticity_token: token
and this should be revised to the new syntax, wrapping the parameters in params: { ... }.
I get the below error at the time of authentication from any IE browser; however, do not get this error if using firefox or chrome.
Help
ActionController::InvalidAuthenticityToken in User sessionsController#create
ActionController::InvalidAuthenticityToken
RAILS_ROOT: /webdata/ASR/docs
Application Trace | Framework Trace | Full Trace
Request
Parameters:
Some authenticity token problems are detected on IE when using IFrames as stated in this question:
Ruby on Rails Invalid Authenticity Token when using IE
If you are using one, you might want to consider:
before_filter :set_p3p
def set_p3p
response.headers["P3P"]='CP="CAO PSA OUR"'
end
In order to fix it.
I have a very simple set up where I make an API call, which calls a function, which will initialize an instance of a class. Weirdly, it works the first time, but any additional attempts to refresh the page gives me an uninitialized constant error for the very class being initialized. Here's an example
Rails 3.1
Ruby 2.0
in app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
require_relative 'test.rb'
def about
build_fleet()
render text: "This worked"
end
end
and in my app/controllers/test.rb:
class Fleet
def initialize(side)
#ships = []
#passive_abilities = []
#side = side
end
end
def build_fleet()
att_fleet = ::Fleet.new("att")
def_fleet = ::Fleet.new("def")
end
I go to localhost/static_pages/about and get "This worked". Hit refresh and see "Fleet uninitialized" complete with the appropriate fleet stack.
When I check the server log I see
>Started GET "/static_pages/about" for 127.0.0.1 at 2014-04-05 15:52:39 -0700
> Processing by StaticPagesController#about as HTML
>Completed 500 Internal Server Error in 4ms
>
>NameError (uninitialized constant Fleet):
> app/controllers/test.rb:10:in `build_fleet'
> app/controllers/static_pages_controller.rb:4:in `about'
What's going wrong on the reload?
This seems related to how rails in development mode tries to automatically reload code on each request.
Try the advice in this answer and replace the call to require_relative with require_or_load "./test.rb"
*Edit: *
I think what's happening is that at the end of every request in development mode, rails undefines most constants it knows about. (Classes are constants.)
The next request comes in and you ask ruby to load the file. But since this second request is part of the same process, ruby remembers that it already loaded test.rb and so it is skipped.
However, it looks like Fleet is a model (even if not a database-backed model). I'd drop it in the app/models/fleet.rb and rails will auto load it just fine.
I have developed a multi-tenancy Rails app using the technique from Railscast #388 Multitenancy with Scopes.
It works great on my local iMac using POW.
But, I haven't been able to get it to work on Heroku. When the app starts, I immediately get an error screen.
The error from the logs is:
2013-09-05T14:54:43.374240+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.12/lib/active_record/relation/finder_methods.rb:310:in `find_with_ids': Couldn't find Tenant without an ID (ActiveRecord::RecordNotFound)
This is the application_controller.rb code:
around_filter :scope_current_tenant
private
def current_tenant
Tenant.find_by_subdomain! request.subdomain
end
helper_method :current_tenant
def scope_current_tenant
Tenant.current_id = current_tenant.id
yield
ensure
Tenant.current_id = nil
end
I have the domain urls working correctly. But, just in case, I also tried changing the code to this (to force it to a specific Tenant). This also works fine on my local iMac:
def current_tenant
Tenant.find_by_subdomain! 'ame'
end
My main problem is that I have no idea how to debug this.
Thanks for your help!
UPDATE 1
I get the following from the log when I run local:
10:31:05 web.1 | Processing by HomeController#index as HTML
10:31:05 web.1 | Creating scope :page. Overwriting existing method Tenant.page.
10:31:05 web.1 | Tenant Load (0.7ms) SELECT "tenants".* FROM "tenants" WHERE "tenants"."subdomain" = 'ame' LIMIT 1
10:31:05 web.1 | Completed 401 Unauthorized in 75ms
Consider using ActsAsTenant gem. It provides a scoped based approach to multi-tenant applications. It allows flexibility in assigning the tenant within each request, aims to ensure all tenant dependent models include a tenant and can ensure attribute uniqueness within a tenant. Everything that Railscast #388 includes plus more.
The gem works on Heroku without issue..
If I create a brand new Rails application (using Rails 3.0.9) and knock up a quick bit of scaffolding as follows:
$ rails new testing
$ rails g scaffold thing name:string
Then app/controllers/application_controller.rb contains a "protect_from_forgery" by default, so it should check the authenticity_token during a POST create. At least, that's my understanding.
Why then, does this line successfully create a new Thing, without supplying the token.
$ curl -F "thing[name]=abc123" http://localhost:3000/things
The log entry says:
Started POST "/things" for 127.0.0.1 at 2011-07-05 08:29:18 +0100
Processing by ThingsController#create as
Parameters: {"thing"=>{"name"=>"abc123"}}
AREL (0.3ms) INSERT INTO "things" ("name", "created_at", "updated_at") VALUES ('abc123', '2011-07-05 07:29:18.484457', '2011-07-05 07:29:18.484457')
Redirected to http://localhost:3000/things/18
Completed 302 Found in 89ms
I can also do this to delete records:
$ curl -X DELETE http://localhost:3000/things/18
The same thing happens in production mode. Doesn't this leave my application open to CSRF?
If you pass invalid CSRF token or send request without it, Rails will nullify session, so protect_form_forgery is useless if your application could be accessed by everyone.
But it will save your application from CSRF attack if you have session-based authentication system.
More info: How does Rails CSRF protection work?