/** * Show the special page * * @param $subpage Mixed: parameter passed to the page or null */ public function execute($subpage) { global $wgRequest, $wgUser, $wgOut; $this->setHeaders(); $hash_key = $wgRequest->getText('key', null); $email = $token = $timestamp = null; if (!empty($hash_key)) { #$hask_key = urldecode ( $hash_key ); $data = Wikia::verifyUserSecretKey($hash_key, 'sha256'); error_log("data = " . print_r($data, true)); if (!empty($data)) { $username = isset($data['user']) ? $data['user'] : null; $token = isset($data['token']) ? $data['token'] : null; $timestamp = isset($data['signature1']) ? $data['signature1'] : null; $oUser = User::newFromName($username); $email = $oUser->getEmail(); } } else { $email = $wgRequest->getText('email', null); $token = $wgRequest->getText('token', null); $timestamp = $wgRequest->getText('timestamp', null); } if ($email == null || $token == null || $timestamp == null) { #give up now, abandon all hope. $wgOut->addWikiMsg('unsubscribe-badaccess'); return; } #validate timestamp isnt spoiled (you only have 7 days) $timeCutoff = strtotime("7 days ago"); if ($timestamp <= $timeCutoff) { $wgOut->addWikiMsg('unsubscribe-badtime'); // $wgOut->addHTML("timestamp={$timestamp}\n"); #DEVL (remove before release) // $wgOut->addHTML("timeCutoff={$timeCutoff}\n"); #DEVL (remove before release) return; } #generate what the token SHOULD be $shouldToken = wfGenerateUnsubToken($email, $timestamp); if ($token != $shouldToken) { $wgOut->addWikiMsg('unsubscribe-badtoken'); // $wgOut->addHTML("shouldtoken={$shouldToken}\n"); #DEVL (remove before release) return; } #does the non-blank email they gave us look like an email? if (Sanitizer::validateEmail($email) == false) { #email wasnt blank, but didnt look like any email $wgOut->addWikiMsg('unsubscribe-bademail'); // $wgOut->addHTML("email={$email}\n"); #DEVL (remove before release) return; } #at this point, the 3 params check out. #is this their 2nd pass at this? $confirmed = $wgRequest->getBool('confirm', null); if ($wgRequest->wasPosted() && $confirmed) { #this is the 2nd round, they pushed the button, so do it $this->procUnsub($email); } else { #this is 1st pass, give them a button to push $this->showInfo($email, $token, $timestamp); } }
/** * check user authentication key * @static * @access public * @param array $params */ public static function verifyUserSecretKey($url, $hash_algorithm = 'sha256') { global $wgWikiaAuthTokenKeys; wfProfileIn(__METHOD__); @(list($user, $signature1, $signature2, $public_key) = explode("|", base64_decode(strtr($url, '-_,', '+/=')))); if (empty($user) || empty($signature1) || empty($signature2) || empty($public_key)) { wfProfileOut(__METHOD__); return false; } # verification public key if ($wgWikiaAuthTokenKeys['public'] == $public_key) { $private_key = $wgWikiaAuthTokenKeys['private']; } else { wfProfileOut(__METHOD__); return false; } $oUser = User::newFromName($user); if (!is_object($oUser)) { wfProfileOut(__METHOD__); return false; } // verify params $email = $oUser->getEmail(); $params = array('user' => (string) $user, 'signature1' => (string) $signature1, 'token' => (string) wfGenerateUnsubToken($email, $signature1)); // message to hash $message = serialize($params); // computed signature $hash = hash_hmac($hash_algorithm, $message, $private_key); // compare values if ($hash != $signature2) { wfProfileOut(__METHOD__); return false; } wfProfileOut(__METHOD__); return $params; }
/** * TODO Move this into unsubscribe extension? * @return string */ private function getUnsubscribeLink() { $params = ['email' => $this->targetUser->getEmail(), 'timestamp' => time()]; $params['token'] = wfGenerateUnsubToken($params['email'], $params['timestamp']); $unsubscribeTitle = \GlobalTitle::newFromText('Unsubscribe', NS_SPECIAL, \Wikia::COMMUNITY_WIKI_ID); return $unsubscribeTitle->getFullURL($params); }