/** * This is the ajax-endpoint that the node server will connect to in order to get the currently logged-in user's info. * The node server will pass the same cookies that the client has set, and this will allow this ajax request to be * part of the same sesssion that the user already has going. By doing this, the user's existing wikia login session * can be used, so they don't need to re-login for us to know that they are legitimately authorized to use the chat or not. * * The returned info is just a custom subset of what the node server needs and does not contain an exhaustive list of rights. * * The 'isLoggedIn' field and 'canChat' field of the result should be checked by the calling code before allowing * the user to chat. This is the last line of security against any users attempting to circumvent our protections. Otherwise, * a banned user could copy the entire client code (HTML/JS/etc.) from an unblocked user, then run that code while logged in as * under a banned account, and they would still be given access. * * The returned 'isChatMod' field is boolean based on whether the user is a chat moderator on the current wiki. * * If the user is not allowed to chat, an error message is returned (which can be shown to the user). */ public static function getUserInfo() { ChatHelper::info(__METHOD__ . ': Method called'); global $wgMemc, $wgServer, $wgArticlePath, $wgRequest, $wgCityId, $wgContLang; wfProfileIn(__METHOD__); $data = $wgMemc->get($wgRequest->getVal('key'), false); if (empty($data)) { wfProfileOut(__METHOD__); return array('errorMsg' => "Key not found"); } $user = User::newFromId($data['user_id']); if (empty($user) || !$user->isLoggedIn() || $user->getName() != urldecode($wgRequest->getVal('name', ''))) { wfProfileOut(__METHOD__); return array('errorMsg' => "User not found"); } $isCanGiveChatMod = false; $userChangeableGroups = $user->changeableGroups(); if (in_array('chatmoderator', $userChangeableGroups['add'])) { $isCanGiveChatMod = true; } // First, check if they can chat on this wiki. $retVal = array('canChat' => Chat::canChat($user), 'isLoggedIn' => $user->isLoggedIn(), 'isChatMod' => $user->isAllowed('chatmoderator'), 'isCanGiveChatMod' => $isCanGiveChatMod, 'isStaff' => $user->isAllowed('chatstaff'), 'username' => $user->getName(), 'username_encoded' => rawurlencode($user->getName()), 'avatarSrc' => AvatarService::getAvatarUrl($user->getName(), self::CHAT_AVATAR_DIMENSION), 'editCount' => "", 'since' => '', 'activeBasket' => ChatHelper::getServerBasket(), 'wgCityId' => $wgCityId, 'wgServer' => $wgServer, 'wgArticlePath' => $wgArticlePath); // Figure out the error message to return (i18n is done on this side). if ($retVal['isLoggedIn'] === false) { $retVal['errorMsg'] = wfMsg('chat-no-login'); } else { if ($retVal['canChat'] === false) { $retVal['errorMsg'] = wfMsg('chat-you-are-banned-text'); } } // If the user is approved to chat, make sure the roomId provided is for this wiki. // Users may be banned on the wiki of the room, but not on this wiki for example, so this prevents cross-wiki chat hacks. if ($retVal['canChat']) { $roomId = $wgRequest->getVal('roomId'); $cityIdOfRoom = NodeApiClient::getCityIdForRoom($roomId); if ($wgCityId !== $cityIdOfRoom) { $retVal['canChat'] = false; // don't let the user chat in the room they requested. $retVal['errorMsg'] = wfMsg('chat-room-is-not-on-this-wiki'); } } // If the user can chat, dig up some other stats which are a little more expensive to compute. if ($retVal['canChat']) { // new user joins the chat, purge the cache ChatEntryPoint::purgeChatUsersCache(); $userStatsService = new UserStatsService($user->getId()); $stats = $userStatsService->getStats(); // NOTE: This is attached to the user so it will be in the wiki's content language instead of wgLang (which it normally will). $stats['edits'] = $wgContLang->formatNum($stats['edits']); if (empty($stats['date'])) { // If the user has not edited on this wiki, don't show anything $retVal['since'] = ""; } else { // this results goes to chat server, which obiously has no user lang // so we just return a short month name key - it has to be translated on client side $date = getdate(wfTimestamp(TS_UNIX, $stats['date'])); $retVal['since'] = $date; } $retVal['editCount'] = $stats['edits']; } wfProfileOut(__METHOD__); return $retVal; }
/** * This is the ajax-endpoint that the node server will connect to in order to get the currently logged-in user's info. * The node server will pass the same cookies that the client has set, and this will allow this ajax request to be * part of the same sesssion that the user already has going. By doing this, the user's existing wikia login session * can be used, so they don't need to re-login for us to know that they are legitimately authorized to use the chat or not. * * The returned info is just a custom subset of what the node server needs and does not contain an exhaustive list of rights. * * The 'isLoggedIn' field and 'canChat' field of the result should be checked by the calling code before allowing * the user to chat. This is the last line of security against any users attemptin to circumvent our protections. Otherwise, * a banned user could copy the entire client code (HTML/JS/etc.) from an unblocked user, then run that code while logged in as * under a banned account, and they would still be given access. * * The returned 'isChatMod' field is boolean based on whether the user is a chat moderator on the current wiki. * * If the user is not allowed to chat, an error message is returned (which can be shown to the user). */ public static function getUserInfo() { global $wgMemc, $wgServer, $wgArticlePath, $wgRequest, $wgCityId, $wgContLang, $wgIP; wfProfileIn(__METHOD__); $data = $wgMemc->get($wgRequest->getVal('key'), false); if (empty($data)) { return array('errorMsg' => wfMsg('chat-room-is-not-on-this-wiki')); } $user = User::newFromId($data['user_id']); if (empty($user) || !$user->isLoggedIn() || $user->getName() != $wgRequest->getVal('name', '')) { wfProfileOut(__METHOD__); return array('errorMsg' => wfMsg('chat-room-is-not-on-this-wiki')); } $isCanGiveChatMode = false; $userChangeableGroups = $user->changeableGroups(); if (in_array('chatmoderator', $userChangeableGroups['add'])) { $isCanGiveChatMode = true; } // First, check if they can chat on this wiki. $retVal = array('canChat' => Chat::canChat($user), 'isLoggedIn' => $user->isLoggedIn(), 'isChatMod' => $user->isAllowed('chatmoderator'), 'isCanGiveChatMode' => $isCanGiveChatMode, 'isStaff' => $user->isAllowed('chatstaff'), 'username' => $user->getName(), 'avatarSrc' => AvatarService::getAvatarUrl($user->getName(), self::CHAT_AVATAR_DIMENSION), 'editCount' => "", 'since' => '', 'activeBasket' => ChatHelper::getServerBasket(), 'wgCityId' => $wgCityId, 'wgServer' => $wgServer, 'wgArticlePath' => $wgArticlePath); // Figure out the error message to return (i18n is done on this side). if ($retVal['isLoggedIn'] === false) { $retVal['errorMsg'] = wfMsg('chat-no-login'); } else { if ($retVal['canChat'] === false) { $retVal['errorMsg'] = wfMsg('chat-you-are-banned-text'); } } // If the user is approved to chat, make sure the roomId provided is for this wiki. // Users may be banned on the wiki of the room, but not on this wiki for example, so this prevents cross-wiki chat hacks. if ($retVal['canChat']) { $roomId = $wgRequest->getVal('roomId'); $cityIdOfRoom = NodeApiClient::getCityIdForRoom($roomId); if ($wgCityId !== $cityIdOfRoom) { $retVal['canChat'] = false; // don't let the user chat in the room they requested. $retVal['errorMsg'] = wfMsg('chat-room-is-not-on-this-wiki'); } } // If the user can chat, dig up some other stats which are a little more expensive to compute. if ($retVal['canChat']) { $userStatsService = new UserStatsService($user->getId()); $stats = $userStatsService->getStats(); // NOTE: This is attached to the user so it will be in the wiki's content language instead of wgLang (which it normally will). $stats['edits'] = $wgContLang->formatNum($stats['edits']); if (empty($stats['date'])) { // If the user has not edited on this wiki, don't show anything $retVal['since'] = ""; } else { // this results goes to chat server, which obiously has no user lang // so we just return a short month name key - it has to be translated on client side $date = getdate(wfTimestamp(TS_UNIX, $stats['date'])); $retVal['since'] = $date; } $retVal['editCount'] = $stats['edits']; } if ($retVal['isLoggedIn'] && $retVal['canChat']) { // record the IP of the connecting user. // use memcache so we order only one (user, ip) pair each day $ip = $wgRequest->getVal('address'); $memcKey = self::getUserIPMemcKey($data['user_id'], $ip, date("Y-m-d")); $entry = $wgMemc->get($memcKey, false); if (empty($entry)) { $wgMemc->set($memcKey, true, 86400); $log = WF::build('LogPage', array('chatconnect', false, false)); $log->addEntry('chatconnect', SpecialPage::getTitleFor('Chat'), '', array($ip), $user); $dbw = wfGetDB(DB_MASTER); $cuc_id = $dbw->nextSequenceValue('cu_changes_cu_id_seq'); $rcRow = array('cuc_id' => $cuc_id, 'cuc_namespace' => NS_SPECIAL, 'cuc_title' => 'Chat', 'cuc_minor' => 0, 'cuc_user' => $user->getID(), 'cuc_user_text' => $user->getName(), 'cuc_actiontext' => wfMsgForContent('chat-checkuser-join-action'), 'cuc_comment' => '', 'cuc_this_oldid' => 0, 'cuc_last_oldid' => 0, 'cuc_type' => CUC_TYPE_CHAT, 'cuc_timestamp' => $dbw->timestamp(), 'cuc_ip' => IP::sanitizeIP($ip), 'cuc_ip_hex' => $ip ? IP::toHex($ip) : null, 'cuc_xff' => '', 'cuc_xff_hex' => null, 'cuc_agent' => null); $dbw->insert('cu_changes', $rcRow, __METHOD__); $dbw->commit(); } } wfProfileOut(__METHOD__); return $retVal; }