Catalyst/DBICでDigest認証する
自分の為のメモですよ。
準備
まずはCatalystプロジェクトを作ります
$ mkdir -p /path/to/dir $ cd /path/to/dir $ catalyst.pl AuthSample
ユーザー用のDBICスキーマを定義します。
$ module-starter --module AuthSample::Schema $ cd AuthSample-Schema
でこのディレクトリにて、
CREATE TABLE user ( user_seq INTEGER PRIMARY KEY, user_id TEXT UNIQUE, password TEXT, created_on DATETIME, updated_on DATETIME );
こんなスキーマを定義して、schema.sqlとして保存して、
$ sqlite3 -init schema.sql authsample.db
として初期化する。
次にスキーマ生成の為の簡易スクリプトをtypesterさんの奴をベースに ./script/schema.pl として作る。
#!/usr/bin/perl use strict; use warnings; use FindBin; use File::Spec; use lib ( File::Spec->catfile( $FindBin::Bin, qw/.. lib/ ), File::Spec->catfile( $FindBin::Bin, qw/.. schema/ ) ); use DBIx::Class::Schema::Loader qw(make_schema_at); die('Required arguments dsn dbuser dbpass') unless (@ARGV); my $schema_class = 'AuthSample::Schema'; unlink( glob( File::Spec->catdir( $FindBin::Bin, '..', 'lib', split( /::/, $schema_class ) ) . '/*.pm' ) ); make_schema_at( $schema_class, { components => [ qw/ResultSetManager UTF8Columns InflateColumn::DateTime TimeStamp DigestColumns/ ], dump_directory => File::Spec->catfile( $FindBin::Bin, qw/.. lib/ ), debug => 1, really_erase_my_files => 0, }, \@ARGV, );
こんな感じ。改良点は、
- 各テーブルクラスは自分で自ら消す
- really_erase_my_files を 0 にしてるので Schema::Loader 自身は何も消さない
- つまり Schema クラスは残ったままで、自由に編集出来る
みたいな点。詳しくはCatalystConで話す。
実行権限を与えて、さらに事前にmodule-starterでSchemaクラスが出来ちゃってるからそれを消してからschema.plを実行する。
$ chmod +x ./script/schema.pl $ rm -f ./lib/AuthSample/Schema.pm $ ./script/schema.pl dbi:SQLite:dbname=authsample.db
これでひな形は完成。
DBIC関連を今回は手動でちょっと弄る
*** lib/AuthSample/Schema/User.pm.orig 2008-04-15 11:49:27.000000000 +0900
--- lib/AuthSample/Schema/User.pm 2008-04-15 11:54:33.000000000 +0900
***************
*** 5,10 ****
--- 5,13 ----
use base 'DBIx::Class';
+ __PACKAGE__->mk_classdata('digest_user_name_column');
+ __PACKAGE__->mk_classdata('digest_realm' => '');
+
__PACKAGE__->load_components(
"ResultSetManager",
"UTF8Columns",
***************
*** 20,34 ****
"user_id",
{ data_type => "TEXT", is_nullable => 0, size => undef },
"password",
! { data_type => "TEXT", is_nullable => 0, size => undef },
"created_on",
! { data_type => "DATETIME", is_nullable => 0, size => undef },
"updated_on",
! { data_type => "DATETIME", is_nullable => 0, size => undef },
);
__PACKAGE__->set_primary_key("user_seq");
__PACKAGE__->add_unique_constraint("user_id_unique", ["user_id"]);
# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-04-15 11:39:32
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7O29VfwnVCsyZL2avThORA
--- 23,61 ----
"user_id",
{ data_type => "TEXT", is_nullable => 0, size => undef },
"password",
! { data_type => "TEXT", is_nullable => 0, size => undef, digest_check_method => 'check_password' },
"created_on",
! { data_type => "DATETIME", is_nullable => 0, size => undef, set_on_create => 1, },
"updated_on",
! { data_type => "DATETIME", is_nullable => 0, size => undef, set_on_create => 1, set_on_update => 1 },
);
__PACKAGE__->set_primary_key("user_seq");
__PACKAGE__->add_unique_constraint("user_id_unique", ["user_id"]);
+ __PACKAGE__->digestcolumns(
+ columns => [qw/password/],
+ algorithm => 'MD5',
+ encoding => 'hex',
+ auto => 1,
+ dirty => 1,
+ );
+
+ __PACKAGE__->digest_user_name_column('user_id');
+ __PACKAGE__->digest_realm('Are you TKSK?');
+
+ sub _get_digest_string {
+ my ($self, $value) = @_;
+
+ $self->digest_maker->reset;
+
+ return $self->next::method(
+ join(':',
+ $self->get_column($self->digest_user_name_column) || '',
+ $self->digest_realm,
+ $value || ''
+ )
+ );
+ }
# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-04-15 11:39:32
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7O29VfwnVCsyZL2avThORA主な変更点は、
- DigestColumnsを使ってDigest認証用のハッシュ値を格納するようにした
- TimeStampが勝手に挿入されるようにした
って感じ。
と言う訳で試してみる。
#!/usr/bin/perl use strict; use warnings; use lib qw(lib); use AuthSample::Schema; my $schema = AuthSample::Schema->connect('dbi:SQLite:dbname=authsample.db'); $schema->resultset('User')->create({ user_id => 'zigorou', password => 'hogehoge' }); print $schema->find({ user_id => 'zigorou' })->password;
とすると、
c11485a6cebc09f30a61df78a33961df
などと出るので、どうやら問題なく動作している模様。念のためSQLiteコンソールでも確かめる。
sqlite> SELECT * FROM user; 1|zigorou|c11485a6cebc09f30a61df78a33961df|2008-04-15 02:59:12|2008-04-15 02:59:12
これでDBICはとりあえず出来た。
Catalyst側の実装とか設定とか
まずは最初にauthsample_server.plにモジュールのパスを追加しておきます。
use lib ( "$FindBin::Bin/../lib", glob("$FindBin::Bin/../../*/lib") );
こういう感じ。
今度はCatalyst側に移動して、./lib/AuthSample.pmの use Catalyst してる部分を次のように。
use Catalyst qw/ -Debug ConfigLoader Static::Simple Cache Authentication Authentication::Store::DBIC Authentication::Credential::HTTP /;
次にCatalyst::Model::AdaptorでDBIC::Schemaのadaptorを作ります。
$ ./script/authsample_create.pl model DBIC::Schema Adaptor AuthSample::Schema
出来上がったadaptorにprepare_arguments, mangle_argumentsを追加します。Catalyst::Utilsをuseしておく必要があります。
ついでにconfigのconstructorもnewからconnectに変更する。
__PACKAGE__->config( class => 'AuthSample::Schema', constructor => 'connect', ); sub prepare_arguments { my ($self, $app) = @_; return $app->config->{Catalyst::Utils::class2classsuffix(__PACKAGE__)}; } sub mangle_arguments { my ($self, $args) = @_; return $args ? @$args : (); }
さらにAuthentication::Store::DBIC用にUserクラスを作ります。
$ ./script/authsample_create.pl model DBIC::Schema::User
生成されたModel::DBIC::Schema::UserにACCEPT_CONTEXT()を次のように追加する。
sub ACCEPT_CONTEXT { my ($self, $c, @args) = @_; return $c->model('DBIC::Schema')->resultset('User'); }
さらにyamlを次のように設定する。
------
Model::DBIC::Schema:
- dbi:SQLite:dbname=/Users/zigorou/tmp/digestauth/AuthSample-Schema/authsample.db
authentication:
dbic:
password_field: password
password_hash_type: MD5
password_type: clear
user_class: DBIC::Schema::User
user_field: user_id
http:
algorithm: MD5
type: digest
cache:
backend:
class: Cache::Memory
default_expire: 600 sec
namespace: test
name: AuthSampleこれで準備完了です。
$ ./script/authsample_server.pl -d -r
とかで動くはず。
認証ページを作る
面倒なのでController/Root.pmに仕込みます。
sub default : Private { my ( $self, $c ) = @_; $c->authorization_required( realm => 'Are you TKSK?' ); # Hello World $c->response->body( $c->welcome_message ); }
これで http://localhost:3000/ にアクセスすれば認証ページが出るはずです。
おしまい。