Amon2::Plugin::Web::Auth::PathというPluginを書いた
ソースはこちら。トルネで録画したJOJOの映像が乱れまくって辛いです。
https://github.com/rymizuki/p5-Amon2-Web-Auth-Path
頑張って短くした前置き
Amon2にはWeb::Authという認証をサポートするプラグインがあります。が、それらは外部の認証自身のアプリケーションで使うためのツールであり、アプリケーションのページ単位での認証をおこなってくれません。
例えば、トップページは認証をかけない、マイページはかける。お問い合わせページはかけない、という細かい制御が難しい。
BEFORE_DISPATCHでPATH毎、あるいはController毎に管理するとか、Routerのany使うとかやりようはありますが、アプリの規模が膨らめば膨らむ程コードも膨らむ。可読性も下がるしあまりよろしくない。
Amon2::Plugin::Web::Auth::PathはPATH単位でBEFORE_DISPATCHを走らせて、認証の処理をContextから分離したものです。Pluginのinit時にpathsをArrayRefで渡すか、moduleを渡すかの選択ができます。
少ない認証だけでしたら、pathsでも十分なのですが、僕の要求ではWebContextの行数が倍に膨れ上がる複雑な処理だったので、出来ればそこは切り出したい、と思って外部Moduleを呼べるようにしました。ついでにDSLなんかをつくってみました。
そんなわけで、
Router::Simpleでの実装は、@takuji31さんのAmon2::Web::Authorizerを参考にさせて頂きました。
https://github.com/takuji31/Amon2-Web-Authorizer
処理の中で、認証が通ったか通ってないかの判断を明示的にするために$auth->success,$auth->failedのメソッドを呼ぶといいんじゃない?とid:karupaneruraにアドバイスいただいてまんま実装しました。
つかいかた
package YourApp::Web; use Amon2::Lite; get '/' => sub { my $c = shift; }; get '/mypage' => sub { my $c = shift; }; __PACKAGE__->load_plugins( 'Web::Auth' => +{ module => 'Twitter', ... }, 'Web::Auth::Path' => sub { paths => [ '/' => sub { $_[1]->success }, qr{^/auth} => sub { $_[1]->success }, qr{^/mypage} => sub { my ($c, $auth,) = @_; if ($c->session->get('is_login')) { $auth->success; } else { $auth->failed; # redirect to Auth::Site::Twitter return $c->redirect('/auth/twitter/authenticate'); }, }, ], }, ); 1;
callbackにはWebContext, Plack::Util::initline_objectのインスタンス, Router::Simpleのmatchが渡されます。
callbackの中では$auth->succes/$auth->failedを呼びましょう。呼ばないと死にます。Appが。
callbackの返り値があればそのままreturnするので(Plack::Responseだけに固定したほうが良いのだろうか)$c->redirect, $c->render等ご自由に。after_dispatchにも影響します。controllerが走らないだけなの。
Router::Simpleつかってるのでパスの指定はRouter::Simpleのままつかえます。
モジュールを使う場合
こんなモジュールを書いて
package MyApp::Auth::Path::App; use Amon2::Auth::Path::Declare; path '/' => sub { $_[1]->success }; 1;
WebContextではこんな感じでパラメータ渡してやる。
__PACKAGE__->load_plugins( 'Web::Auth::Path', { module => 'MyApp::Auth::Path::App', }, );
以下はpathsと同様。