App-iTan
view release on metacpan or search on metacpan
lib/App/iTan/Utils.pm view on Meta::CPAN
# ================================================================
package App::iTan::Utils;
# ================================================================
use utf8;
use Moose::Role;
use MooseX::App::Role;
use 5.0100;
use Path::Class;
use Params::Coerce;
use MooseX::Types::Path::Class;
use File::HomeDir;
use Term::ReadKey;
use DBI;
use Crypt::Twofish;
use DateTime;
=head1 NAME
App::iTan::Utils - Utility methods role
=head1 METHODS
=head2 Accessors
=head3 database
Path to the database as a L<Path::Class::File> object.
=head3 dbh
Active database handle
=head3 cipher
L<Crypt::Twofish> cipher object
=head2 Methods
=head3 get
my $tandata = $self->get($index);
Fetches a valid iTan with the given index.
=head3 mark
$self->mark($index[,$memo]);
=head3 crypt_string
my $crypt = $self->crypt_string($string);
Encrpyts a string
=head3 decrypt_string
my $string = $self->decrypt_string($crypt);
Decrpyts a string
=cut
option 'database' => (
is => 'ro',
isa => 'Path::Class::File',
required => 1,
coerce => 1,
documentation => q[Path to the iTAN database file. Defaults to ~/.itan],
default => sub {
return Path::Class::File->new( File::HomeDir->my_home, '.itan' );
},
);
has 'dbh' => (
is => 'ro',
lib/App/iTan/Utils.pm view on Meta::CPAN
)]
) or die "ERROR: Cannot execute: " . $dbh->errstr();
$dbh->do(
q[CREATE TABLE system (
name VARCHAR NOT NULL,
value VARCHAR NOT NULL
)]
) or die "ERROR: Cannot execute: " . $dbh->errstr();
my $sth = $dbh->prepare(q[INSERT INTO system (name,value) VALUES (?,?)]);
$sth->execute('password',$crypted);
$sth->execute('version',$App::iTan::VERSION);
$sth->finish;
}
# $dbh->{'csv_tables'}->{'itan'}
# = { 'col_names' => [ "tindex", "itan", "imported", "used", "valid", "memo" ] };
#
# $dbh->{'csv_tables'}->{'system'}
# = { 'col_names' => [ "name", "value" ] };
return $dbh;
}
sub _build_cipher {
my ($self) = @_;
my $password = $self->_get_password();
my $cipher = Crypt::Twofish->new($password);
$self->cipher($cipher);
my $stored_password = $self->dbh->selectrow_array("SELECT value FROM system WHERE name = 'password'")
or die "ERROR: Cannot query: " . $self->dbh->errstr();
unless ( $self->decrypt_string($stored_password) eq $password) {
die "ERROR: Invalid password";
}
return $cipher;
}
sub _parse_date {
my ( $self, $date ) = @_;
return
unless defined $date && $date =~ m/^
(?<year>\d{4})
\/
(?<month>\d{1,2})
\/
(?<day>\d{1,2})
\s
(?<hour>\d{1,2})
:
(?<minute>\d{1,2})
$/x;
return DateTime->new(
year => $+{year},
month => $+{month},
day => $+{day},
hour => $+{hour},
minute => $+{minute},
);
}
sub crypt_string {
my ( $self, $string ) = @_;
use bytes;
while (1) {
last if length($string) % 16 == 0;
$string .= ' ';
}
return $self->cipher->encrypt($string);
}
sub decrypt_string {
my ( $self, $data ) = @_;
my $tan = $self->cipher->decrypt($data);
$tan =~ s/\s+//g;
return $tan;
}
sub _date {
return DateTime->now->format_cldr('yyyy/MM/dd HH:mm');
}
sub _get_password {
my $password;
ReadMode 2;
say 'Please enter your password:';
while ( not defined( $password = ReadLine(-1) ) ) {
# no key pressed yet
}
ReadMode 0;
chomp($password);
my $length;
{
use bytes;
$length = length $password;
}
if ($length == 16) {
# ok
} elsif ($length < 4) {
die('ERROR: Password is too short (Min 4 bytes required)');
} elsif ($length > 16) {
die('ERROR: Password is too long (Max 16 bytes allowed)');
} else {
while (1) {
$password .= '0';
last
if length $password == 16;
}
}
return $password;
}
sub get {
my ($self,$index) = @_;
my $sth = $self->dbh->prepare('SELECT
tindex,
itan,
imported,
used,
memo
FROM itan
WHERE tindex = ?
AND valid = 1')
or die "ERROR: Cannot prepare: " . $self->dbh->errstr();
$sth->execute($index)
or die "ERROR: Cannot execute: " . $sth->errstr();
my $data = $sth->fetchrow_hashref();
unless (defined $data) {
die "ERROR: Could not find iTAN ".$index;
}
$data->{imported} = $self->_parse_date($data->{imported});
$data->{used} = $self->_parse_date($data->{used});
( run in 1.197 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )