package Catalyst::Plugin::Authentication::Credential::TypeKey;
use strict;
+use warnings;
+
use Authen::TypeKey;
use Carp ();
use File::Spec;
+use Catalyst::Utils ();
+use NEXT;
+use UNIVERSAL::require;
our $VERSION = '0.1';
-our $PARAMETERS = qw[
- email
- name
- nick
- ts
- sig
-];
+sub setup {
+ my $c = shift;
+
+ my $config = $c->config->{authentication}{typekey} ||= {};
+
+ $config->{typekey_object} ||= do {
+ $config->{user_class} ||=
+ "Catalyst::Plugin::Authentication::User::Hash";
+
+ $config->{key_cache} ||=
+ File::Spec->catfile( Catalyst::Utils::class2tempdir( $c, 1 ),
+ 'regkeys.txt' );
+
+ my $typekey = Authen::TypeKey->new;
+
+ for (qw/expires key_cache key_url token version/) {
+ $typekey->$_( $config->{$_} || next );
+ }
+
+ $typekey;
+ };
+
+ $c->NEXT::setup(@_);
+}
sub authenticate_typekey {
- my ( $c, $email, $name, $nick, $ts, $sig, $options ) = @_;
+ my ( $c, %parameters ) = @_;
- unless ( @_ == 6 || ( @_ == 7 && ref($options) eq 'HASH' ) ) {
- Carp::croak('usage: $c->authenticate_typekey( $email, $name, $nick, $ts, $sig [, \%options ] )');
- }
+ my $config = $c->config->{authentication}{typekey};
- unless ( @_ == 7 ) {
- $options = {};
+ my $typekey = delete( $parameters{typekey_object} )
+ || $config->{typekey_object};
+
+ my @fields = qw/email name nick ts sig/;
+
+ foreach my $field (@fields) {
+ $parameters{$field} ||= $c->req->param($field) || return;
}
- my $config = $c->config->{authenticate}->{typekey};
-
- my $token = $options->{token} || $config->{token} || undef;
- my $expires = $options->{expires} || $config->{expires} || 0;
- my $version = $options->{version} || $config->{version} || 1.1;
- my $cache = $options->{cache} || $config->{cache} || File::Spec->catfile( File::Spec->tmpdir, 'regkeys.txt' );
- my $keys = $options->{keys} || $config->{keys} || 'http://www.typekey.com/extras/regkeys.txt';
-
- my $typekey = Authen::TypeKey->new;
- $typekey->expires($expires);
- $typekey->key_cache($cache);
- $typekey->key_url($keys);
- $typekey->token($token);
- $typekey->version($version);
-
- my $parameters = {
- email => $email,
- name => $name,
- nick => $nick,
- ts => $ts,
- sig => $sig
- };
+ if ( $typekey->verify( \%parameters ) ) {
+ $c->log->debug("Successfully authenticated user '$parameters{name}'.")
+ if $c->debug;
- unless ( $typekey->verify($parameters) ) {
- my $error = $typekey->errstr;
- $c->log->debug(qq/Failed to authenticate user '$name'. Reason: '$error'/);
- return 0;
+ my $user;
+
+ if ( my $store = $config->{auth_store} ) {
+ $store = $c->get_auth_store($store) unless ref $store;
+ $user = $store->get_user( \%parameters );
+ }
+ else {
+ my $user_class = $config->{user_class};
+ $user_class->require or die $@;
+ $user = $user_class->new( \%parameters );
+ }
+
+ $c->set_authenticated($user);
+
+ return 1;
}
+ else {
+ $c->log->debug(
+ sprintf "Failed to authenticate user '%s'. Reason: '%s'",
+ $parameters{name}, $typekey->errstr )
+ if $c->debug;
- $c->log->debug( qq/Successfully authenticated user '$name'./);
- return 1;
+ return;
+ }
}
1;
=head1 NAME
Catalyst::Plugin::Authentication::Credential::TypeKey - TypeKey Authentication
+for Catalyst.
=head1 SYNOPSIS
token => 'xxxxxxxxxxxxxxxxxxxx'
};
- if ( $c->authenticate_typekey( $email, $name, $nick, $ts, $sig ) ) {
- # successful autentication
- }
+ sub foo : Local {
+ my ( $self, $c ) = @_;
+
+ if ( $c->authenticate_typekey( name => $name, email => $email, ... ) ) {
+ # successful autentication
+
+ $c->user; # blah
+ }
+ }
+
+
+ sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ $c->authenticate_typekey; # uses $c->req->params
+
+ return 1;
+ }
=head1 DESCRIPTION
-TypeKey Authentication.
+This module integrates L<Authen::TypeKey> with
+L<Catalyst::Plugin::Authentication>.
+
+=head1 METHODS
+
+=item authenticate_typekey %parameters
+
+=item authenticate_typekey
+
+=item EXTENDED METHODS
+
+=item setup
+
+Fills the config with defaults.
+
+=head1 CONFIGURATION
+
+C<<$c->config->{autentication}{typekey}>> is a hash with these fields (all can
+be left out):
+
+=over 4
+
+=item typekey_object
+
+If this field does not exist an L<Authen::TypeKey> object will be created based
+on the other param and put here.
+
+=item expires
+
+=item key_url
+
+=item token
+
+=item version
+
+See L<Authen::TypeKey> for all of these. If they aren't specified
+L<Authen::TypeKey>'s defaults will be used.
+
+=item key_cache
+
+Also see L<Authen::TypeKey>.
+
+Defaults to C<regkeys.txt> under L<Catalyst::Utils/class2tempdir>.
+
+=item auth_store
+
+A store (or store name) to retrieve the user from.
+
+When a user is successfully authenticated it will call this:
+
+ $store->get_user( $parameters );
+
+Where C<$parameters> is a the hash reference passed to L<Authen::TypeKey/verify>.
+
+=item user_class
+
+If C<auth_store> is not set it will use this class to instantiate an object,
+calling C<new> on the class with the same C<$parameters> hash ref.
+
+=back
=head1 SEE ALSO
-L<Authen::TypeKey>, L<Catalyst>.
+L<Authen::TypeKey>, L<Catalyst>, L<Catalyst::Plugin::Authentication>.
=head1 AUTHOR
-Christian Hansen, C<ch@ngmedia.com>
+Christian Hansen
+
+Yuval Kogman, C<nothingmuch@woobling.org>
=head1 LICENSE
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+use Test::MockObject::Extends;
+use Test::MockObject;
+use Test::Exception;
+
+use Catalyst::Plugin::Authentication::User::Hash;
+
+my $m;
+BEGIN { use_ok( $m = "Catalyst::Plugin::Authentication::Credential::TypeKey" ) }
+
+my $req = Test::MockObject->new;
+$req->set_always( params => {} );
+$req->mock( param => sub { $_[0]->params->{ $_[1] } } );
+
+my $tk = Test::MockObject->new;
+$tk->set_true("verify");
+
+my $store = Test::MockObject->new;
+$store->mock( get_user => sub { shift; Catalyst::Plugin::Authentication::User::Hash->new( @_ ) } );
+
+my $c = Test::MockObject::Extends->new($m);
+$c->set_always( config => {} );
+my $config = $c->config->{authentication}{typekey} ||= {};
+
+$c->set_always( req => $req );
+$c->set_always( request => $req );
+$c->set_false("debug");
+
+my $authenticated;
+$c->mock( set_authenticated => sub { $authenticated = $_[1] } );
+
+can_ok( $m, "setup" );
+
+$c->setup;
+
+isa_ok( $config->{typekey_object},
+ "Authen::TypeKey", '$c->config->{authentication}{typekey}{obj}' );
+
+$config->{typekey_object} = $tk;
+
+can_ok( $m, "authenticate_typekey" );
+
+lives_ok {
+ $c->authenticate_typekey;
+ }
+ "can try to auth with no args, no params";
+
+ok( !$c->called("set_authenticated"), "nothing was authenticated" );
+ok( !$tk->called("verify"), "didn't even verify with no params" );
+
+$_->clear for $c, $tk;
+
+# from 01-verify.t in Authen-TypeKey-0.04
+%{ $req->params } = my %vars = (
+ ts => '1091163746',
+ email => 'bentwo@stupidfool.org',
+ name => 'Melody',
+ nick => 'foobar baz',
+ sig => 'GWwAIXbkb2xNrQO2e/r2LDl14ek=:U5+tDsPM0+EXeKzFWsosizG7+VU=',
+);
+
+lives_ok {
+ $c->authenticate_typekey;
+ }
+ "can try to auth, no args, all params";
+
+$tk->called_ok("verify");
+$c->called_ok( "set_authenticated", "authenticated" );
+
+$_->clear for $c, $tk;
+
+%{ $req->params } = ();
+$config->{auth_store} = $store;
+
+lives_ok {
+ $c->authenticate_typekey(%vars);
+ }
+ "can try to auth with args";
+
+$tk->called_ok("verify");
+$c->called_ok( "set_authenticated", "authenticated" );
+$store->called_ok( "get_user", "user retrieved from store" );
+
+
+$_->clear for $c, $tk, $store;
+
+$tk->set_false("verify");
+
+
+lives_ok {
+ $c->authenticate_typekey(%vars);
+ }
+ "can try to auth with args";
+
+$tk->called_ok("verify");
+ok( !$c->called( "set_authenticated" ), "authenticated" );
+ok( !$store->called( "get_user" ), "no user retrieved from store");
+
+