Making Rails 4 and Sinatra share a Session
In our latest project, I wanted to restrict access to the
resque web interface by reusing
based authorization stack of the main Rails app. I quickly found
explaining how to mount the resque Sinatra app side by side with the
Rails app in the
config.ru file. Still, I always ended up with an
env['rack.session'] inside the Sinatra app.
As it turns out, Rails 4 changes the session middleware to use
encrypted cookies. It no longer relies on
Unfortunately, the new
CookieStore middleware cannot live on its own
ActionDispatch::Cookies::CookieJar it uses to get cookies
to be present on the
env. If it is missing, the session cookie
cannot be decrypted leading to silent failure.
Rails::Application objects place the key generator on an
merged into the request's env
before processing a request. To make the Rails session available to
our Sinatra app, we have to insert a middleware which mimics this
# rails_env_config_middleware.rb class RailsEnvConfigMiddleware def initialize(app) @app = app end def call(env) env.merge!(Rails.application.env_config) @app.call(env) end end
The final rack up file then looks like this:
# config.ru require ::File.expand_path('../config/environment', __FILE__) map '/' do run Rails.application end map '/resque' do # This is the important line use RailsEnvConfigMiddleware use ActionDispatch::Session::CookieStore, :key => '_<app_name>_session', :path => '/', :secret => '<secret_key_base>' use Warden::Manager do |manager| manager.failure_app = Pageflow::Application manager.default_scope = Devise.default_scope end run AdminResqueServer.new end
Now we can access the authenticated user via warden and authorize with CanCan:
# admin_resque_server.rb require 'resque/server' require 'resque_scheduler/server' class AdminResqueServer < Resque::Server before do redirect '/admin/login' unless authenticated? redirect '/' unless can?(:manage, Resque) end private def can?(*args) Ability.new(user).can?(*args) end def authenticated? request.env['warden'].authenticated? end def user request.env['warden'].user end end
In our production environment, I faced a final bug. For some reason the
which comes as
the Sinatra stack kept
dropping the session
as a reaction to some falsely detected attack. Deactivating
protection inside the Sinatra app solved the issue for now.
class AdminResqueServer < Resque::Server disable :protection ... end
This is of course not the desired solution. I'll post an update once I have figured out the details.