问题
I have a Sinatra app that's mounted on a Rails app under /admin
. The Sinatra app is an admin dashboard, and therefore should only be available to authorized users.
To enforce that, I built a piece of Rack Middleware that will run before the Sinatra app is called.
The logic is simple -
- If user is authenticated, continue as normal
- If user is not authenticated, redirect to the root path with a flash alert message (I'm using the rack-flash gem to allow access to the flash messages in Rack)
Code below. I feel like I'm missing something in the redirect
method. The Rack::Builder
block constructs a mini-Rack application and the block inside further creates another Rack application (the Proc) that builds the Redirect Response with flash message.
When I run it, I get undefined method 'detect' for nil:NilClass
, which indicates that neither block is returning a valid non-nil
response. Do I need to run call
somewhere on one of these blocks?
I'm using a Puma Webserver if that helps.
Thanks!
require "rack"
require "rack-flash"
class AdminAuthorizer
def initialize(app)
@app = app
end
def call(env)
@env = env
id = @env["rack.session"][:user_id]
user = User.where(id: id).first
# Check if user is authorized, otherwise redirect
user.admin? ? ok : redirect
end
private
def ok
@app.call(@env)
end
def redirect
Rack::Builder.new do
use Rack::Flash, sweep: true, accessorize: true
run(
Proc.new do |env|
env["x-rack.flash"].alert = "Insufficient permissions"
res = Rack::Response.new
res.redirect("/")
res.finish
end
)
end
end
end
回答1:
Ok, figured it out myself for anyone else that's curious.
I had to use the env
key 'action_dispatch.request.flash_hash'
, which is used by the Flash middelware here
I didn't have to use the rack-flash
gem, although I'm sure that's still useful when building Sinatra apps and such
NOTE: This is on Rails v4.2.4. I believe there have been several changes to that Flash
module since, so I don't know if that key has changed. But you can confirm by searching the latest repo for a similar definition.
require "rack"
class AdminAuthorizer
FLASH = ActionDispatch::Flash
def initialize(app)
@app = app
end
def call(env)
@env = env
id = @env["rack.session"][:user_id]
user = User.where(id: id).first
# Check if user is authorized, otherwise redirect
user.admin? ? ok : redirect
end
private
def ok
@app.call(@env)
end
def redirect
# Calls a Rack application (the defined Proc). If you want to do more steps
# or get fancier, you can wrap this in a Rack::Builder call
#
# Rack::Builder.app(redirect_proc)
# use (blah)
# run (blah)
# end.call(@env)
#
redirect_proc.call(@env)
end
def redirect_proc
Proc.new do |env|
# Use the key 'action_dispatch.request.flash_hash' to set
# an new FlashHash object. This will overrite any existing FlashHash
# object, so use it carefully. You can probably avoid that by checking
# for an existing one and adding to it.
env[FLASH::KEY] = FLASH::FlashHash.new(alert: "Insufficient permissions")
# Construct the redirect response
res = Rack::Response.new
res.redirect("/")
res.finish
end
end
end
来源:https://stackoverflow.com/questions/32954916/building-rack-middleware-responses-with-flash-message-functionality