From 98c9d95e9b7a1e78f5cda93904c6624d57df4518 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Wed, 18 Sep 2019 12:38:18 +0200 Subject: v2rw: Convert authentication code to VNWeb::Auth More churn! Also converted v3 to use VNWeb::Auth, considering the API is pretty much the same. Converted VNWeb::* to use VNDB::Config directly rather than read from tuwf->{}, converted VNWeb::HTML to use VNWeb::Auth, and updated util/vndb.pl with the new code style. I tested as much as I could, but I'm sure I broke something. --- lib/VNDB/Util/Auth.pm | 161 ++++++++++---------------------------------------- 1 file changed, 31 insertions(+), 130 deletions(-) (limited to 'lib/VNDB/Util/Auth.pm') diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm index c335041a..91453edf 100644 --- a/lib/VNDB/Util/Auth.pm +++ b/lib/VNDB/Util/Auth.pm @@ -1,16 +1,12 @@ - +# Compatibility shim around VNWeb::Auth, new code should use that instead. package VNDB::Util::Auth; use strict; use warnings; use Exporter 'import'; -use Digest::SHA qw|sha1 sha1_hex|; -use Crypt::URandom 'urandom'; -use Crypt::ScryptKDF 'scrypt_raw'; -use Encode 'encode_utf8'; use TUWF ':html'; -use VNDB::Func; +use VNWeb::Auth; our @EXPORT = qw| @@ -19,102 +15,32 @@ our @EXPORT = qw| |; -sub randomascii { - return join '', map chr($_%92+33), unpack 'C*', urandom shift; -} - - -# Fetches and parses the auth cookie. -# Returns (uid, encrypted_token) on success, (0, '') on failure. -sub parsecookie { - # Earlier versions of the auth cookie didn't have the dot separator, so that's optional. - return ($_[0]->reqCookie('auth')||'') =~ /^([a-fA-F0-9]{40})\.?(\d+)$/ ? ($2, sha1 pack 'H*', $1) : (0, ''); -} - - -# initializes authentication information and checks the vndb_auth cookie -sub authInit { - my $self = shift; - - my($uid, $token_e) = parsecookie($self); - $self->{_auth} = $uid && $self->dbUserGet(uid => $uid, session => $token_e, what => 'extended notifycount prefs')->[0]; - $self->{_auth}{token} = $token_e if $self->{_auth}; - - # update the sessions.lastused column if lastused < now()-'6 hours' - $self->dbUserUpdateLastUsed($uid, $token_e) if $self->{_auth} && $self->{_auth}{session_lastused} < time()-6*3600; - - # Drop the cookie if it's not valid - $self->resCookie(auth => undef) if !$self->{_auth} && $self->reqCookie('auth'); -} - - # login, arguments: user, password, url-to-redirect-to-on-success # returns 1 on success (redirected), 0 otherwise (no reply sent) sub authLogin { - my($self, $user, $pass, $to) = @_; - - return 0 if !$user || !$pass; - - my $d = $self->dbUserGet(username => $user, what => 'scryptargs extended prefs notifycount')->[0]; - return 0 if !$d->{id} || !$d->{scryptargs} || length($d->{scryptargs}) != 14; - - my($N, $r, $p, $salt) = unpack 'NCCa8', $d->{scryptargs}; - my $encpass = _preparepass($self, $pass, $salt, $N, $r, $p); - - return _createsession($self, $d->{id}, $encpass, $to); + my(undef, $user, $pass, $to) = @_; + my $success = auth->login($user, $pass); + tuwf->resRedirect($to, 'post') if $success; + $success } - -# Prepares a plaintext password for database storage -# Arguments: pass, optionally: salt, N, r, p -# Returns: encrypted password (as a binary string) -sub _preparepass { - my($self, $pass, $salt, $N, $r, $p) = @_; - ($N, $r, $p) = @{$self->{scrypt_args}} if !$N; - $salt ||= urandom(8); - return pack 'NCCa8a*', $N, $r, $p, $salt, scrypt_raw(encode_utf8($pass), $self->{scrypt_salt} . $salt, $N, $r, $p, 32); -} - - -# self, uid, encpass, url-to-redirect-to -sub _createsession { - my($self, $uid, $encpass, $url) = @_; - - my $token = urandom(20); - my $token_e = sha1 $token; - return 0 if !$self->dbUserLogin($uid, $encpass, $token_e); - - $self->resRedirect($url, 'post'); - $self->resCookie(auth => unpack('H*', $token).'.'.$uid, httponly => 1, expires => time + 31536000); # keep the cookie for 1 year - return $token_e; -} - - # clears authentication cookie and redirects to / sub authLogout { - my $self = shift; - - my($uid, $token_e) = parsecookie($self); - $self->dbUserLogout($uid, $token_e) if $uid; - - $self->resRedirect('/', 'temp'); - $self->resCookie(auth => undef); + auth->logout; + tuwf->resRedirect('/', 'temp'); } # Replaces the user's password with a random token that can be used to reset the password. sub authResetPass { - my $self = shift; - my $mail = shift; - my $token = unpack 'H*', urandom(20); - my $id = $self->dbUserResetPass($mail, sha1(lc($token))); - return $id ? ($id, $token) : (); + my(undef, $mail) = @_; + auth->resetpass($mail) } -# uid, token sub authIsValidToken { - $_[0]->dbUserIsValidToken($_[1], sha1(lc($_[2]))) + my(undef, $uid, $token) = @_; + auth->isvalidtoken($uid, $token) } @@ -122,44 +48,32 @@ sub authIsValidToken { # Changes the user's password, invalidates all existing sessions, creates a new # session and redirects. sub authSetPass { - my($self, $uid, $pass, $redir, $oldtype, $oldpass) = @_; - - if($oldtype eq 'token') { - $oldpass = sha1(lc($oldpass)); - - } elsif($oldtype eq 'pass') { - my $u = $self->dbUserGet(uid => $uid, what => 'scryptargs')->[0]; - return 0 if !$u->{id} || !$u->{scryptargs} || length($u->{scryptargs}) != 14; - my($N, $r, $p, $salt) = unpack 'NCCa8', $u->{scryptargs}; - $oldpass = _preparepass($self, $oldpass, $salt, $N, $r, $p); - } + my(undef, $uid, $pass, $redir, $oldtype, $oldpass) = @_; - $pass = _preparepass($self, $pass); - return 0 if !$self->dbUserSetPass($uid, $oldpass, $pass); - return _createsession($self, $uid, $pass, $redir); + my $success = auth->setpass($uid, $oldtype eq 'token' ? $oldpass : undef, $oldtype eq 'pass' ? $oldpass : undef, $pass); + tuwf->resRedirect($redir, 'post') if $success; + $success } sub authAdminSetPass { - my($self, $uid, $pass) = @_; - $pass = _preparepass($self, $pass); - $self->dbUserAdminSetPass($uid, $self->authInfo->{id}, $self->authInfo->{token}, $pass); + my(undef, $uid, $pass) = @_; + auth->admin_setpass($uid, $pass); } -# returns a hashref with information about the current loggedin user -# the hash is identical to the hash returned by dbUserGet -# returns empty hash if no user is logged in. sub authInfo { - return shift->{_auth} || {}; + # Used to return a lot more, but this was by far the most common use + # (code using other fields has been migrated) + +{ id => auth->uid, username => auth->username } } # returns whether the currently loggedin or anonymous user can perform # a certain action. sub authCan { - my($self, $act) = @_; - return $self->{_auth} ? $self->{_auth}{perm} & $self->{permissions}{$act} : 0; + my(undef, $act) = @_; + auth->perm() & auth->listPerms->{$act} } @@ -167,14 +81,10 @@ sub authCan { # submitted from our site and by the same user/visitor. Not limited to # logged-in users. # Arguments: -# form-id (string, can be empty, but makes the validation stronger) -# time (optional, time() to encode in the code) +# form-id (ignored nowadyas) +# time (also ignored) sub authGetCode { - my $self = shift; - my $id = shift; - my $time = (shift || time)/3600; # accuracy of an hour - my $uid = encode_utf8($self->{_auth} ? $self->{_auth}{id} : norm_ip($self->reqIP())); - return lc substr sha1_hex($self->{form_salt} . $uid . encode_utf8($id||'') . pack('N', int $time)), 0, 16; + auth->csrftoken; } @@ -187,14 +97,10 @@ sub authGetCode { # form-id is not given, the path of the current requests is used. sub authCheckCode { my $self = shift; - my $id = shift || $self->reqPath(); + my $id = shift; my $code = shift || $self->reqParam('formcode'); - return _incorrectcode($self) if !$code || $code !~ qr/^[0-9a-f]{16}$/; - my $time = time; - return 1 if $self->authGetCode($id, $time) eq $code; - return 1 if $self->authGetCode($id, $time-3600) eq $code; - return 1 if $self->authGetCode($id, $time-2*3600) eq $code; - return _incorrectcode($self); + return _incorrectcode($self) if !auth->csrfcheck($code); + 1; } @@ -216,13 +122,8 @@ sub _incorrectcode { sub authPref { - my($self, $key, $val) = @_; - my $nfo = $self->authInfo; - return '' if !$nfo->{id}; - return $nfo->{prefs}{$key}||'' if @_ == 2; - $nfo->{prefs}{$key} = $val; - $self->dbUserPrefSet($nfo->{id}, $key, $val); + my(undef, $key, $val) = @_; + @_ == 2 ? auth->pref($key)||'' : auth->prefSet($key, $val); } 1; - -- cgit v1.2.3