2

我有一个安装在 Rails 应用程序下的 Sinatra 应用程序/admin。Sinatra 应用程序是一个管理仪表板,因此应该只对授权用户可用。

为了强制执行,我构建了一个 Rack Middleware,它将在调用 Sinatra 应用程序之前运行。

逻辑很简单——

  1. 如果用户通过身份验证,则照常继续
  2. 如果用户未通过身份验证,请使用 flash 警报消息重定向到根路径(我正在使用rack-flash gem 来允许访问 Rack 中的 flash 消息)

代码如下。我觉得我在redirect方法中遗漏了一些东西。该Rack::Builder块构造了一个迷你机架应用程序,内部块进一步创建另一个机架应用程序(Proc),该应用程序使用闪存消息构建重定向响应。

当我运行它时,我得到undefined method 'detect' for nil:NilClass,这表明两个块都没有返回有效的非nil响应。我需要call在这些块之一上的某个地方运行吗?

如果有帮助,我正在使用 Puma Webserver。

谢谢!

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
4

1 回答 1

3

好的,为其他好奇的人自己弄清楚了。

我必须使用这里的 Flash middelware使用的env密钥'action_dispatch.request.flash_hash'

我不必使用rack-flashgem,尽管我确信在构建 Sinatra 应用程序等时它仍然有用

注意:这是在 Rails v4.2.4 上。我相信从那以后该Flash模块已经发生了一些变化,所以我不知道那个键是否已经改变。但是您可以通过在最新的 repo 中搜索类似的定义来确认。

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
于 2015-10-05T21:23:25.340 回答