質問

I'm writing a Sinatra Rack App and I want to use Warden for authentication. I'm using heroku's toolbelt so I use foreman to run my app. I've found some code that's presumably supposed to get this working. Unfortunately, when I attempt to actually access the Warden env object, it is nil.

I've attempted to use the sinatra_warden gem, but it also has its own bugs (might be related to this one).

config.ru:

require './web.rb'
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
run MyApp

web.rb:

require 'sinatra'
require 'warden'
require 'data_mapper'

require './config/datamapper.rb'
require './config/warden.rb' # I've tried this inside of MyApp, still didn't work

class MyApp < Sinatra::Base

  get '/test' do
    env['warden'].authenticate! # env['warden'] is nil :(
  end

end

config/warden.rb:

use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']

use Warden::Manager do |manager|
  manager.default_strategies :password
  manager.failure_app = MyApp.new
end

Warden::Manager.serialize_into_session { |user| user.id }
Warden::Manager.serialize_from_session { |id| User.get(id) }

Warden::Manager.before_failure do |env,opts|
  # Sinatra is very sensitive to the request method
  # since authentication could fail on any type of method, we need
  # to set it for the failure app so it is routed to the correct block
  env['REQUEST_METHOD'] = "POST"
end

Warden::Strategies.add(:password) do
  def valid?
    params["email"] || params["password"]
  end

  def authenticate!
    u = User.authenticate(params["email"], params["password"])
    u.nil? ? fail!("Could not log in") : success!(u)
  end
end

Versions:

  • Sinatra: 1.1.0
  • Warden: 1.2.1
  • Rack: 1.4.1
  • Ruby: 1.9.3p194
  • Foreman: 0.60.0

Any ideas how to use Warden the set up I've described?

(P.S. Out of curiosity, what exactly is the env variable?)

役に立ちましたか?

解決

Rack internally uses the class Rack::Builder to parse your config.ru file and wrap directives to build up the middleware components.

I believe your builder calls to use in config/warden.rb are getting ignored. It may work to remove the directives from that file and add them to the middleware stack in config.ru:

require './web.rb'

use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']

use Warden::Manager do |manager|
  manager.default_strategies :password
  manager.failure_app = MyApp.new
end

use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"

run MyApp

他のヒント

Put a link to your config/warden in your config.ru

require File.dirname(__FILE__) + '/config/warden'

Read the warden readme. Or look right in the lib/warden.rb

I put

Warden.test_mode!

in place of the env call at the /test path and get a nice blank page at

http://localhost:9292/test

Some bloggers have stated that there isn't a lot of documentation for warden but I disagree. There is a whole wiki. see https://github.com/hassox/warden/wiki

Take it slow and find out how to use middleware in Rack. Here's a very good article https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware

I think maybe you might want to start out with tests as I found a good example and you could use it with your app.

ENV['RACK_ENV'] = 'test'
require 'test/unit'
require 'rack/test'

require File.dirname(__FILE__) + '/web'

class AuthenticationTest < Test::Unit::TestCase
  include Rack::Test::Methods

  def app
    WardenTest #MyApp
  end

  def test_without_authentication
    get '/protected'
    assert_equal 401, last_response.status
  end

  def test_with_bad_credentials
    authorize 'bad', 'boy'
    get '/protected'
    assert_equal 401, last_response.status
  end

  def test_with_proper_credentials
    authorize 'admin', 'admin'
    get '/protected'
    assert_equal 200, last_response.status
    assert_equal "You're welcome, authenticated client", last_response.body
  end
end

Then a few routes added to your app.

helpers do
  def protected!
    return if authorized?
    headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
    halt 401, "Not authorized\n"
  end

  def authorized?
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    @auth.provided? and @auth.basic? and @auth.credentials and     
    @auth.credentials == ['admin', 'admin']
  end
end

get '/' do
  "Everybody can see this page"
end

get '/protected' do
  protected!
  "You're welcome, authenticated client"
end 

In my experience working with Ruby, it's always a good idea to start out with tests for any new project. I often test little pieces first though just to gain an understanding of how they work.

Once you get a better understanding of Rack, especially Rack::Builder, you can use

map '/test' do
  ...all the middleware needed
  run App
end

and try out different configurations to see which ones work best for your needs as I'm doing while I write this.

Enjoy! ;-)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top