X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FUser.php;h=2131406d4a5f8cf2be7432c66b57f2839936224c;hb=8c99d3acc1b65bd7645d69f430d5fd9604d6b3b5;hp=dbace74e5d9360fb38c4c3dd224192cebe2ae557;hpb=df135c31fe10777c21c02480262107d9cab0e563;p=friendica.git diff --git a/src/Model/User.php b/src/Model/User.php index dbace74e5d..2131406d4a 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -1,6 +1,6 @@ getHostname())) { + // Avoid database queries when the local node hostname isn't even part of the url. + if (!Contact::isLocal($url)) { return 0; } @@ -399,7 +399,7 @@ class User return false; } - if (!$repairMissing) { + if (!$repairMissing || $owner['account_expired']) { return $owner; } @@ -515,7 +515,27 @@ class User */ public static function getIdFromPasswordAuthentication($user_info, $password, $third_party = false) { - $user = self::getAuthenticationInfo($user_info); + // Addons registered with the "authenticate" hook may create the user on the + // fly. `getAuthenticationInfo` will fail if the user doesn't exist yet. If + // the user doesn't exist, we should give the addons a chance to create the + // user in our database, if applicable, before re-throwing the exception if + // they fail. + try { + $user = self::getAuthenticationInfo($user_info); + } catch (Exception $e) { + $username = (is_string($user_info) ? $user_info : $user_info['nickname'] ?? ''); + + // Addons can create users, and since this 'catch' branch should only + // execute if getAuthenticationInfo can't find an existing user, that's + // exactly what will happen here. Creating a numeric username would create + // abiguity with user IDs, possibly opening up an attack vector. + // So let's be very careful about that. + if (empty($username) || is_numeric($username)) { + throw $e; + } + + return self::getIdFromAuthenticateHooks($username, $password); + } if ($third_party && DI::pConfig()->get($user['uid'], '2fa', 'verified')) { // Third-party apps can't verify two-factor authentication, we use app-specific passwords instead @@ -544,6 +564,41 @@ class User } return $user['uid']; + } else { + return self::getIdFromAuthenticateHooks($user['nickname'], $password); // throws + } + + throw new HTTPException\ForbiddenException(DI::l10n()->t('Login failed')); + } + + /** + * Try to obtain a user ID via "authenticate" hook addons + * + * Returns the user id associated with a successful password authentication + * + * @param string $username + * @param string $password + * @return int User Id if authentication is successful + * @throws HTTPException\ForbiddenException + */ + public static function getIdFromAuthenticateHooks($username, $password) + { + $addon_auth = [ + 'username' => $username, + 'password' => $password, + 'authenticated' => 0, + 'user_record' => null + ]; + + /* + * An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record + * Addons should never set 'authenticated' except to indicate success - as hooks may be chained + * and later addons should not interfere with an earlier one that succeeded. + */ + Hook::callAll('authenticate', $addon_auth); + + if ($addon_auth['authenticated'] && $addon_auth['user_record']) { + return $addon_auth['user_record']['uid']; } throw new HTTPException\ForbiddenException(DI::l10n()->t('Login failed')); @@ -584,7 +639,7 @@ class User if (is_int($user_info)) { $user = DBA::selectFirst( 'user', - ['uid', 'password', 'legacy_password'], + ['uid', 'nickname', 'password', 'legacy_password'], [ 'uid' => $user_info, 'blocked' => 0, @@ -594,7 +649,7 @@ class User ] ); } else { - $fields = ['uid', 'password', 'legacy_password']; + $fields = ['uid', 'nickname', 'password', 'legacy_password']; $condition = [ "(`email` = ? OR `username` = ? OR `nickname` = ?) AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`", @@ -1353,7 +1408,7 @@ class User */ public static function remove(int $uid) { - if (!$uid) { + if (empty($uid)) { return false; } @@ -1367,6 +1422,9 @@ class User // unique), so it cannot be re-registered in the future. DBA::insert('userd', ['username' => $user['nickname']]); + // Remove all personal settings, especially connector settings + DBA::delete('pconfig', ['uid' => $uid]); + // The user and related data will be deleted in Friendica\Worker\ExpireAndRemoveUsers DBA::update('user', ['account_removed' => true, 'account_expires_on' => DateTimeFormat::utc('now + 7 day')], ['uid' => $uid]); Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::REMOVAL, $uid);