diff options
Diffstat (limited to 'lib/VNDB/Util')
-rw-r--r-- | lib/VNDB/Util/Auth.pm | 140 |
1 files changed, 70 insertions, 70 deletions
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm index 6e1dfa5d..e3ee20eb 100644 --- a/lib/VNDB/Util/Auth.pm +++ b/lib/VNDB/Util/Auth.pm @@ -14,8 +14,8 @@ use VNDB::Func; our @EXPORT = qw| - authInit authLogin authLogout authInfo authCan authPreparePass authCreateSession authCheck - authPrepareReset authValidateReset authGetCode authCheckCode authPref + authInit authLogin authLogout authInfo authCan authSetPass authAdminSetPass + authResetPass authIsValidToken authGetCode authCheckCode authPref |; @@ -28,7 +28,7 @@ sub randomascii { # 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-zA-Z0-9]{40})\.?(\d+)$/ ? ($2, sha1 pack 'H*', $1) : (0, ''); + return ($_[0]->reqCookie('auth')||'') =~ /^([a-fA-F0-9]{40})\.?(\d+)$/ ? ($2, sha1 pack 'H*', $1) : (0, ''); } @@ -38,9 +38,10 @@ sub authInit { 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->dbSessionUpdateLastUsed($uid, $token_e) if $self->{_auth} && $self->{_auth}{session_lastused} < time()-6*3600; + $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'); @@ -52,29 +53,39 @@ sub authInit { sub authLogin { my($self, $user, $pass, $to) = @_; - if($self->authCheck($user, $pass)) { - $self->authCreateSession($user, $to); - return 1; - } + return 0 if !$user || !$pass; - return 0; + 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); } -# Args: user, url-to-redirect-to-on-success -# Should only be called if the user is already authenticated (i.e. after authCheck or when the user just confirmed his email address). -sub authCreateSession { - my($self, $user, $to) = @_; +# 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($pass, $self->{scrypt_salt} . $salt, $N, $r, $p, 32); +} + - $self->{_auth} = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0] if $user; - die "No valid user!" if !$self->{_auth}{id}; +# self, uid, encpass, url-to-redirect-to +sub _createsession { + my($self, $uid, $encpass, $url) = @_; my $token = urandom(20); - my $cookie = unpack('H*', $token).'.'.$self->{_auth}{id}; - $self->dbSessionAdd($self->{_auth}{id}, sha1 $token); + return 0 if !$self->dbUserLogin($uid, $encpass, sha1 $token); - $self->resRedirect($to, 'post'); - $self->resCookie(auth => $cookie, httponly => 1, expires => time + 31536000); # keep the cookie for 1 year + $self->resRedirect($url, 'post'); + $self->resCookie(auth => unpack('H*', $token).'.'.$uid, httponly => 1, expires => time + 31536000); # keep the cookie for 1 year + return 1; } @@ -83,82 +94,71 @@ sub authLogout { my $self = shift; my($uid, $token_e) = parsecookie($self); - $self->dbSessionDel($uid, $token_e) if $uid; + $self->dbUserLogout($uid, $token_e) if $uid; $self->resRedirect('/', 'temp'); $self->resCookie(auth => undef); } -# 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} || {}; +# 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) : (); } -# returns whether the currently loggedin or anonymous user can perform -# a certain action. Argument is the action name as defined in global.pl -sub authCan { - my($self, $act) = @_; - return $self->{_auth} ? $self->{_auth}{perm} & $self->{permissions}{$act} : 0; +# uid, token +sub authIsValidToken { + $_[0]->dbUserIsValidToken($_[1], sha1(lc($_[2]))) } -# Checks for a valid login and writes information in _auth -# Arguments: user, pass -# Returns: 1 if login is valid, 0 otherwise -sub authCheck { - my($self, $user, $pass) = @_; - - return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass; +# uid, new_pass, url_to_redir_to, 'token'|'pass', $token_or_pass +# Changes the user's password, invalidates all existing sessions, creates a new +# session and redirects. +sub authSetPass { + my($self, $uid, $pass, $redir, $oldtype, $oldpass) = @_; - my $d = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0]; - return 0 if !$d->{id}; + if($oldtype eq 'token') { + $oldpass = sha1(lc($oldpass)); - # scrypt format - if(length $d->{passwd} == 46) { - my($N, $r, $p, $salt) = unpack 'NCCa8', $d->{passwd}; - return 0 if $self->authPreparePass($pass, $salt, $N, $r, $p) ne $d->{passwd}; - $self->{_auth} = $d; - return 1; + } 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); } - return 0; + $pass = _preparepass($self, $pass); + return 0 if !$self->dbUserSetPass($uid, $oldpass, $pass); + return _createsession($self, $uid, $pass, $redir); } -# Prepares a plaintext password for database storage -# Arguments: pass, optionally: salt, N, r, p -# Returns: encrypted password (as a binary string) -sub authPreparePass { - 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($pass, $self->{scrypt_salt} . $salt, $N, $r, $p, 32); +sub authAdminSetPass { + my($self, $uid, $pass) = @_; + $pass = _preparepass($self, $pass); + $self->dbUserAdminSetPass($uid, $self->authInfo->{id}, $self->authInfo->{token}, $pass); } -# Generates a random token that can be used to reset the password. -# Returns: token (hex string), token-encrypted (binary string) -sub authPrepareReset { - my $self = shift; - my $token = unpack 'H*', urandom(20); - my $salt = randomascii(9); - my $token_e = encode_utf8($salt) . sha1(lc($token).$salt); - return ($token, $token_e); +# 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} || {}; } -# Checks whether the password reset token is valid. -# Arguments: passwd (binary string), token (hex string) -sub authValidateReset { - my($self, $passwd, $token) = @_; - return 0 if length $passwd != 29; - my $salt = substr $passwd, 0, 9; - return 0 if $salt.sha1(lc($token).$salt) ne $passwd; - return 1; +# returns whether the currently loggedin or anonymous user can perform +# a certain action. Argument is the action name as defined in global.pl +sub authCan { + my($self, $act) = @_; + return $self->{_auth} ? $self->{_auth}{perm} & $self->{permissions}{$act} : 0; } |