Sinatra Session Caching - ruby

I'm having a little trouble understanding how to get Sinatra to cache sessions. This problem manifests itself when I fire up more than one application instance (ala Puma or multiple Thins).
I'm asking Sinatra (1.4) to use sessions like this:
enable :sessions
set :session_secret, 'secret sauce'
set :protection, except: :session_hijacking
$connections = []
set connections: $connections
I've left the $connections in there to demonstrate that this App is using server sent events, in case that has any relevance.
I am them using Persona (Mozilla) to support logins/authentication and am ultimately storing the logged in email in Sinatra's session with:
session[:auth_email] = data["email"]
Where data is given to me by Persona.
What I can't work out is how I ask Sinatra to store session data in a persistent store (ideally Redis) so the app can be recycled without losing session state (I've done this with Ramaze before with Ramaze::Cache.options.session = Ramaze::Cache::Redis.using())

Check out Moneta.
The project's Github page gives examples of how to set this up and is extremely easy to integrate with Sinatra.

Related

How to disable sessions in Yesod for a specific set of urls or a subsite?

I want to disable sessions for headless API endpoints, but I have to keep them turned on because this service also handles user logins.
However makeSessionBackend doesn't have access to Handler stuff or even current URI, like isAuthorizedSource does.
It appears to me that I should lift Client Session Backend code and sprinkle it with wrappers until the moment I can get at least textual path from that WAI Request.
Isn't there a better way to tell any bakend to ignore some routes like StaticR?
All of your points can be modified by overriding the makeSessionBackend method in the Yesod typeclass. Something like
instance Yesod App where
makeSessionBackend _ = fmap Just $ defaultClientSessionBackend expireTime filepath
where expireTime = 24 * 60

Sinatra + Rack::Session::Pool + Moneta

I'm using server side session handling with Moneta in my Sinatra application.
The part of my config.ru looks like that:
require 'rack/session/moneta'
use Rack::Session::Moneta do
use :Expires
adapter :Memory
end
How long does is take for sessions to expire? I couldn't find documentation for it.
I currently delete sessions with
get '/logout'
session.destroy
end
But I believe that only destroys the session cookie on the client side.
How can I find the sessions which are currently active?
There is a variable called #pool in Rack::Session. How can I access it from my Sinatra app?
You would set the expiration time when storing/accessing session keys by adding the expires: n option (set n to 0/false to have disable expiration). Here is the relevant entry in the Moneta README.

How to set application_name with sinatra/sequel

I'm setting application_name in my connection string like so:
DB = Sequel.connect(:adapter=>'postgres', :host=> 'localhost',
:database=>'blah', :user=>'gator',
:application_name => 'blahwebapp')
However, when I view the pg_stat_activity or any other metric to filter down by application_name I'm not seeing it assigned properly.
My Pg gem is "pg (0.17.0)" and I believe that since 0.16 it's been able to handle the application name. sequel_pg is (1.6.8) and Sequel is (4.2.0).
An application name is set, but it is path/webserver related rather than what is set in the config:
/Users/gator/.rvm/gems/rub...47#blahwebapp/bin/shotgun
Even when I use a URL-type connection string it still doesn't register the application_name:
DB = Sequel.connect('postgres://gator#localhost/blah?application_name=blahwebapp')
That is the same URL I use in psql to connect and it shows up fine.
In the documentation for Sequel I don't see much about application_name as an option which has me worried:
Any thoughts/ideas on how to get it to respect the application_name?
Sequel's PostgreSQL adapter doesn't pass the URL directly to PostgreSQL. It can't really do so and keep backwards compatibility.
It looks like you can just set the application_name at runtime. The best way to do this with Sequel is via after_connect:
DB = Sequel.connect('postgres://gator#localhost/blah', :after_connect=>proc{|c| c.execute("SET application_name TO 'blahwebapp'")})
It is possible to integrate this feature into Sequel so that using it inside the 'postgres://' URL would work correctly. I'm open to that if other people think it would be useful.

Difference in cookie content using Sinatra session & Rack::Session::EncryptedCookie

