Sinatra + Rack::Session::Pool + Moneta - session

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.

Related

Sinatra Session Caching

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.

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.

How do I access Thin::Connection from inside rack middleware?

I would like to run set_comm_inactivity_timeout(0) on my EM Connection from inside Rack Middleware.
This will allow me to have one timeout for my upgraded web sockets and another for the rails app.
My web sockets are all first in the middleware chain so it is not wreaking havoc with Rack::Lock and such.
I see that some simply go for an infinite timeout in Thin and then inject something like Rack::Timeout after.
Is there a way to access EM::Connection or the signature of the connection from Rack middleware?
Only way to do this is monkey patch thin:
class Thin::Connection
alias :thin_process :process
def process
set_comm_inactivity_timeout(0)
thin_process
end
end

How do I set/get session vars in a Rack app?

use Rack::Session::Pool
...
session[:msg]="Hello Rack"
EDIT: The word session doesn't seem to resolve. I included the Session pool middleware in my config.ru, and try to set a variable in an ERB file (I'm using Ruby Serve) and it complains "undefined local variable or method `session'"
Thanks!
session is a method that is part of some web frameworks, for example Sinatra and Rails both have session methods. Plain rack applications don’t have a session method, unless you add one yourself.
The session hash is stored in the rack env hash under the key rack.session, so you can access it like this (assuming you’ve named the rack environment to your app env):
env['rack.session'][:msg]="Hello Rack"
Alternatively, you could use Rack’s built in request object, like this:
request = Rack::Request.new(env)
request.session[:msg]="Hello Rack"
You need to load rack::session module next probably cookie like here
http://rack.rubyforge.org/doc/classes/Rack/Session/Cookie.html
This like explains it with example.

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