public function actionAuthorizeGuest() { $requestPaths = XenForo_Application::get('requestPaths'); $social = $this->_input->filterSingle('social', XenForo_Input::STRING); switch ($social) { case 'facebook': $facebookLink = XenForo_Link::buildPublicLink('full:register/facebook', null, array('reg' => 1, 'redirect' => $requestPaths['fullUri'])); return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $facebookLink); case 'twitter': $twitterLink = XenForo_Link::buildPublicLink('full:register/twitter', null, array('reg' => 1, 'redirect' => $requestPaths['fullUri'])); return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $twitterLink); } /* @var $oauth2Model bdApi_Model_OAuth2 */ $oauth2Model = $this->getModelFromCache('bdApi_Model_OAuth2'); /* @var $clientModel bdApi_Model_Client */ $clientModel = $oauth2Model->getClientModel(); $clientId = $this->_input->filterSingle('client_id', XenForo_Input::STRING); $client = $clientModel->getClientById($clientId); if (empty($client)) { return $this->responseError(new XenForo_Phrase('bdapi_authorize_error_client_x_not_found', array('client' => $clientId)), 404); } $authorizeParams = $this->_input->filter($oauth2Model->getAuthorizeParamsInputFilter()); $redirectParams = $authorizeParams; $redirectParams['timestamp'] = time() + bdApi_Option::get('authorizeBypassSecs'); $redirectParams['hash'] = bdApi_Crypt::encryptTypeOne(serialize($authorizeParams), $redirectParams['timestamp']); $redirect = XenForo_Link::buildPublicLink('account/authorize', null, $redirectParams); $viewParams = array('client' => $client, 'authorizeParams' => $authorizeParams, 'social' => $social, 'redirect' => $redirect); $view = $this->responseView('bdApi_ViewPublic_Account_Authorize', 'bdapi_error_authorize_guest', $viewParams); $view->responseCode = 403; return $view; }
public function actionApi() { $input = $this->_input->filter(array('redirect' => XenForo_Input::STRING, 'timestamp' => XenForo_Input::UINT, 'user_id' => XenForo_Input::STRING)); $userId = 0; if (!empty($input['user_id']) && !empty($input['timestamp'])) { try { $userId = intval(bdApi_Crypt::decryptTypeOne($input['user_id'], $input['timestamp'])); } catch (XenForo_Exception $e) { if (XenForo_Application::debugMode()) { $this->_response->setHeader('X-Api-Exception', $e->getMessage()); } } } if ($userId > 0) { $this->_response->setHeader('X-Api-Login-User', $userId); $this->_getUserModel()->setUserRememberCookie($userId); XenForo_Model_Ip::log($userId, 'user', $userId, 'login_api'); $this->_getUserModel()->deleteSessionActivity(0, $this->_request->getClientIp(false)); $session = XenForo_Application::get('session'); $session->changeUserId($userId); XenForo_Visitor::setup($userId); } if (empty($input['redirect'])) { $input['redirect'] = $this->getDynamicRedirectIfNot(XenForo_Link::buildPublicLink('login')); } return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $input['redirect']); }
public function getDynamicRedirect($fallbackUrl = false, $useReferrer = true) { $input = $this->_input->filter(array('redirect' => XenForo_Input::STRING, 'timestamp' => XenForo_Input::UINT, 'md5' => XenForo_Input::STRING)); if (!empty($input['md5']) && md5($input['redirect']) === bdApi_Crypt::decryptTypeOne($input['md5'], $input['timestamp'])) { $this->_bdApi_redirect = $input['redirect']; return $input['redirect']; } return parent::getDynamicRedirect($fallbackUrl, $useReferrer); }
public function actionPostPasswordTest() { $input = $this->_input->filter(array('password' => XenForo_Input::STRING, 'password_algo' => XenForo_Input::STRING, 'decrypt' => XenForo_Input::UINT)); if (!XenForo_Application::debugMode()) { return $this->responseNoPermission(); } if (empty($input['decrypt'])) { $result = bdApi_Crypt::encrypt($input['password'], $input['password_algo']); } else { $result = bdApi_Crypt::decrypt($input['password'], $input['password_algo']); } $data = array('result' => $result); return $this->responseData('bdApi_ViewApi_Tool_PasswordTest', $data); }
public static function front_controller_pre_route(XenForo_FrontController $fc) { // use cookie flag to change web UI interface to match requested language_id from api $request = $fc->getRequest(); $apiLanguageId = $request->getParam('_apiLanguageId'); if (!empty($apiLanguageId) && preg_match('#^(?<timestamp>\\d+) (?<data>.+)$#', $apiLanguageId, $matches)) { try { $languageId = bdApi_Crypt::decryptTypeOne($matches['data'], $matches['timestamp']); if ($languageId > 0) { $cookiePrefix = XenForo_Application::getConfig()->get('cookie')->get('prefix'); XenForo_Helper_Cookie::setCookie('language_id', $languageId); $_COOKIE[$cookiePrefix . 'language_id'] = $languageId; $fc->getResponse()->setHeader('X-Api-Language', $languageId); } } catch (XenForo_Exception $e) { // ignore } } }
public function getDynamicRedirect($fallbackUrl = false, $useReferrer = true) { $input = $this->_input->filter(array('redirect' => XenForo_Input::STRING, 'timestamp' => XenForo_Input::UINT, 'md5' => XenForo_Input::STRING)); if (!empty($input['md5']) && !empty($input['timestamp']) && !empty($input['redirect'])) { $md5 = ''; try { $md5 = bdApi_Crypt::decryptTypeOne($input['md5'], $input['timestamp']); } catch (XenForo_Exception $e) { if (XenForo_Application::debugMode()) { $this->_response->setHeader('X-Api-Exception', $e->getMessage()); } } if (!empty($md5) && $md5 === md5($input['redirect'])) { $this->_bdApi_redirect = $input['redirect']; return $input['redirect']; } } return parent::getDynamicRedirect($fallbackUrl, $useReferrer); }
public static function buildPublicLink($type, $data = null, array $extraParams = array(), $skipPrepend = false) { // the type MUST BE canonical:$type // NOTE: this is the opposite with api links if (strpos($type, 'full:') === 0) { // replace full: with canonical: $type = str_replace('full:', 'canonical:', $type); } elseif (strpos($type, 'canonical:') === false) { // enforce canonical: $type = 'canonical:' . $type; } $session = bdApi_Data_Helper_Core::safeGetSession(); if (!empty($session)) { // auto appends locale param from session if (!isset($extraParams['locale'])) { $locale = $session->get('requestLocale'); if (!empty($locale)) { $timestamp = time() + 86400; $extraParams['_apiLanguageId'] = sprintf('%s %s', $timestamp, bdApi_Crypt::encryptTypeOne($session->get('languageId'), $timestamp)); } } } return parent::buildPublicLink($type, $data, $extraParams, $skipPrepend); }
public function actionPutIndex() { $input = $this->_input->filter(array('password' => XenForo_Input::STRING, 'password_old' => XenForo_Input::STRING, 'password_algo' => XenForo_Input::STRING, 'user_email' => XenForo_Input::STRING, 'username' => XenForo_Input::STRING, 'primary_group_id' => XenForo_Input::UINT, 'secondary_group_ids' => array(XenForo_Input::UINT, 'array' => true), 'user_dob_day' => XenForo_Input::UINT, 'user_dob_month' => XenForo_Input::UINT, 'user_dob_year' => XenForo_Input::UINT, 'user_fields' => XenForo_Input::ARRAY_SIMPLE)); $user = $this->_getUserOrError(); $visitor = XenForo_Visitor::getInstance(); $session = bdApi_Data_Helper_Core::safeGetSession(); $isAdmin = $session->checkScope(bdApi_Model_OAuth2::SCOPE_MANAGE_SYSTEM) && $visitor->hasAdminPermission('user'); $requiredAuth = 0; if (!empty($input['password'])) { $requiredAuth++; } if (!empty($input['user_email'])) { $requiredAuth++; } if ($requiredAuth > 0) { $isAuth = false; if ($isAdmin && $visitor['user_id'] != $user['user_id']) { $isAuth = true; } elseif (!empty($input['password_old'])) { $auth = $this->_getUserModel()->getUserAuthenticationObjectByUserId($user['user_id']); if (!empty($auth)) { $passwordOld = bdApi_Crypt::decrypt($input['password_old'], $input['password_algo']); if ($auth->hasPassword() && $auth->authenticate($user['user_id'], $passwordOld)) { $isAuth = true; } } } if (!$isAuth) { return $this->responseError(new XenForo_Phrase('bdapi_slash_users_requires_password_old'), 403); } } /* @var $writer XenForo_DataWriter_User */ $writer = XenForo_DataWriter::create('XenForo_DataWriter_User'); $writer->setExistingData($user, true); if ($isAdmin) { $writer->setOption(XenForo_DataWriter_User::OPTION_ADMIN_EDIT, true); } if (!empty($input['password'])) { $password = bdApi_Crypt::decrypt($input['password'], $input['password_algo']); $writer->setPassword($password, $password); } if (!empty($input['user_email'])) { $writer->set('email', $input['user_email']); if ($writer->isChanged('email') && XenForo_Application::getOptions()->get('registrationSetup', 'emailConfirmation') && !$isAdmin) { switch ($writer->get('user_state')) { case 'moderated': case 'email_confirm': $writer->set('user_state', 'email_confirm'); break; default: $writer->set('user_state', 'email_confirm_edit'); } } } if (!empty($input['username'])) { $writer->set('username', $input['username']); if ($writer->isChanged('username') && !$isAdmin) { return $this->responseError(new XenForo_Phrase('bdapi_slash_users_denied_username'), 403); } } if ($input['primary_group_id'] > 0) { $userGroups = $this->_getUserGroupModel()->getAllUserGroups(); if (!isset($userGroups[$input['primary_group_id']])) { return $this->responseError(new XenForo_Phrase('requested_user_group_not_found')); } if (!empty($input['secondary_group_ids'])) { foreach ($input['secondary_group_ids'] as $secondaryGroupId) { if (!isset($userGroups[$secondaryGroupId])) { return $this->responseError(new XenForo_Phrase('requested_user_group_not_found')); } } } $writer->set('user_group_id', $input['primary_group_id']); $writer->setSecondaryGroups($input['secondary_group_ids']); } if (!empty($input['user_dob_day']) && !empty($input['user_dob_month']) && !empty($input['user_dob_year'])) { $writer->set('dob_day', $input['user_dob_day']); $writer->set('dob_month', $input['user_dob_month']); $writer->set('dob_year', $input['user_dob_year']); $hasExistingDob = false; $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_day'); $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_month'); $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_year'); if ($hasExistingDob && ($writer->isChanged('dob_day') || $writer->isChanged('dob_month') || $writer->isChanged('dob_year')) && !$isAdmin) { // setting new dob is fine but changing dob requires admin permission return $this->responseError(new XenForo_Phrase('bdapi_slash_users_denied_dob'), 403); } } if (!empty($input['user_fields'])) { $profileFieldsInput = new XenForo_Input($input['user_fields']); $profileFields = $profileFieldsInput->filter(array('about' => XenForo_Input::STRING, 'homepage' => XenForo_Input::STRING, 'location' => XenForo_Input::STRING, 'occupation' => XenForo_Input::STRING)); $writer->bulkSet($profileFields); $writer->setCustomFields($input['user_fields']); } $writer->preSave(); if (!$isAdmin) { if ($writer->isChanged('user_group_id') || $writer->isChanged('secondary_group_ids')) { // this has to be checked here because `secondary_group_ids` only get set within preSave() return $this->responseError(new XenForo_Phrase('bdapi_slash_users_denied_user_group'), 403); } } $writer->save(); $user = $writer->getMergedData(); if ($writer->isChanged('email') && in_array($user['user_state'], array('email_confirm', 'email_confirm_edit'))) { /* @var $userConfirmationModel XenForo_Model_UserConfirmation */ $userConfirmationModel = $this->getModelFromCache('XenForo_Model_UserConfirmation'); $userConfirmationModel->sendEmailConfirmation($user); } return $this->responseMessage(new XenForo_Phrase('changes_saved')); }
public function actionPutIndex() { $user = $this->_getUserOrError(); $visitor = XenForo_Visitor::getInstance(); $input = $this->_input->filter(array('password_old' => XenForo_Input::STRING, 'password_algo' => XenForo_Input::STRING, 'user_email' => XenForo_Input::STRING, 'username' => XenForo_Input::STRING, 'password' => XenForo_Input::STRING, 'user_dob_day' => XenForo_Input::UINT, 'user_dob_month' => XenForo_Input::UINT, 'user_dob_year' => XenForo_Input::UINT)); $session = bdApi_Data_Helper_Core::safeGetSession(); $isAdmin = $session->checkScope(bdApi_Model_OAuth2::SCOPE_MANAGE_SYSTEM) && $visitor->hasAdminPermission('user'); $isAuth = false; if ($isAdmin && $visitor['user_id'] != $user['user_id']) { $isAuth = true; } elseif (!empty($input['password_old'])) { $auth = $this->_getUserModel()->getUserAuthenticationObjectByUserId($user['user_id']); if (!empty($auth)) { $passwordOld = bdApi_Crypt::decrypt($input['password_old'], $input['password_algo']); if ($auth->hasPassword() && $auth->authenticate($user['user_id'], $passwordOld)) { $isAuth = true; } } } if (!$isAuth) { return $this->responseNoPermission(); } /* @var $writer XenForo_DataWriter_User */ $writer = XenForo_DataWriter::create('XenForo_DataWriter_User'); $writer->setExistingData($user, true); if (!empty($input['user_email'])) { $writer->set('email', $input['user_email']); if ($writer->isChanged('email') && XenForo_Application::getOptions()->get('registrationSetup', 'emailConfirmation') && !$isAdmin) { switch ($writer->get('user_state')) { case 'moderated': case 'email_confirm': $writer->set('user_state', 'email_confirm'); break; default: $writer->set('user_state', 'email_confirm_edit'); } } } if (!empty($input['username'])) { if (!$isAdmin) { return $this->responseNoPermission(); } $writer->set('username', $input['username']); } if (!empty($input['password'])) { $password = bdApi_Crypt::decrypt($input['password'], $input['password_algo']); $writer->setPassword($password, $password); } if (!empty($input['user_dob_day']) && !empty($input['user_dob_month']) && !empty($input['user_dob_year'])) { $hasExistingDob = false; $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_day'); $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_month'); $hasExistingDob = $hasExistingDob || !!$writer->getExisting('dob_year'); if ($hasExistingDob) { if (!$isAdmin) { // changing dob requires admin permission return $this->responseNoPermission(); } } else { // new dob just needs auth } $writer->set('dob_day', $input['user_dob_day']); $writer->set('dob_month', $input['user_dob_month']); $writer->set('dob_year', $input['user_dob_year']); } if (!$writer->hasChanges()) { return $this->responseError(new XenForo_Phrase('error_occurred_or_request_stopped'), 400); } $writer->save(); $user = $writer->getMergedData(); if ($writer->isChanged('email') && in_array($user['user_state'], array('email_confirm', 'email_confirm_edit'))) { /* @var $userConfirmationModel XenForo_Model_UserConfirmation */ $userConfirmationModel = $this->getModelFromCache('XenForo_Model_UserConfirmation'); $userConfirmationModel->sendEmailConfirmation($user); } return $this->responseMessage(new XenForo_Phrase('changes_saved')); }
public function actionAuthorize() { /* @var $oauth2Model bdApi_Model_OAuth2 */ $oauth2Model = $this->getModelFromCache('bdApi_Model_OAuth2'); $authorizeParams = $this->_input->filter($oauth2Model->getAuthorizeParamsInputFilter()); if ($this->_request->isPost()) { // allow user to deny some certain scopes // only when this is a POST request, this should keep us safe from some vectors // of attack $scopesIncluded = $this->_input->filterSingle('scopes_included', XenForo_Input::UINT); $scopes = $this->_input->filterSingle('scopes', XenForo_Input::ARRAY_SIMPLE); if (!empty($scopesIncluded)) { $authorizeParams['scope'] = bdApi_Template_Helper_Core::getInstance()->scopeJoin($scopes); } } $client = null; $clientIsAuto = false; if (empty($authorizeParams['client_id'])) { // try to get the first client of user if available $visitorClients = $this->_bdApi_getClientModel()->getClients(array('user_id' => XenForo_Visitor::getUserId()), array('limit' => 1)); if (!empty($visitorClients)) { $randClientId = array_rand($visitorClients, 1); $client = $visitorClients[$randClientId]; $clientIsAuto = true; $authorizeParams['client_id'] = $client['client_id']; // auto assign at least the READ scope if (empty($authorizeParams['scope'])) { $authorizeParams['scope'] = bdApi_Model_OAuth2::SCOPE_READ; } // reset the redirect uri to prevent security issue $authorizeParams['redirect_uri'] = ''; // force to use implicit authentication flow2 $authorizeParams['response_type'] = 'token'; } } else { $client = $oauth2Model->getClientModel()->getClientById($authorizeParams['client_id']); } if (empty($client)) { if (XenForo_Visitor::getInstance()->hasPermission('general', 'bdApi_clientNew')) { return $this->responseError(new XenForo_Phrase('bdapi_authorize_no_client_create_one_question', array('link' => XenForo_Link::buildPublicLink('account/api/client-add'))), 404); } return $this->responseError(new XenForo_Phrase('bdapi_authorize_error_client_x_not_found', array('client' => $authorizeParams['client_id'])), 404); } // sondh@2013-03-19 // this is a non-standard implementation: bypass confirmation dialog if the // client has appropriate option set $bypassConfirmation = false; if ($oauth2Model->getClientModel()->canAutoAuthorize($client, $authorizeParams['scope'])) { $bypassConfirmation = true; } // sondh@2015-09-28 // bypass confirmation if user logged in to authorize // this is secured by checking our encrypted time-expiring hash $hashInput = $this->_input->filter(array('hash' => XenForo_Input::STRING, 'timestamp' => XenForo_Input::UINT)); if (!empty($hashInput['hash']) && !empty($hashInput['timestamp'])) { try { if (bdApi_Crypt::decryptTypeOne($hashInput['hash'], $hashInput['timestamp'])) { $bypassConfirmation = true; } } catch (XenForo_Exception $e) { if (XenForo_Application::debugMode()) { $this->_response->setHeader('X-Api-Exception', $e->getMessage()); } } } // sondh@2014-09-26 // bypass confirmation if all requested scopes have been granted at some point // in old version of this add-on, it checked for scope from active tokens // from now on, we look for all scopes (no expiration) for better user experience // if a token expires, it should not invalidate all user's choices $userScopes = $oauth2Model->getUserScopeModel()->getUserScopes($client['client_id'], XenForo_Visitor::getUserId()); $paramScopes = bdApi_Template_Helper_Core::getInstance()->scopeSplit($authorizeParams['scope']); $paramScopesNew = array(); foreach ($paramScopes as $paramScope) { if (!isset($userScopes[$paramScope])) { $paramScopesNew[] = $paramScope; } } if (empty($paramScopesNew)) { $bypassConfirmation = true; } else { $authorizeParams['scope'] = bdApi_Template_Helper_Core::getInstance()->scopeJoin($paramScopesNew); } // sondh@2015-09-28 // disable bypassing confirmation for testing purpose if ($clientIsAuto) { $bypassConfirmation = false; } $response = $oauth2Model->getServer()->actionOauthAuthorize1($this, $authorizeParams); if (is_object($response) && $response instanceof XenForo_ControllerResponse_Abstract) { return $response; } if ($this->_request->isPost() || $bypassConfirmation) { $accept = $this->_input->filterSingle('accept', XenForo_Input::STRING); $accepted = !!$accept; if ($bypassConfirmation) { // sondh@2013-03-19 // of course if the dialog was bypassed, $accepted should be true $accepted = true; } if ($accepted) { // sondh@2014-09-26 // get all up to date user scopes and include in the new token // that means client only need to ask for a scope once and they will always have // that scope in future authorizations, even if they ask for less scope! // making it easy for client dev, they don't need to track whether they requested // a scope before. Just check the most recent token for that information. $paramScopes = bdApi_Template_Helper_Core::getInstance()->scopeSplit($authorizeParams['scope']); foreach ($userScopes as $userScope => $userScopeInfo) { if (!in_array($userScope, $paramScopes, true)) { $paramScopes[] = $userScope; } } $paramScopes = array_unique($paramScopes); asort($paramScopes); $authorizeParams['scope'] = bdApi_Template_Helper_Core::getInstance()->scopeJoin($paramScopes); } return $oauth2Model->getServer()->actionOauthAuthorize2($this, $authorizeParams, $accepted, XenForo_Visitor::getUserId()); } else { $viewParams = array('client' => $client, 'authorizeParams' => $authorizeParams, 'clientIsAuto' => $clientIsAuto); return $this->_getWrapper('account', 'api', $this->responseView('bdApi_ViewPublic_Account_Authorize', 'bdapi_account_authorize', $viewParams)); } }
public function actionPostTokenGoogle() { $client = $this->_getClientOrError(); /* @var $userExternalModel XenForo_Model_UserExternal */ $userExternalModel = $this->getModelFromCache('XenForo_Model_UserExternal'); $googleToken = $this->_input->filterSingle('google_token', XenForo_Input::STRING); $httpClient = XenForo_Helper_Http::getClient('https://www.googleapis.com/plus/v1/people/me'); $httpClient->setParameterGet('access_token', $googleToken); $response = $httpClient->request('GET'); $googleUser = json_decode($response->getBody(), true); if (empty($googleUser['id'])) { return $this->responseError(new XenForo_Phrase('bdapi_invalid_google_token')); } $googleAssoc = $userExternalModel->getExternalAuthAssociation('google', $googleUser['id']); if (empty($googleAssoc)) { $userData = array(); if (!empty($googleUser['displayName'])) { $testDw = XenForo_DataWriter::create('XenForo_DataWriter_User'); $testDw->set('username', $googleUser['displayName']); if (!$testDw->hasErrors()) { // good username $userData['username'] = $googleUser['displayName']; } } if (!empty($googleUser['emails'])) { foreach ($googleUser['emails'] as $googleEmail) { $userData['user_email'] = $googleEmail['value']; break; } } if (!empty($googleUser['birthday'])) { if (preg_match('#^(?<year>\\d+)-(?<month>\\d+)-(?<day>\\d+)$#', $googleUser['birthday'], $birthdayMatches)) { $userData['user_dob_year'] = $birthdayMatches['year']; $userData['user_dob_month'] = $birthdayMatches['month']; $userData['user_dob_day'] = $birthdayMatches['day']; } } $extraData = array('external_provider' => 'google', 'external_provider_key' => $googleUser['id']); if (!empty($userData['user_email'])) { $extraData['user_email'] = $userData['user_email']; } $extraData = serialize($extraData); $extraTimestamp = time() + bdApi_Option::get('refreshTokenTTLDays') * 86400; $userData += array('extra_data' => bdApi_Crypt::encryptTypeOne($extraData, $extraTimestamp), 'extra_timestamp' => $extraTimestamp); $data = array('status' => 'ok', 'message' => new XenForo_Phrase('bdapi_no_google_association_found'), 'user_data' => $userData); return $this->responseData('bdApi_ViewApi_OAuth_TokenGoogle_NoAssoc', $data); } return $this->_actionPostTokenNonStandard($client, $googleAssoc['user_id']); }