diff options
Diffstat (limited to 'lib/VNDB')
-rw-r--r-- | lib/VNDB/DB/Users.pm | 98 | ||||
-rw-r--r-- | lib/VNDB/Handler/Users.pm | 67 | ||||
-rw-r--r-- | lib/VNDB/Util/Auth.pm | 140 |
3 files changed, 170 insertions, 135 deletions
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm index d6776b2b..84ff10f2 100644 --- a/lib/VNDB/DB/Users.pm +++ b/lib/VNDB/DB/Users.pm @@ -6,15 +6,16 @@ use warnings; use Exporter 'import'; our @EXPORT = qw| - dbUserGet dbUserEdit dbUserAdd dbUserDel dbUserPrefSet - dbSessionAdd dbSessionDel dbSessionUpdateLastUsed + dbUserGet dbUserEdit dbUserAdd dbUserDel dbUserPrefSet dbUserLogin dbUserLogout + dbUserUpdateLastUsed dbUserEmailExists dbUserGetMail dbUserSetMail dbUserSetPerm dbUserAdminSetPass + dbUserResetPass dbUserIsValidToken dbUserSetPass dbNotifyGet dbNotifyMarkRead dbNotifyRemove dbThrottleGet dbThrottleSet |; -# %options->{ username passwd mail session uid ip registered search results page what sort reverse notperm } -# what: notifycount stats extended prefs hide_list +# %options->{ username session uid ip registered search results page what sort reverse notperm } +# what: notifycount stats scryptargs extended prefs hide_list # sort: username registered votes changes tags sub dbUserGet { my $s = shift; @@ -26,6 +27,7 @@ sub dbUserGet { @_ ); + my $token = unpack 'H*', $o{session}||''; $o{search} =~ s/%// if $o{search}; my %where = ( $o{username} ? ( @@ -34,8 +36,6 @@ sub dbUserGet { 'SUBSTRING(username from 1 for 1) = ?' => $o{firstchar} ) : (), !$o{firstchar} && defined $o{firstchar} ? ( 'ASCII(username) < 97 OR ASCII(username) > 122' => 1 ) : (), - $o{mail} ? ( - 'LOWER(mail) = LOWER(?)' => $o{mail} ) : (), $o{uid} && !ref($o{uid}) ? ( 'id = ?' => $o{uid} ) : (), $o{uid} && ref($o{uid}) ? ( @@ -48,8 +48,8 @@ sub dbUserGet { 'registered > to_timestamp(?)' => $o{registered} ) : (), $o{search} ? ( 'username ILIKE ?' => "%$o{search}%") : (), - $o{session} ? ( - q|s.token = decode(?, 'hex')| => unpack 'H*', $o{session} ) : (), + $token ? ( + q|user_isloggedin(id, decode(?, 'hex')) IS NOT NULL| => $token ) : (), $o{notperm} ? ( 'perm & ~(?::smallint) > 0' => $o{notperm} ) : (), ); @@ -57,8 +57,9 @@ sub dbUserGet { my @select = ( qw|id username c_votes c_changes c_tags|, q|extract('epoch' from registered) as registered|, - $o{what} =~ /extended/ ? qw|mail perm ign_votes passwd| : (), + $o{what} =~ /extended/ ? qw|perm ign_votes| : (), # mail $o{what} =~ /hide_list/ ? 'up.value AS hide_list' : (), + $o{what} =~ /scryptargs/ ? 'user_getscryptargs(id) AS scryptargs' : (), $o{what} =~ /notifycount/ ? '(SELECT COUNT(*) FROM notifications WHERE uid = u.id AND read IS NULL) AS notifycount' : (), $o{what} =~ /stats/ ? ( @@ -69,11 +70,10 @@ sub dbUserGet { '(SELECT COUNT(DISTINCT tag) FROM tags_vn WHERE uid = u.id) AS tagcount', '(SELECT COUNT(DISTINCT vid) FROM tags_vn WHERE uid = u.id) AS tagvncount', ) : (), - $o{session} ? q|extract('epoch' from s.lastused) as session_lastused| : (), + $token ? qq|extract('epoch' from user_isloggedin(id, decode('$token', 'hex'))) as session_lastused| : (), ); my @join = ( - $o{session} ? 'JOIN sessions s ON s.uid = u.id' : (), $o{what} =~ /hide_list/ || $o{sort} eq 'votes' ? "LEFT JOIN users_prefs up ON up.uid = u.id AND up.key = 'hide_list'" : (), ); @@ -119,10 +119,7 @@ sub dbUserEdit { my %h; defined $o{$_} && ($h{$_.' = ?'} = $o{$_}) - for (qw| username mail perm ign_votes email_confirmed |); - $h{'passwd = decode(?, \'hex\')'} = unpack 'H*', $o{passwd} - if defined $o{passwd}; - + for (qw| username ign_votes email_confirmed |); return if scalar keys %h <= 0; return $s->dbExec(q| @@ -133,11 +130,9 @@ sub dbUserEdit { } -# username, pass(ecrypted), mail, [ip] +# username, mail, [ip] sub dbUserAdd { - my($s, @o) = @_; - $s->dbRow(q|INSERT INTO users (username, passwd, mail, ip) VALUES(?, decode(?, 'hex'), ?, ?) RETURNING id|, - $o[0], unpack('H*', $o[1]), $o[2], $o[3]||$s->reqIP)->{id}; + $_[0]->dbRow(q|INSERT INTO users (username, mail, ip) VALUES(?, ?, ?) RETURNING id|, $_[1], $_[2], $_[3]||$_[0]->reqIP)->{id}; } @@ -156,27 +151,64 @@ sub dbUserPrefSet { } -# Adds a session to the database -# uid, 40 character session token -sub dbSessionAdd { - $_[0]->dbExec(q|INSERT INTO sessions (uid, token) VALUES(?, decode(?, 'hex'))|, $_[1], unpack 'H*', $_[2]); +# uid, encpass, token +sub dbUserLogin { + $_[0]->dbRow( + q|SELECT user_login(?, decode(?, 'hex'), decode(?, 'hex')) AS r|, + $_[1], unpack('H*', $_[2]), unpack('H*', $_[3]) + )->{r}||0; } -# Deletes session(s) from the database -# If no token is supplied, all sessions for the uid are destroyed -# uid, token (optional) -sub dbSessionDel { - my($s, @o) = @_; - my %where = ('uid = ?' => $o[0]); - $where{"token = decode(?, 'hex')"} = unpack 'H*', $o[1] if $o[1]; - $s->dbExec('DELETE FROM sessions !W', \%where); +# uid, token +sub dbUserLogout { + $_[0]->dbExec(q|SELECT user_logout(?, decode(?, 'hex'))|, $_[1], unpack 'H*', $_[2]); } # uid, token -sub dbSessionUpdateLastUsed { - $_[0]->dbExec(q|UPDATE sessions SET lastused = NOW() WHERE uid = ? AND token = decode(?, 'hex')|, $_[1], unpack 'H*', $_[2]); +sub dbUserUpdateLastUsed { + $_[0]->dbExec(q|SELECT user_update_lastused(?, decode(?, 'hex'))|, $_[1], unpack 'H*', $_[2]); +} + + +sub dbUserEmailExists { + $_[0]->dbRow(q|SELECT user_emailexists(?) AS r|, $_[1])->{r}; +} + + +sub dbUserIsValidToken { + $_[0]->dbRow(q|SELECT user_isvalidtoken(?, decode(?, 'hex')) AS r|, $_[1], unpack 'H*', $_[2])->{r}; +} + + +sub dbUserResetPass { + $_[0]->dbRow(q|SELECT user_resetpass(?, decode(?, 'hex')) AS r|, $_[1], unpack 'H*', $_[2])->{r}; +} + + +sub dbUserSetPass { + $_[0]->dbRow(q|SELECT user_setpass(?, decode(?, 'hex'), decode(?, 'hex')) AS r|, $_[1], unpack('H*', $_[2]), unpack('H*', $_[3]))->{r}; +} + + +sub dbUserGetMail { + $_[0]->dbRow(q|SELECT user_getmail(?, ?, decode(?, 'hex')) AS r|, $_[1], $_[2], unpack 'H*', $_[3])->{r}; +} + + +sub dbUserSetMail { + $_[0]->dbExec(q|SELECT user_setmail(?, ?, decode(?, 'hex'), ?)|, $_[1], $_[2], unpack('H*', $_[3]), $_[4]); +} + + +sub dbUserSetPerm { + $_[0]->dbExec(q|SELECT user_setperm(?, ?, decode(?, 'hex'), ?)|, $_[1], $_[2], unpack('H*', $_[3]), $_[4]); +} + + +sub dbUserAdminSetPass { + $_[0]->dbExec(q|SELECT user_admin_setpass(?, ?, decode(?, 'hex'), decode(?, 'hex'))|, $_[1], $_[2], unpack('H*', $_[3]), unpack('H*', $_[4])); } diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm index 5b6888cb..d1f0df93 100644 --- a/lib/VNDB/Handler/Users.pm +++ b/lib/VNDB/Handler/Users.pm @@ -145,7 +145,7 @@ sub userpage { sub login { my $self = shift; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; my $tm = $self->dbThrottleGet(norm_ip($self->reqIP)); if($tm-time() > $self->{login_throttle}[1]) { @@ -209,21 +209,18 @@ sub logout { sub newpass { my $self = shift; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; - my($frm, $u); + my($frm, $uid, $token); if($self->reqMethod eq 'POST') { return if !$self->authCheckCode; $frm = $self->formValidate({ post => 'mail', template => 'email' }); if(!$frm->{_err}) { - $u = $self->dbUserGet(mail => $frm->{mail})->[0]; - $frm->{_err} = [ 'No user found with that email address' ] if !$u || !$u->{id}; + ($uid, $token) = $self->authResetPass($frm->{mail}); + $frm->{_err} = [ 'No user found with that email address' ] if !$uid; } if(!$frm->{_err}) { - my %o; - my $token; - ($token, $o{passwd}) = $self->authPrepareReset(); - $self->dbUserEdit($u->{id}, %o); + my $u = $self->dbUserGet(uid => $uid)->[0]; my $body = sprintf "Hello %s,\n\nYour VNDB.org login has been disabled, you can now set a new password by following the link below:\n\n" ."%s\n\nNow don't forget your password again! :-)\n\nvndb.org", @@ -253,7 +250,7 @@ sub newpass { sub newpass_sent { my $self = shift; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; $self->htmlHeader(title => 'New password', noindex => 1); div class => 'mainbox'; h1 'New password'; @@ -267,14 +264,14 @@ sub newpass_sent { sub setpass { my($self, $uid) = @_; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; my $t = $self->formValidate({get => 't', regex => qr/^[a-f0-9]{40}$/i }); return $self->resNotFound if $t->{_err}; $t = $t->{t}; - my $u = $self->dbUserGet(uid => $uid, what => 'extended')->[0]; - return $self->resNotFound if !$u || !$self->authValidateReset($u->{passwd}, $t); + my $u = $self->dbUserGet(uid => $uid)->[0]; + return $self->resNotFound if !$u || !$self->authIsValidToken($u->{id}, $t); my $frm; if($self->reqMethod eq 'POST') { @@ -286,10 +283,8 @@ sub setpass { push @{$frm->{_err}}, 'Passwords do not match' if $frm->{usrpass} ne $frm->{usrpass2}; if(!$frm->{_err}) { - my %o = (email_confirmed => 1); - $o{passwd} = $self->authPreparePass($frm->{usrpass}); - $self->dbUserEdit($uid, %o); - return $self->authCreateSession($u->{username}, "/u$uid"); + $self->dbUserEdit($uid, email_confirmed => 1); + return $self->authSetPass($uid, $frm->{usrpass}, "/u$uid", token => $t) } } @@ -306,7 +301,7 @@ sub setpass { sub register { my $self = shift; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; my $frm; if($self->reqMethod eq 'POST') { @@ -323,7 +318,7 @@ sub register { push @{$frm->{_err}}, 'Someone already has this username, please choose another name' if $frm->{usrname} eq 'anonymous' || !$frm->{_err} && $self->dbUserGet(username => $frm->{usrname})->[0]{id}; push @{$frm->{_err}}, 'Someone already registered with that email address' - if !$frm->{_err} && $self->dbUserGet(mail => $frm->{mail})->[0]{id}; + if !$frm->{_err} && $self->dbUserEmailExists($frm->{mail}); # Use /32 match for IPv4 and /48 for IPv6. The /48 is fairly broad, so some # users may have to wait a bit before they can register... @@ -332,8 +327,8 @@ sub register { if !$frm->{_err} && $self->dbUserGet(ip => $ip =~ /:/ ? "$ip/48" : $ip, registered => time-24*3600)->[0]{id}; if(!$frm->{_err}) { - my($token, $pass) = $self->authPrepareReset(); - my $uid = $self->dbUserAdd($frm->{usrname}, $pass, $frm->{mail}); + my $uid = $self->dbUserAdd($frm->{usrname}, $frm->{mail}); + my(undef, $token) = $self->authResetPass($frm->{mail}); my $body = sprintf "Hello %s,\n\n" ."Someone has registered an account on VNDB.org with your email address. To confirm your registration, follow the link below.\n\n" ."%s\n\n" @@ -369,7 +364,7 @@ sub register { sub register_done { my $self = shift; - return $self->resRedirect('/') if $self->authInfo->{id}; + return $self->resRedirect('/', 'temp') if $self->authInfo->{id}; $self->htmlHeader(title => 'Account created', noindex => 1); div class => 'mainbox'; h1 'Account created'; @@ -416,9 +411,6 @@ sub edit { ); push @{$frm->{_err}}, 'Passwords do not match' if ($frm->{usrpass} || $frm->{usrpass2}) && (!$frm->{usrpass} || !$frm->{usrpass2} || $frm->{usrpass} ne $frm->{usrpass2}); - push @{$frm->{_err}}, 'Invalid password' - if !($self->authInfo->{id} != $u->{id} && $self->authCan('usermod')) - && ($frm->{usrpass} || $frm->{usrpass2}) && !$self->authCheck($u->{username}, $frm->{curpass}); if(!$frm->{_err}) { $frm->{skin} = '' if $frm->{skin} eq $self->{skin_default}; @@ -426,23 +418,34 @@ sub edit { my $tags_cat = join(',', sort @{$frm->{tags_cat}}) || 'none'; $self->dbUserPrefSet($uid, tags_cat => $tags_cat eq $self->{default_tags_cat} ? '' : $tags_cat); + my %o; if($self->authCan('usermod')) { $o{username} = $frm->{usrname} if $frm->{usrname}; - $o{perm} = 0; - $o{perm} |= $self->{permissions}{$_} for(@{ delete $frm->{perms} }); + $o{ign_votes} = $frm->{ign_votes} ? 1 : 0; + + my $perm = 0; + $perm |= $self->{permissions}{$_} for(@{ delete $frm->{perms} }); + $self->dbUserSetPerm($u->{id}, $self->authInfo->{id}, $self->authInfo->{token}, $perm); } - $o{mail} = $frm->{mail}; - $o{passwd} = $self->authPreparePass($frm->{usrpass}) if $frm->{usrpass}; - $o{ign_votes} = $frm->{ign_votes} ? 1 : 0 if $self->authCan('usermod'); + $self->dbUserSetMail($u->{id}, $self->authInfo->{id}, $self->authInfo->{token}, $frm->{mail}); $self->dbUserEdit($uid, %o); - return $self->resRedirect("/u$uid/edit?d=1", 'post'); + $self->authAdminSetPass($u->{id}, $frm->{usrpass}) if $frm->{usrpass} && $self->authInfo->{id} != $u->{id}; + + if($frm->{usrpass} && $self->authInfo->{id} == $u->{id}) { + # Bit ugly: On incorrect password, all other changes are still saved. + my $ok = $self->authSetPass($u->{id}, $frm->{usrpass}, "/u$uid/edit?d=1", pass => $frm->{curpass}); + return if $ok; + push @{$frm->{_err}}, 'Invalid password'; + } else { + return $self->resRedirect("/u$uid/edit?d=1", 'post'); + } } } # fill out default values $frm->{usrname} ||= $u->{username}; - $frm->{mail} ||= $u->{mail}; + $frm->{mail} ||= $self->dbUserGetMail($u->{id}, $self->authInfo->{id}, $self->authInfo->{token}); $frm->{perms} ||= [ grep $u->{perm} & $self->{permissions}{$_}, keys %{$self->{permissions}} ]; $frm->{$_} //= $u->{prefs}{$_} for(qw|skin customcss show_nsfw traits_sexual tags_all hide_list spoilers|); $frm->{tags_cat} ||= [ split /,/, $u->{prefs}{tags_cat}||$self->{default_tags_cat} ]; 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; } |