I’m looking at the Rack::Staging middleware today. This middleware looks like a great idea for staging servers, since it requires all requests to have a valid staging code (cookie). If a user doesn’t have one yet, you can create an HTML page that lets them submit their staging code.
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# Hi! I'am rack middleware! # I was born for protect you staging application from anonymous and prying # My author's name was Aleksandr Koss. Mail him at kossnocorp@gmail.com # Nice to MIT you! module Rack class Staging def initialize app, options = {} @app = app @options = { :session_key => :staging_auth, :code_param => :code }.merge options end def call env request = Rack::Request.new env # Check code in session and return Rails call if is valid return @app.call env if \ request.session[@options[:session_key]] != nil and request.session[@options[:session_key]] != '' and code_valid? request.session[@options[:session_key]] # If post method check :code_param value if request.post? and code_valid? request.params[@options[:code_param].to_s] request.session[@options[:session_key]] = request.params[@options[:code_param].to_s] [301, {'Location' => '/'}, ''] # Redirect if code is valid else render_staging end end private # Render staging html file def render_staging [200, {'Content-Type' => 'text/html'}, [ ::File.open(@options[:template_path], 'rb').read ]] end # Validate code def code_valid? code @options[:auth_codes].include? code end end end |
Review
There are three different cases I want to review in Rack::Staging.
1. No staging code
1 2 3 4 5 |
def render_staging [200, {'Content-Type' => 'text/html'}, [ ::File.open(@options[:template_path], 'rb').read ]] end |
This is the first case a user will run into. It happens when they don’t have a staging code in their session yet. The request ends up falling through most of #call
until render_staging
is reached. render_staging
then reads the HTML file and sends it back to the browser.
2. Posting a new staging code
1 2 3 4 5 6 |
if request.post? and code_valid? request.params[@options[:code_param].to_s] request.session[@options[:session_key]] = request.params[@options[:code_param].to_s] [301, {'Location' => '/'}, ''] # Redirect if code is valid else # ... end |
When a new staging code is posted from the form, it runs the code above. If the staging code posted is valid, Rack::Staging will then set the staging code in the session and redirect back to the root.
3. Responding to a request that has a staging code
1 2 3 4 |
return @app.call env if \ request.session[@options[:session_key]] != nil and request.session[@options[:session_key]] != '' and code_valid? request.session[@options[:session_key]] |
The final case happens when a request already has a valid staging code. This would send the request to the next application in the Rack stack, letting the final app handle the response like normal.
I think Rack::Staging would be very useful. I’m using some HTTP basic authentication to block access to the different staging sites and it’s clunky at best. I’d like to see Rack::Staging have some tests added, I think there is an edge case that might not be handled correctly.