diff --git a/MANIFEST b/MANIFEST index f2cb08b..355f650 100644 --- a/MANIFEST +++ b/MANIFEST @@ -51,6 +51,7 @@ lib/Ark/Plugin/Authentication.pm lib/Ark/Plugin/Authentication/Backend.pm lib/Ark/Plugin/Authentication/Credential/OpenID.pm lib/Ark/Plugin/Authentication/Credential/Password.pm +lib/Ark/Plugin/Authentication/Store/Data/Model.pm lib/Ark/Plugin/Authentication/Store/DBIx/Class.pm lib/Ark/Plugin/Authentication/Store/Minimal.pm lib/Ark/Plugin/Authentication/Store/Model.pm @@ -95,7 +96,7 @@ t/path_to/lib/TestApp.pm t/plugin_authentication.t t/plugin_authentication_cred_password.t t/plugin_authentication_store_datamodel.t -t/plugin_authentication_store_datamodelfast.t +t/plugin_authentication_store_datamodelbykey.t t/plugin_authentication_store_dbixclass.t t/plugin_authentication_store_model.t t/plugin_core.t diff --git a/lib/Ark/Plugin/Authentication/Store/Data/Model.pm b/lib/Ark/Plugin/Authentication/Store/Data/Model.pm index fe5fde2..2db90c6 100644 --- a/lib/Ark/Plugin/Authentication/Store/Data/Model.pm +++ b/lib/Ark/Plugin/Authentication/Store/Data/Model.pm @@ -1,7 +1,7 @@ package Ark::Plugin::Authentication::Store::Data::Model; use Ark::Plugin 'Auth'; -our $VERSION = '0.01_00'; +our $VERSION = '0.04_00'; has 'data_model_model' => ( is => 'rw', @@ -9,7 +9,7 @@ has 'data_model_model' => ( lazy => 1, default => sub { my $self = shift; - $self->class_config->{model} || 'Data::Model'; + $self->class_config->{model} || 'DataModel'; }, ); @@ -33,6 +33,16 @@ has 'data_model_user_field' => ( }, ); +has 'data_model_by_key' => ( + is => 'rw', + isa => 'Bool', + lazy => 1, + default => sub { + my $self = shift; + $self->class_config->{by_key} || 0; + }, +); + around 'find_user' => sub { my $prev = shift->(@_); return $prev if $prev; @@ -45,15 +55,18 @@ around 'find_user' => sub { if ($model->can('find_user')) { $user = $model->find_user($id, $info); } + elsif ($self->data_model_by_key) { + $user = $model->lookup( $self->data_model_target => $id ); + } else { - my $iterator = $model->get( $self->data_model_target => { + my @users = $model->get( $self->data_model_target => { where => [ $self->data_model_user_field => $id, ], limit => 1, } ); - return unless $iterator; - $user = $iterator->next; + return unless @users; + $user = $users[0]; } if ($user) { @@ -85,20 +98,29 @@ around 'from_session' => sub { obj_builder => sub { my $model = $self->app->model( $self->data_model_model ); - $model->get( - $self->data_model_target => { - where => [ - $self->data_model_user_field => - $user->{hash}{ $self->data_model_user_field }, - ], - limit => 1, - } - )->next; + if ($self->data_model_by_key) { + return $model->lookup( + $self->data_model_target => + $user->{hash}{ $self->data_model_user_field } + ); + } + else { + return ( $model->get( + $self->data_model_target => { + where => [ + $self->data_model_user_field => + $user->{hash}{ $self->data_model_user_field }, + ], + limit => 1, + } + ) )[0]; + } }, ); }; 1; +__END__ =head1 NAME @@ -107,41 +129,41 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi =head1 VERSION -0.01_00 +0.04_00 =head1 SYNOPSIS -=head2 Application root +=head2 Application class package MyApp::Web; use Ark; use_plugins qw( - Session - Session::State::Cookie - Session::Store::Memory - Authentication Authentication::Credential::Password Authentication::Store::Data::Model + + Session + Session::State::Cookie + Session::Store::Memory ); # optional: as your pleasure... - conf 'Plugin::Authentication::Store::Data::Model' => { - model => 'Foobar', # *A - target => 'user', # *B (same as default) - user_field => 'name', # *C (default is 'username') - }; conf 'Plugin::Authentication::Credential::Password' => { - user_field => 'name', # *C (default is 'username') - password_field => 'password', # *D (same as default) - password_type => 'clear', # (same as default) + user_field => 'name', # *A (default is 'username') + password_field => 'password', # *B (same as default) + password_type => 'clear', # (same as default) + }; + conf 'Plugin::Authentication::Store::Data::Model' => { + model => 'Foobar', # *C (default is 'DataModel') + target => 'user', # *D (same as default) + user_field => 'name', # *A (default is 'username') }; 1; -=head2 Autentication controller +=head2 Autentication controller class package MyApp::Web::Controller::Authentication; use Ark 'Controller'; @@ -153,36 +175,27 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi sub login :Path('login') { my ($self, $c) = @_; - $c->detach( - $c->req->method ne 'POST' ? 'require_authentication' - : $c->authenticate($c->req->params) ? 'authorized' - # alternate: $c->authenticate({id => $id, pw => $pw}), etc. - : 'unauthorized' - ); - } - sub require_authentication :Private { - my ($self, $c) = @_; - - $c->view('MT')->template('authorization/form'); - } - sub authorized :Private { - my ($self, $c) = @_; - - $c->stash->{message} = 'Welcome, ' . $c->user->obj->name . '!!'; - $c->view('MT')->template('home'); - } - sub unauthorized :Private { - my ($self, $c) = @_; - - $c->stash->{message} = 'Invalid username or password'; - $c->view('MT')->template('authorization/form'); + if ($c->req->method eq 'POST') { + # e.g. $c->authenticate({name => $username, password => $password}) + if (my $user = $c->authenticate($c->req->params) { + $c->stash->{message} = 'Welcome, ' . $user->obj->name . '!!'; + $c->view('MT')->template('home'); + } + else { + $c->stash->{message} = 'Invalid username or password'; + $c->view('MT')->template('authorization/form'); + } + } + else { + $c->view('MT')->template('authorization/form'); # require login + } } 1; -=head2 Authentication model +=head2 Authentication model class - package MyApp::Web::Model::Foobar; # *A + package MyApp::Web::Model::Foobar; # *C use Ark 'Model::Adaptor'; __PACKAGE__->config( @@ -191,12 +204,12 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi 1; -=head2 Table schema of user table +=head2 Table schema class of user table package MyApp::Schema::Table::User; # *E use base qw(Data::Model); - use Data::Model::Schema sugar => 'myapp'; # *F use Data::Model::Driver::DBI; + use Data::Model::Schema sugar => 'myapp'; # *F use MyApp::Schema::Column::User; # *G my $dbfile = '/foo/bar.db'; @@ -205,12 +218,12 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi ); base_driver($driver); - install_model 'user' => schema { # *B + install_model 'user' => schema { # *D key 'id'; - column 'user.id'; - column 'user.name'; # *C + column 'user.id' => { auto_increment => 1 }; + column 'user.name'; # *A unique 'name'; - column 'user.password'; # *D + column 'user.password'; # *B # ... }; @@ -220,8 +233,10 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi sub find_user { my ($self, $id, $info) = @_; - my @users = $self->get( user => { # *B - name => lc $id, # ignore case + my @users = $self->get( user => { # *D + where => [ + name => lc $id, # ignore case + ], # you can use $info for any more conditions! } ); @@ -229,34 +244,33 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi return $users[0]; # found } -=head2 Column schema of user table + 1; + +=head2 Column schema class of user table package MyApp::Schema::Column::User; # *G use Data::Model::Schema sugar => 'myapp'; # *F column_sugar 'user.id' - => int => { + => integer => { require => 1, unsigned => 1, }; - column_sugar 'user.name' => # *C - varchar => { - require => 1, - size => 16, + column_sugar 'user.name' # *A + => text => { + require => 1, }; - column_sugar 'user.password' => # *D - varchar => { - require => 1, - size => 16, + column_sugar 'user.password' # *B + => text => { + require => 1, }; - # ... (common.last_updated_datetime, user.last_login_datetime, etc.) + # ... (common.last_updated_datetime, user.last_accessed_datetime, etc.) 1; -=head2 /root/authorization/form.mt - - [%=r $self->render('inc/header') %] +=head2 Authentication template file +

@@ -266,66 +280,119 @@ Ark::Plugin::Authentication::Store::Data::Model - Ark plugin for storing auth vi

- -

+

+ +

- [%=r $self->render('inc/footer') %] - =head1 DESCRIPTION This module is a plugin for L; to store authentication informations for any data by L. -=head2 Finding user +=head2 How to find an user -Default behavior of finding user is below: +Default behavior of finding an user is below: =over 4 -=item 1. Constructs C. -You can change the class-name into other one +=item 1 + +Constructs a model object of class named C. +You can change the class name into other one by C function (see L). -Most people will have the class delegete all method to -L's model. +Most people make the model class delegete all methods to +L's model by +L. + +=item 2 -=item 2. Finds row by C column from C model. -You can change these column name and model name into other ones +Finds user's row by the column (field) named C, from the model +named C. +You can change these column (field) name and model name into other ones by C function (see L). -You can also define C method for L's model -(in that case, this plugin use the method instead of plugin's procedure). +You can also implement C method for L's +model (in that case, this plugin use the method instead of plugin's procedure). + +=item 3 + +Returns L +object as C<< $user >> to application controller, when user is authorized +by any C plugin. +This will enable you to get L object +by C<< $user->obj >> method. -=item 3. Returns L object as C<< $user >>. -You can get L object to call -C<< $user->obj >> method. +=item 4 -=item 4. Retrieves L object as C<< $c->user >> over session, -as your pleasure (under C<< use_plugins qw(Session) >>). -You can get L object to call -C<< $c->user->obj >> method. +Retrieves L object as C<< $c->user >> to application +controller over a session, when you use C<< use_plugins qw(Session) >>. +You can get L object +by C<< $c->user->obj >> method. =back +=head2 How to find an user by key column (key field) -=head1 SEE ALSO +The plugin can find user's row by index +(so that means that the plugin use method C<< $model->lookup >>). +This will enable you to find user's row mostly faster about several dozen +persent (see L). -=over 4 +When you define a B column (key field) at schema... -=item L + install_model 'user' => schema { + key 'id'; # define "id" column as key + column 'user.id' => { auto_increment => 1 }; + column 'user.name'; + unique 'name'; + column 'user.password'; + # ... + }; -This plugin looks-up a row B. Maybe fast! +...and specify that C flag is true; + + conf 'Plugin::Authentication::Credential::Password' => { + user_field => 'id', # key column + password_field => 'password', + password_type => 'clear', + }; + conf 'Plugin::Authentication::Store::Data::Model' => { + model => 'Foobar', + target => 'user', + user_field => 'id', # key column + by_key => 1, # turn on! + }; + +you can have the plugin use the key column. + +
+

+ + +

+

+ + +

+

+ +

+
+ + +=head1 SEE ALSO + +=over 4 =item L This plugin looks-up a row from L's model. -=item L +=item L This plugin verifies password with user's row object. @@ -336,10 +403,11 @@ This plugin verifies password with user's row object. =over 4 -=item MURASE Daisuke ("typester") +=item Daisuke Murase ("typester") The author of L. +I stolen almost every codes from this plugin. =back @@ -348,7 +416,7 @@ Ark::Plugin::Authentication::Store::DBIx::Class>. =over 4 -=item MORIYA Masaki ("Gardejo") +=item MORIYA Masaki ("gardejo") C<< >>, L @@ -358,7 +426,7 @@ L =head1 LICENCE AND COPYRIGHT -Copyright (c) 2009 by MORIYA Masaki ("Gardejo"), +Copyright (c) 2009 by MORIYA Masaki ("gardejo"), L. This library is free software; diff --git a/lib/Ark/Plugin/Authentication/Store/Data/Model/Fast.pm b/lib/Ark/Plugin/Authentication/Store/Data/Model/Fast.pm deleted file mode 100644 index fcc520a..0000000 --- a/lib/Ark/Plugin/Authentication/Store/Data/Model/Fast.pm +++ /dev/null @@ -1,243 +0,0 @@ -package Ark::Plugin::Authentication::Store::Data::Model::Fast; -use Ark::Plugin 'Auth'; - -our $VERSION = '0.01_00'; - -has 'data_model_model' => ( - is => 'rw', - isa => 'Str', - lazy => 1, - default => sub { - my $self = shift; - $self->class_config->{model} || 'Data::Model::Fast'; - }, -); - -has 'data_model_target' => ( - is => 'rw', - isa => 'Str', - lazy => 1, - default => sub { - my $self = shift; - $self->class_config->{target} || 'user'; - }, -); - -has 'data_model_user_field' => ( - is => 'rw', - isa => 'Str', - lazy => 1, - default => sub { - my $self = shift; - $self->class_config->{user_field} || 'username'; - }, -); - -around 'find_user' => sub { - my $prev = shift->(@_); - return $prev if $prev; - - my ($self, $id, $info) = @_; - - my $model = $self->app->model( $self->data_model_model ); - - my $user; - if ($model->can('find_user')) { - $user = $model->find_user($id, $info); - } - else { - $user = $model->lookup( $self->data_model_target => $id ); - } - - if ($user) { - $self->ensure_class_loaded('Ark::Plugin::Authentication::User'); - - return Ark::Plugin::Authentication::User->new( - store => 'Data::Model::Fast', - obj => $user, - hash => $user->get_columns, - ); - } - - return; -}; - -around 'from_session' => sub { - my $prev = shift->(@_); - return $prev if $prev; - - my ($self, $user) = @_; - - return unless $user->{store} eq 'Data::Model::Fast'; - - $self->ensure_class_loaded('Ark::Plugin::Authentication::User'); - - Ark::Plugin::Authentication::User->new( - store => 'Data::Model::Fast', - hash => $user->{hash}, - obj_builder => sub { - my $model = $self->app->model( $self->data_model_model ); - - $model->lookup( - $self->data_model_target => - $user->{hash}{ $self->data_model_user_field } - ); - }, - ); -}; - -1; - -=head1 NAME - -Ark::Plugin::Authentication::Store::Data::Model::Fast - Ark plugin for storing auth via Data::Model - - -=head1 VERSION - -0.01_00 - - -=head1 SYNOPSIS - -=head2 Application root - - package MyApp::Web; - use Ark; - - use_plugins qw( - Session - Session::State::Cookie - Session::Store::Memory - - Authentication - Authentication::Credential::Password - Authentication::Store::Data::Model::Fast - ); - - # as your pleasure... - conf 'Plugin::Authentication::Store::Data::Model::Fast' => { - model => 'Foobar', # *A - target => 'user', # *B (same as default) - user_field => 'id', # *C (default is 'username') - }; - conf 'Plugin::Authentication::Credential::Password' => { - user_field => 'id', # *C (default is 'username') - password_field => 'password', # *D (same as default) - password_type => 'clear', # (same as default) - }; - - 1; - -=head2 Autentication controller - -See L. - -=head2 Authentication model - -See L. - -=head2 Table schema of user table - -See L. - -=head2 Column schema of user table - -See L. - -=head2 /root/authorization/form.mt - - [%=r $self->render('inc/header') %] - -
-

- - -

-

- - -

- -

-
- - [%=r $self->render('inc/footer') %] - - - -=head1 DESCRIPTION - -This module is a plugin for L; to store authentication informations -for any data by L. - -=head2 Finding user - -See L. - -In addition, this C plugin finds user's row -on condition that user-name's field must be defined as key. -See L -for further details. - - -=head1 SEE ALSO - -=over 4 - -=item L - -This class looks-up a row without key (by C<< $model->get >>). - -=item L - -This plugin looks-up a row from L's model. - -=item L - -This plugin verifies password with user's row object. - -=back - - -=head1 ACKNOWLEDGEMENTS - -=over 4 - -=item MURASE Daisuke ("typester") - -The author of L. - -=back - - -=head1 AUTHOR - -=over 4 - -=item MORIYA Masaki ("Gardejo") - -C<< >>, -L - -=back - - -=head1 LICENCE AND COPYRIGHT - -Copyright (c) 2009 by MORIYA Masaki ("Gardejo"), -L. - -This library is free software; -you can redistribute it and/or modify it under the same terms as Perl itself. -See L and L. diff --git a/t/plugin_authentication_store_datamodel.t b/t/plugin_authentication_store_datamodel.t index d396834..e5dff26 100644 --- a/t/plugin_authentication_store_datamodel.t +++ b/t/plugin_authentication_store_datamodel.t @@ -4,33 +4,10 @@ use File::Temp; eval "use Data::Model"; plan skip_all => 'Data::Model required to run this test' if $@; -my $db = "testdatabase_datamodel"; +my $db = "testdatabase_datamodel"; +my $dsn = "dbi:SQLite:dbname=$db"; END { unlink $db } -{ - # create Database - my $dbh = DBI->connect("dbi:SQLite:dbname=$db") - or die DBI->errstr; - - $dbh->do(<<'...'); -CREATE TABLE user ( - id INTEGER NOT NULL PRIMARY KEY, - username TEXT NOT NULL, - password TEXT NOT NULL -); -... - - $dbh->do(<<'...'); -INSERT INTO user (username, password) values ('user1', 'pass1'); -... - - - $dbh->do(<<'...'); -INSERT INTO user (username, password) values ('user2', 'pass2'); -... - -} - { package T1::Schema::Column; use Data::Model::Schema sugar => 't1'; @@ -55,17 +32,36 @@ INSERT INTO user (username, password) values ('user2', 'pass2'); use Data::Model::Schema sugar => 't1'; use Data::Model::Driver::DBI; my $driver = Data::Model::Driver::DBI->new( - dsn => "dbi:SQLite:dbname=$db", + dsn => $dsn, ); base_driver( $driver ); install_model user => schema { key 'id'; - column 'user.id'; + column 'user.id' => { auto_increment => 1 }; column 'user.username'; column 'user.password'; }; + if (! -f $db) { + # create Database + my $dbh = DBI->connect($dsn) + or die DBI->errstr; + foreach my $sql (__PACKAGE__->as_sqls) { + $dbh->do($sql); + } + + $dbh->do(<<'...'); +INSERT INTO user (username, password) values ('user1', 'pass1'); +... + + + $dbh->do(<<'...'); +INSERT INTO user (username, password) values ('user2', 'pass2'); +... + + $dbh->disconnect; + } } { @@ -82,7 +78,7 @@ INSERT INTO user (username, password) values ('user2', 'pass2'); Authentication::Store::Data::Model /; - package T1::Model::Data::Model; + package T1::Model::DataModel; use Ark 'Model::Adaptor'; __PACKAGE__->config( @@ -118,7 +114,7 @@ plan 'no_plan'; use Ark::Test 'T1', components => [qw/Controller::Root - Model::Data::Model + Model::DataModel /], reuse_connection => 1; diff --git a/t/plugin_authentication_store_datamodelfast.t b/t/plugin_authentication_store_datamodelbykey.t similarity index 78% rename from t/plugin_authentication_store_datamodelfast.t rename to t/plugin_authentication_store_datamodelbykey.t index e2f6424..ec7a179 100644 --- a/t/plugin_authentication_store_datamodelfast.t +++ b/t/plugin_authentication_store_datamodelbykey.t @@ -4,33 +4,10 @@ use File::Temp; eval "use Data::Model"; plan skip_all => 'Data::Model required to run this test' if $@; -my $db = "testdatabase_datamodelfast"; +my $db = "testdatabase_datamodelbykey"; +my $dsn = "dbi:SQLite:dbname=$db"; END { unlink $db } -{ - # create Database - my $dbh = DBI->connect("dbi:SQLite:dbname=$db") - or die DBI->errstr; - - $dbh->do(<<'...'); -CREATE TABLE user ( - id INTEGER NOT NULL PRIMARY KEY, - username TEXT NOT NULL, - password TEXT NOT NULL -); -... - - $dbh->do(<<'...'); -INSERT INTO user (id, username, password) values ('1', 'user1', 'pass1'); -... - - - $dbh->do(<<'...'); -INSERT INTO user (id, username, password) values ('2', 'user2', 'pass2'); -... - -} - { package T1::Schema::Column; use Data::Model::Schema sugar => 't1'; @@ -55,17 +32,36 @@ INSERT INTO user (id, username, password) values ('2', 'user2', 'pass2'); use Data::Model::Schema sugar => 't1'; use Data::Model::Driver::DBI; my $driver = Data::Model::Driver::DBI->new( - dsn => "dbi:SQLite:dbname=$db", + dsn => $dsn, ); base_driver( $driver ); install_model user => schema { key 'id'; - column 'user.id'; + column 'user.id' => { auto_increment => 1 }; column 'user.username'; column 'user.password'; }; + if (! -f $db) { + # create Database + my $dbh = DBI->connect($dsn) + or die DBI->errstr; + foreach my $sql (__PACKAGE__->as_sqls) { + $dbh->do($sql); + } + + $dbh->do(<<'...'); +INSERT INTO user (id, username, password) values ('1', 'user1', 'pass1'); +... + + + $dbh->do(<<'...'); +INSERT INTO user (id, username, password) values ('2', 'user2', 'pass2'); +... + + $dbh->disconnect; + } } { @@ -79,17 +75,18 @@ INSERT INTO user (id, username, password) values ('2', 'user2', 'pass2'); Authentication Authentication::Credential::Password - Authentication::Store::Data::Model::Fast + Authentication::Store::Data::Model /; - conf 'Plugin::Authentication::Store::Data::Model::Fast' => { + conf 'Plugin::Authentication::Store::Data::Model' => { user_field => 'id', + by_key => 1, }; conf 'Plugin::Authentication::Credential::Password' => { user_field => 'id', }; - package T1::Model::Data::Model::Fast; + package T1::Model::DataModel; use Ark 'Model::Adaptor'; __PACKAGE__->config( @@ -125,7 +122,7 @@ plan 'no_plan'; use Ark::Test 'T1', components => [qw/Controller::Root - Model::Data::Model::Fast + Model::DataModel /], reuse_connection => 1;