Devise を知るにはまず Warden を知るが良い
背景
devise という gem があるが、warden をベースにした認証の仕組みだということだった。
devise を知るには、まず warden を知らなければなるまい。
そう思った俺は warnden を rack ベースでいじってみることにした。
warden とは
これのこと。
https://github.com/hassox/warden
wiki が充実していたのでかなりわかりやすかった。
https://github.com/hassox/warden/wiki
とりあえずやってみる、以下メモ書き。
config.rb
require 'rack'
require 'pp'
require 'warden'
class Application
def call(env)
request = Rack::Request.new(env)
response = if request.path_info = '/'
body = "#{request.request_method}: Hello! #{request.params['name']}!"
Rack::Response.new(body, "200", {'Content-Type' => 'text/plain'})
else
Rack::Response.new('Not Found', "404", {'Content-Type' => 'text/plain'})
end
response.finish
end
end
app = Rack::Builder.new do
use Rack::Session::Cookie, :secret => "replace this with some secret key"
use Warden::Manager do |manager|
manager.default_strategies :password, :basic
#manager.failure_app = BadAuthenticationEndsUpHere
end
run lambda { |env| Application.new().call(env) }
end
run app
基本的に, default_strategies で定義した順に行われて、一つでも成功したら成功になる模様。
cookie セットされている
試しに nil を返した時。
明らかに error ハンドラがないぞッて感じのエラーが出ているので、
雰囲気でBadAuthenticationEndsUpHere クラスを足してみる。
class BadAuthenticationEndsUpHere
def self.call(env)
p env
p "BadAuthenticationEndsUpHere"
Rack::Response.new('Not Found', "404", {'Content-Type' => 'text/plain'})
end
end
思い通りだ。 error ハンドラとして BadAuthenticationEndsUpHere クラスが呼ばれた。
それでは、正しく期待している serialize_from_session が動くように User.get を定義する。
def get(id)
## 永続化されたものから引いてうまく引けたと過程して返す
return User.new(id)
end
1回目、とりあえずログインされるはず。
サーバのデバッグログ
“Authenticate!!!!!!!!!!!!”
“serialize into session!!!!!!!!!!!!”
ログから、セッションに値が仕込まれた模様。
2回目、セッションから値を引いてみる。
あれ、ミス。。あ、 serialize_from_session が読んでるのは User.get(id) だから class method だった。。
修正して再チャレンジ。
ちゃんときたっぽい!
デバッグログ
“Authenticate!!!!!!!!!!!!”
“serialize from session!!!!!!!!!!!!”
127.0.0.1 - - [01/Mar/2014 18:54:54] “GET /?password=hoge HTTP/1.1” 200 13 0.0022
get されている模様!
これで user にアクセスできる。
p "warden user is "
p env['warden'].user
ドキュメントを読むと、
Warden::Strategies で定義した #valid? に関しては、定義されてようがなかろうが呼ばれるらしい。
試しにこの時点で #valid? で nil を返すと次の strategy にチャレンジがうつった。
すべての strategy で失敗したら、 failure_app が呼ばれる。
特筆すべきは、ここで呼ばれるのが /unauthenticated だということだ。
この url http://localhost:9292/?password=hoge を叩くと
127.0.0.1 - - [01/Mar/2014 19:01:45] “GET /unauthenticated?password=hoge HTTP/1.1” 404 9 0.0012
がデバッグログに表示されている。
class BadAuthenticationEndsUpHere
def self.call(env)
p "in BadAuthenticationEndsUpHere!!!!!!!!!!!!"
request = Rack::Request.new(env)
p "request.path_info is "
p request.path_info
Rack::Response.new('Not Found', "404", {'Content-Type' => 'text/plain'})
end
end
をデバッグとして試しに書いてみた。
やはり、内部的に path_info を unauthenticated に書き換えているようだ。
これは lib/warden/manager.rb のあたりを見ればわかる。
def process_unauthenticated(env, options={})
や
def call_failure_app(env, options = {})
で実際にそのように書かれている。
と思ったら
https://github.com/hassox/warden/wiki/Failures
ここにめっちゃそのように書いてあった。
halt! や success など便利なメソッドが strategy で使える模様。
https://github.com/hassox/warden/wiki/Strategies
詳細はコチラを参照。
このような認証の仕組みが rack で使えて、 認証自体が strategy パターンになっているので
いい感じに他の人が作った strategy を使えるようになっている。
https://github.com/hassox/warden/wiki/Setup#advanced-setup-with-scopes
これを見ると、warden は、認証の scope を分けられるようになっている。
それぞれの認証で、違うロジックを定義して、認証できるようだ。
特に指定がない場合は、 env[‘warden’] は user として認証する。
しかし、これは上書きができるようだ。
default の設定上書きとは別に、明示的にスコープを指定することもできる。
env['warden'].authenticate! :scope => :api
callback
"serialize into session!!!!!!!!!!!!"
"after_set_user caled!"
"auth is"
Warden::Proxy:70106638122900 @config={:default_scope=>:default, :scope_defaults=>{}, :default_strategies=>{:_all=>[:password, :basic]}, :intercept_401=>true, :failure_app=>BadAuthenticationEndsUpHere}
"logout called!!!!!!!!!!!"
"in BadAuthenticationEndsUpHere!!!!!!!!!!!!"
"request.path_info is "
"/unauthenticated"
127.0.0.1 - - [01/Mar/2014 19:55:10] "GET /unauthenticated?password=hoge HTTP/1.1" 404 9 0.0025
これも結構参考になる。
https://gist.github.com/lukesutton/107966
所感
warden 大体わかった。