Bivio::Biz::Model::OTP
# Copyright (c) 2007-2013 bivio Software Artisans, Inc. All Rights Reserved.
# $Id$
package Bivio::Biz::Model::OTP;
use strict;
use Bivio::Base 'Biz.PropertyModel';
use Bivio::Biz::RFC2289;
Bivio::IO::Config->register(my $_CFG = {
login_timeout_seconds => 3600,
reinitialize_sequence => 10,
});
sub create {
my($self, $values) = (shift, shift);
return $self->SUPER::create(_values($self, $values), @_);
}
sub get_challenge {
my($self, $sequence, $seed) = @_;
if ($self->is_loaded) {
$sequence ||= $self->get('sequence');
$seed ||= $self->get('seed');
}
$sequence ||= $self->get_field_type('sequence')->get_max;
return join(' ', 'otp_md5', $sequence, lc($seed));
}
sub handle_config {
my(undef, $cfg) = @_;
$_CFG = $cfg;
return;
}
sub should_reinit {
return shift->get('sequence') <= $_CFG->{reinitialize_sequence}
? 1 : 0;
}
sub validate_password {
my($self, $passwd, $auth_user) = @_;
$self->unauth_load_or_die({user_id => $auth_user->get('realm_id')});
my($t) = $self->get_field_type('last_login');
return $self->get('otp_md5') eq $passwd
&& $t->diff_seconds($t->now, $self->get('last_login'))
<= $_CFG->{login_timeout_seconds}
? 1 : 0;
}
sub internal_initialize {
my($self) = @_;
return $self->merge_initialize_info($self->SUPER::internal_initialize, {
version => 1,
table_name => 'otp_t',
columns => {
user_id => ['User.user_id', 'PRIMARY_KEY'],
otp_md5 => ['OTPMD5', 'NONE'],
seed => ['OTPSeed', 'NONE'],
sequence => ['OTPSequence', 'NONE'],
last_login => ['DateTime', 'NOT_NULL'],
},
auth_id => 'user_id',
});
}
sub reset_auth_user {
my($self, $values) = @_;
my($u) = $self->req('auth_user');
$values->{user_id} = $u->get('realm_id');
$u->update({password => $u->get_field_type('password')->OTP_VALUE});
return $self->unauth_create_or_update($values);
}
sub update {
my($self, $values) = (shift, shift);
return $self->SUPER::update(_values($self, $values), @_);
}
sub verify {
my($self, $input) = @_;
my($otp_md5) = Bivio::Biz::RFC2289->canonical_hex($input);
return 0
unless $otp_md5;
return 0
unless Bivio::Biz::RFC2289->verify($otp_md5, $self->get('otp_md5'));
$self->update({
otp_md5 => $otp_md5,
sequence => $self->get('sequence') - 1,
});
return 1;
}
sub _values {
my($self, $values) = @_;
#TODO: Is this a good idea to hardcode here? Shouldn't it be pulled from OTPForm?
$values->{sequence} = $self->get_field_type('sequence')->get_max - 1
unless exists($values->{sequence});
$values->{last_login} ||= $self->get_field_type('last_login')->now();
return $values;
}
1;