How can I make Sinatra use CSRF Authenticity tokens? - ruby

I'm building a simple app in ruby using the Sinatra framework. It's mainly "get" based - most requests will be for listing data. However there are a couple of key screens in the app that will collect user input. I want to ensure the app is as safe as I can make it, and currently, trying to find how to implement the kind of authenticity tokens that you get in a Rails form?
Where I've got to:
Well, I know I need the tokens for csrf, but I'm unsure if I need to generate them myself or if Sinatra can do it for me - I've looked through the docs and they say that Sinatra is using Rack Protection, however, I can't find any example code for it and can't seem to figure out how to get it going - any help apprectiated - thanks!

Use the rack_csrf gem. Install it with
gem install rack_csrf
The rack_csrf gem has a Sinatra example. Below is a simpler example adapted from this page (seems offline. Archived version):
require "rack/csrf"
configure do
use Rack::Session::Cookie, :secret => "some unique secret string here"
use Rack::Csrf, :raise => true
end
Using enable :sessions instead of use Rack::Session::Cookie ... will also work in most cases (see Bill's comment).
In your view, you can get the token (or the tag) with the Rack::Csrf.csrf_token and Rack::Csrf.csrf_tag methods. If this appears lengthy, you may want to define a helper along the lines of:
helpers do
def csrf_token
Rack::Csrf.csrf_token(env)
end
def csrf_tag
Rack::Csrf.csrf_tag(env)
end
end
Small example using the helper method:
<form method="post" action="/tweet">
<%= csrf_tag %>
<input type="text" name="message"/>
<input type="submit" value="Submit a tweet!"/>
</form>

When same erb view is rendered, in a case where credentials are invalid while logging in. now after the render on submit it throws the error.
Rack::Csrf::InvalidCsrfToken at /login
Rack::Csrf::InvalidCsrfToken
Do we need to do a redirect in place of render to make this work? because in render the old csrf token is still in place. in a complete redirect we have a new token generated.

Related

How make rails forms submit if using rails-api gem. Undefined method `protect_against_forgery?

I have a rails application which is api only i am using gem rails-api. Now I am trying to create admin panel on it because we realised later that we do need an admin panel. But i think rails-api doesnt have good support for views.
When I try to submit forms it says undefined method protect_against_forgery? maybe because it doesnt supports <%= csrf_meta_tags %>
I tried to define manually this method in my controller but no luck.
def protect_against_forgery?
true
end
How can i submit the forms without getting this error.
in your api controller put following line.
skip_before_filter :verify_authenticity_token
or try with
def protect_against_forgery?
false
end
this may be help you.
According to rails-api, try to set config.api_only = false to config/application.rb file
Or, since protect_from_forgery is a method that belongs to ActionController, try to add require "action_controller/railtie" to application.rb

My partial wasn't loading because of CanCan authorization. How can I debug that?

I was having this problem trying to render a partial via an asynchronous request from my users views.
The problem was that CanCan was not authorizing the call, because it was inside a new action that I created on user_controller.rb. I am new to Rails and I thought that putting :edit on the ability class was enough.
I caught this error because I removed the remote: true from my link, so it can render the view via a http request. Don't as me why I did that. Begginer's luck!
My question is: how could I have debugged that error if not by luck?
You could have added test cases that accounted for the rendering of that partial, for instance (with rspec):
response.should render_template(:partial => 'partial_name')

Testing Sinatra's redirect back in rspec

I am running a sinatra app and have a testing suite setup using rspec 2.7.0 and webrat 0.7.3 (both the most recent versions). I have an extensive set of tests for all of my request actions and it seems to be working fine. Today I discovered Sinatra's redirect back request-level helper and implemented it in a couple of areas of my application that were rendering forms with get requests which were taking parameters.
The nice thing about the redirect back helper is that if I have an action say:
get '/login' do
#used_var = params[:var]
haml :login
end
Which renders a form, I can have validation on the post request receiving the form:
post '/login' do
# pretend User.authenticate pulls back a user entry from the database if there
# is a valid username/password combination
unless User.authenticate(params[:username], params[:password]).nil?
redirect '/content'
else
flash[:notice] = "Invalid username/password combo"
redirect back # will redirect back to the get '/login' request
end
end
And if the form doesn't validate properly, it will redirect back to the page with the from and retain any parameters that were passed in without me having to worry about storing it to a session variable. The only problem is that rspec doesn't seem to want to play nicely with the redirect back helper. i.e. if I have a spec action that does this:
it 'should redirect to login when invalid username/password combo is received.' do
get '/login', :var => 'value'
fill_in 'username', :with => 'invalid_username'
fill_in 'password', :with => 'invalid_password'
click_button 'Submit'
last_response.should be_redirect; follow_redirect!
last_request.url.should include("/login")
end
The spec fails to pass because for some reason it seems that rspec or webrat isn't picking up on the redirect back helper and is instead redirecting the request back to the root url for my application ('/').
What I want to know is whether there is a way to get rspec to redirect to the proper location in these instances? The actual application functions as expected when I test it with my browser (it redirects me to the first page with parameters), but the rspec tests don't pass properly.
try to pass :referer => '/login' to your requests, so redirect_back can know where the actually 'back' is
Apparently this was a bug in rack, and it appears to have been fixed with the release of rack 1.3.0. I tested this spec with rack 1.2.5 (most recent version prior to 1.3.0 release), and it failed, but upon upgrading to 1.3, it started passing.
After some digging around, pretty sure that this pull request (here is the commit) was the change that fixed it.
So it is no longer an issue in rack ~> 1.3.

Accessing warden login information in Sinatra test response

Practically everything I am looking for is said in the title - I need to access warden user variable in test to check whether authentication worked. Another way is also possible for me, just trying to test authentication nicely :)
should "authenticate" do
post "/login", {:login => "test_login", :password => "password"}, {"HTTP_HOST" => "test.host"}
assert last_response.redirect?
assert_equal last_response.env["warden"].user.login, "test_login"
end
You can't get at the internal request environment in a rack-test-style full-stack test. If you want to verify that you've been logged in as a specific user you'll need to look at something like the redirect URL (if it redirects to a user-identifiable URL) or follow the redirect (easy with rack-test's follow_redirect! helper) and then look for evidence of the User ID in the HTML.
I would have thought that you don't really need to test Warden itself, but you will want to make sure you're providing the correct information to it, and aren't mangling it in the middleware stack.
You might find something like Cucumber handy for doing genuine form-filling and submission.
Finally, Warden has its own test helpers (which definitely work with rack-test) so you can set up a request to be logged in without having to actually run through the logging in request/redirect cycle in each test - https://github.com/hassox/warden/wiki/testing has more details.

How to do a Post/Redirect/Get using Sinatra?

What's Sinatra's equivalent of Rails' redirect_to method? I need to follow a Post/Redirect/Get flow for a form submission whilst preserving the instance variables that are passed to my view. The instance variables are lost when using the redirect method.
Redirect in Sinatra is the most simple to use.
So the code below can explain:
require 'rubygems'
require 'sinatra'
get '/' do
redirect "http://example.com"
end
You can also redirect to another path in your current application like this, though this sample will delete a method.
delete '/delete_post' do
redirect '/list_posts'
end
A very common place where this redirect instruction is used is under Authentication
def authorize!
redirect '/login' unless authorized?
end
You can see more samples under:
Sinatra Manual
FAQ
Extensions
As for your second question, passing variables into views, it's possible like this:
get '/pizza/:id' do
# makeing lots of pizza
#foo = Foo.find(params[:id])
erb '%h1= #foo.name'
end
The Sinatra Book should clear your question. Especially the "Redirect" part.
Quoted from the book:
The redirect actually sends back a Location header to the browser, and the browser makes a followup request to the location indicated. Since the browser makes that followup request, you can redirect to any page, in your application, or another site entirely.
The flow of requests during a redirect is: Browser –> Server (redirect to ’/’) –> Browser (request ’/’) –> Server (result for ’/’)

Resources