I'm learning Sinatra framework & developing a login system. I came across two ways of using cookies.
A simple Sinatra inbuilt way:
enable :sessions
set :session_secret, 'random-key'
This approach produces following cookie content while logged in (used session.inspect to get the output):
{"session_id"=>"6be0b9a31831604ba51114d265ba952482e0b2da6ced6c54e15ebe7f212858ca",
"tracking"=>{"HTTP_USER_AGENT"=>"b8c1e8f89eeaea0b825bed0d811f0c7678e98c74",
"HTTP_ACCEPT_ENCODING"=>"a0bfc876d68fe7aea700da5ea8925abac6f2f794",
"HTTP_ACCEPT_LANGUAGE"=>"dd065ed263c67d799f943ab6c39b55c5e008cbb5"},
"csrf"=>"b480324f510e4f391d15cee8236a8fb74a5aaa5ce2f9ad38e4dbb025a823b16e",
"name"=>"john"}
Another approach is using an encrypted cookie :
require 'sinatra'
require 'encrypted_cookie'
use Rack::Session::EncryptedCookie, :secret => "random-key"
But this approach produces following cookie content while logged in (used session.inspect here too):
{:name=>"john"}
Why enable :sessions is creating such a big cookie with all that information & why is it required (especially those HTTP_... parts?) Because Rack::Session::EncryptedCookie isn't generating any of it.
Do you think that using enable :sessions should be preferred because it has csrf token & session id? Or do you think that Rack::Session::EncryptedCookie is enough since it is encrypted?
I have following versions of gems installed :
encrypted_cookie (0.0.4)
rack (1.5.2)
rack_csrf (2.4.0)
sinatra (1.4.3)
thin (1.5.1)
Please tell me if you need more information...
Because Sinatra will use rack-protection middleware when you enable :sessions. It makes cookie bigger but more secure.
Relevant snippet:
def setup_default_middleware(builder)
builder.use ExtendedRack
builder.use ShowExceptions if show_exceptions?
builder.use Rack::MethodOverride if method_override?
builder.use Rack::Head
setup_logging builder
setup_sessions builder
setup_protection builder
end
def setup_sessions(builder)
return unless sessions?
options = {}
options[:secret] = session_secret if session_secret?
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
builder.use session_store, options
end
def setup_protection(builder)
return unless protection?
options = Hash === protection ? protection.dup : {}
options = {
img_src: "'self' data:",
font_src: "'self'"
}.merge options
protect_session = options.fetch(:session) { sessions? }
options[:without_session] = !protect_session
options[:reaction] ||= :drop_session
builder.use Rack::Protection, options
end
sessions? returns true if you enable :sessions
session_store is Rack::Session::Cookie by default
The difference between Rack::Session::EncryptedCookie
That is, if you want to use Rack::Session::EncryptedCookie with rack-production, it can be easily done by:
enable :sessions
set :session_store, Rack::Session::EncryptedCookie
FYI, since encrypted_cookie is lack of some features (secret rotation, custom serializer, etc) and no longer under maintenance, I made another one to replace it.
Hope it helps.
Because Rack::Session::EncryptedCookie requires that your secret be at least 16 bits long. In the README, they recommend using OpenSSL for generating the secret, like so:
ruby -ropenssl -e "puts OpenSSL::Random.random_bytes(16).inspect"
If you open your inspector, you'll see a cookie named 'rack.session', and its contents obfuscated.
As I know, when using Rack::Session::Cookie in Sinatra, and write session_secret as an environment variable, the session which has created won't destroy after the project deploy. I think this is a risk in Single Page Application.

Sinatra session members "disappearing"

I've successfully troubleshooted an issue with session members not being available even though they were set and would like to know why it's happening. My situation can be described as:
Sinatra app using :session.
Using oAuth to authorise users and in the process setting a :ret_url session member so that the app knows where to come back to after auth.
Server is unicorn on Cedar stack (Heroku)
This works perfectly whilst running locally but the :ret_url session member was completely disappearing from the session on Heroku. I found that if I removed this code it fixed the problem:
before do
cache_control :public, :must_revalidate, :max_age => 60
end
Question 1: I'm guessing that my cookie was being cached without the :ret_url value and that's why it was breaking?
Question 2: I was setting the session member as shown in the route condition code below, is this the wrong place to do it?
# redirect users to login if necessary
set(:auth) do |access_token|
condition do
if request.request_method == 'GET'
session[:ret_url] = request.path_info
end
redirect '/' unless user_logged_in?
end
end
I'd like to use cacheing and still have my cookie be valid.
Hard to see what is going on without knowing all details, but there is a simple rule that you are most probably violating: do not use http caching on actions that are supposed to do something (other than just show page). When http caching is on, you browser does not even try to re-load the page and renders it from browser cache.
Cookies are not cached anywhere, the only thing cache_control does is setting CacheControl http response value
In your case the best thing you can do is to add list of routes that have no-action pages to your before block:
before '/my/static/page' do
cache_control :public, :must_revalidate, :max_age => 60
end
Most probably you will have very limited set of routes where you can benefit from http caching
A chap by the name of Ari Brown (waves at Ari), who is not a member here but deserves the credit for this answer, pointed me at the right solution, which is, as per the Sinatra FAQ, to not use enable :sessions but to use Rack::Session::Cookie as per
use Rack::Session::Cookie, :key => 'rack.session',
:domain => 'foo.com',
:path => '/',
:expire_after => 2592000, # In seconds
:secret => 'change_me'
I've added this into my config.ru and all is well.
I also noticed over in this post the alternative suggestion to set :session_secret, 'change_me' and, indeed, to do this via an environment variable, namely:
$ heroku config:add SESSION_KEY=a_longish_secret_key
then in your app
enable :sessions
set :session_secret, ENV['SESSION_KEY'] || 'change_me'
Obviously you can use the environment variable strategy with the Rack::Session::Cookie approach too. That's the way I went as it offers more flexibility in configuration.
The reason these work is that the cache controller middleware is farming requests out to multiple server instances and without setting a session secret it's just making one up per server, and thus breaking the sessions.

Resources