/** * @return bdApi_Template_Helper_Core */ public static function getInstance() { if (self::$_instance === null) { $templateHelperClass = XenForo_Application::resolveDynamicClass(__CLASS__, 'bdapi_template_helper'); self::$_instance = new $templateHelperClass(); } return self::$_instance; }
public function renderHtml() { if (!empty($this->_params['tokens'])) { foreach ($this->_params['tokens'] as &$token) { $token['_scopes'] = bdApi_Template_Helper_Core::getInstance()->scopeSplit($token['scope']); } } }
public function updateUserScopes($token) { $userScopes = $this->getUserScopes($token['client_id'], $token['user_id']); $scopes = bdApi_Template_Helper_Core::getInstance()->scopeSplit($token['scope']); foreach ($scopes as $scope) { if (!isset($userScopes[$scope])) { $this->_getDb()->insert('xf_bdapi_user_scope', array('client_id' => $token['client_id'], 'user_id' => $token['user_id'], 'scope' => $scope, 'accept_date' => XenForo_Application::$time)); } } }
public function renderHtml() { $this->_params['authorizeScopes'] = array(); if (!empty($this->_params['authorizeParams']['scope'])) { $this->_params['authorizeScopes'] = bdApi_Template_Helper_Core::getInstance()->scopeSplit($this->_params['authorizeParams']['scope']); } $this->_params['authorizeRedirectUri'] = ''; if (!empty($this->_params['authorizeParams']['redirect_uri'])) { $this->_params['authorizeRedirectUri'] = $this->_params['authorizeParams']['redirect_uri']; } }
public function canAutoAuthorize($client, $scopes) { $scopeArray = bdApi_Template_Helper_Core::getInstance()->scopeSplit($scopes); foreach ($scopeArray as $scope) { if (empty($client['options']['auto_authorize'][$scope])) { // at least one scope requested is missing // CANNOT auto authorize this set of scopes return false; } } return true; }
public function hasScope($client, $token, $scope) { $helper = bdApi_Template_Helper_Core::getInstance(); $scopeArray = $helper->scopeSplit($scope); $tokenScopeArray = $helper->scopeSplit($token['scope']); foreach ($scopeArray as $scopeSingle) { // use simple in_array check here without any normalization if (!in_array($scopeSingle, $tokenScopeArray)) { return false; } } return true; }
public static function init_dependencies(XenForo_Dependencies_Abstract $dependencies, array $data) { // initializes the core template helper object // in the future, we may have different template helpers for public/admin/api context $templateHelper = bdApi_Template_Helper_Core::getInstance(); // register the helper methods in the format `bdApi_<method_name>` $templateHelperReflector = new ReflectionClass(get_class($templateHelper)); $methods = $templateHelperReflector->getMethods(); foreach ($methods as $method) { if (!($method->getModifiers() & ReflectionMethod::IS_PUBLIC) or $method->getModifiers() & ReflectionMethod::IS_STATIC) { // ignore non-public instance methods continue; } $methodName = $method->getName(); $helperCallbackName = utf8_strtolower('api_' . $methodName); XenForo_Template_Helper_Core::$helperCallbacks[$helperCallbackName] = array($templateHelper, $methodName); } }
public static function init_dependencies(XenForo_Dependencies_Abstract $dependencies, array $data) { // initializes the core template helper object // in the future, we may have different template helpers for public/admin/api context $templateHelper = bdApi_Template_Helper_Core::getInstance(); // register the helper methods in the format `bdApi_<method_name>` $templateHelperReflector = new ReflectionClass(get_class($templateHelper)); $methods = $templateHelperReflector->getMethods(); foreach ($methods as $method) { if (!($method->getModifiers() & ReflectionMethod::IS_PUBLIC) || $method->getModifiers() & ReflectionMethod::IS_STATIC) { // ignore restricted or static methods continue; } $methodName = $method->getName(); $helperCallbackName = utf8_strtolower('api_' . $methodName); XenForo_Template_Helper_Core::$helperCallbacks[$helperCallbackName] = array($templateHelper, $methodName); } XenForo_CacheRebuilder_Abstract::$builders['bdApi_CacheRebuilder_ClientContentDeleteAll'] = 'bdApi_CacheRebuilder_ClientContentDeleteAll'; bdApi_ShippableHelper_Updater::onInitDependencies($dependencies, bdApi_Option::UPDATER_URL); }
public function actionAdd() { if ($this->isConfirmedPost()) { $clientId = $this->_input->filterSingle('client_id', XenForo_Input::STRING); $client = $this->_getClientOrError($clientId); $username = $this->_input->filterSingle('username', XenForo_Input::STRING); /* @var $userModel XenForo_Model_User */ $userModel = $this->getModelFromCache('XenForo_Model_User'); $user = $userModel->getUserByName($username); if (empty($user)) { return $this->responseError(new XenForo_Phrase('requested_user_not_found'), 404); } $scopes = $this->_input->filterSingle('scopes', XenForo_Input::ARRAY_SIMPLE); $scopes = bdApi_Template_Helper_Core::getInstance()->scopeJoin($scopes); $ttl = $this->_input->filterSingle('ttl', XenForo_Input::UINT); $this->_getOAuth2Model()->getServer()->createAccessToken($client['client_id'], $user['user_id'], $scopes, $ttl, false); return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, XenForo_Link::buildAdminLink('api-tokens')); } else { $viewParams = array('clients' => $this->_getClientModel()->getList(), 'scopes' => $this->_getOAuth2Model()->getSystemSupportedScopes()); return $this->responseView('bdApi_ViewAdmin_Token_Add', 'bdapi_token_add', $viewParams); } }
public function getAutoAndUserScopes($clientId, $userId) { $client = $this->getClientModel()->getClientById($clientId); if (empty($client)) { return ''; } $scopes = array(); if (!empty($client['options']['auto_authorize'])) { foreach ($client['options']['auto_authorize'] as $scope => $canAutoAuthorize) { if ($canAutoAuthorize) { $scopes[] = $scope; } } } $userScopes = $this->getUserScopeModel()->getUserScopes($client['client_id'], $userId); if (!empty($userScopes)) { foreach ($userScopes as $scope => $userScope) { $scopes[] = $scope; } $scopes = array_unique($scopes); } return bdApi_Template_Helper_Core::getInstance()->scopeJoin($scopes); }
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 = $oauth2Model->getClientModel()->getClientById($authorizeParams['client_id']); if (empty($client)) { throw new XenForo_Exception(new XenForo_Phrase('bdapi_authorize_error_client_x_not_found', array('client' => $authorizeParams['client_id']))); } // 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@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); } $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); return $this->_getWrapper('account', 'api', $this->responseView('bdApi_ViewPublic_Account_Authorize', 'bdapi_account_authorize', $viewParams)); } }
public function actionPostIndex() { /* @var $oauth2Model bdApi_Model_OAuth2 */ $oauth2Model = $this->getModelFromCache('bdApi_Model_OAuth2'); /* @var $userConfirmationModel XenForo_Model_UserConfirmation */ $userConfirmationModel = $this->getModelFromCache('XenForo_Model_UserConfirmation'); /* @var $session bdApi_Session */ $session = XenForo_Application::getSession(); $clientId = $session->getOAuthClientId(); $clientSecret = $session->getOAuthClientSecret(); if (empty($clientId) or empty($clientSecret)) { $clientId = $this->_input->filterSingle('client_id', XenForo_Input::STRING); $client = $oauth2Model->getClientModel()->getClientById($clientId); if (empty($client)) { return $this->responseError(new XenForo_Phrase('bdapi_post_slash_users_requires_client_id'), 400); } $clientSecret = $client['client_secret']; } $input = $this->_input->filter(array('user_email' => XenForo_Input::STRING, 'username' => XenForo_Input::STRING, 'password' => XenForo_Input::STRING, 'password_algo' => XenForo_Input::STRING, 'user_dob_day' => XenForo_Input::UINT, 'user_dob_month' => XenForo_Input::UINT, 'user_dob_year' => XenForo_Input::UINT)); if (empty($input['user_email'])) { // backward compatibility $input['user_email'] = $this->_input->filterSingle('email', XenForo_Input::STRING); } $extraInput = $this->_input->filter(array('extra_data' => XenForo_Input::STRING, 'extra_timestamp' => XenForo_Input::UINT)); if (!empty($extraInput['extra_data'])) { $extraData = bdApi_Crypt::decryptTypeOne($extraInput['extra_data'], $extraInput['extra_timestamp']); if (!empty($extraData)) { $extraData = @unserialize($extraData); } if (empty($extraData)) { $extraData = array(); } } $userModel = $this->_getUserModel(); $options = XenForo_Application::getOptions(); $session = XenForo_Application::getSession(); $visitor = XenForo_Visitor::getInstance(); /* @var $writer XenForo_DataWriter_User */ $writer = XenForo_DataWriter::create('XenForo_DataWriter_User'); $registrationDefaults = $options->get('registrationDefaults'); if (!empty($registrationDefaults)) { $writer->bulkSet($registrationDefaults, array('ignoreInvalidFields' => true)); } $writer->set('email', $input['user_email']); $writer->set('username', $input['username']); $password = bdApi_Crypt::decrypt($input['password'], $input['password_algo'], $clientSecret); if (!empty($password)) { $writer->setPassword($password, $password); } else { // no password or unable to decrypt password // create new user with no password auth scheme $auth = XenForo_Authentication_Abstract::create('XenForo_Authentication_NoPassword'); $writer->set('scheme_class', $auth->getClassName()); $writer->set('data', $auth->generate(''), 'xf_user_authenticate'); } if ($options->get('gravatarEnable') && XenForo_Model_Avatar::gravatarExists($input['user_email'])) { $writer->set('gravatar', $input['user_email']); } $writer->set('dob_day', $input['user_dob_day']); $writer->set('dob_month', $input['user_dob_month']); $writer->set('dob_year', $input['user_dob_year']); $writer->set('user_group_id', XenForo_Model_User::$defaultRegisteredGroupId); $writer->set('language_id', XenForo_Visitor::getInstance()->get('language_id')); $allowEmailConfirm = true; if (!empty($extraData['user_email']) && $extraData['user_email'] == $writer->get('email')) { // the email address has been validated by some other mean (external provider?) // do not require email confirmation again to avoid complication $allowEmailConfirm = false; } $writer->advanceRegistrationUserState($allowEmailConfirm); if ($visitor->hasAdminPermission('user') and $session->checkScope(bdApi_Model_OAuth2::SCOPE_MANAGE_SYSTEM)) { $writer->set('user_state', 'valid'); } $writer->save(); $user = $writer->getMergedData(); // log the ip of the user registering XenForo_Model_Ip::log(XenForo_Visitor::getUserId() ? XenForo_Visitor::getUserId() : $user['user_id'], 'user', $user['user_id'], 'register'); if ($user['user_state'] == 'email_confirm') { $userConfirmationModel->sendEmailConfirmation($user); } if (!empty($extraData['external_provider']) && !empty($extraData['external_provider_key'])) { /* @var $userExternalModel XenForo_Model_UserExternal */ $userExternalModel = $this->getModelFromCache('XenForo_Model_UserExternal'); $userExternalModel->updateExternalAuthAssociation($extraData['external_provider'], $extraData['external_provider_key'], $user['user_id']); } if (XenForo_Visitor::getUserId() == 0) { XenForo_Visitor::setup($user['user_id']); } $scopes = $oauth2Model->getSystemSupportedScopes(); $scopes = bdApi_Template_Helper_Core::getInstance()->scopeJoin($scopes); $token = $oauth2Model->getServer()->createAccessToken($clientId, $user['user_id'], $scopes); $user = $userModel->getUserById($user['user_id'], $userModel->getFetchOptionsToPrepareApiData()); $data = array('user' => $this->_filterDataSingle($this->_getUserModel()->prepareApiDataForUser($user)), '_user' => $user, 'token' => $token); return $this->responseData('bdApi_ViewApi_User_Single', $data); }
public function actionApiData() { /* @var $clientModel bdApi_Model_Client */ $clientModel = $this->getModelFromCache('bdApi_Model_Client'); /* @var $userScopeModel bdApi_Model_UserScope */ $userScopeModel = $this->getModelFromCache('bdApi_Model_UserScope'); /* @var $userModel bdApi_XenForo_Model_User */ $userModel = $this->getModelFromCache('XenForo_Model_User'); $callback = $this->_input->filterSingle('callback', XenForo_Input::STRING); $cmd = $this->_input->filterSingle('cmd', XenForo_Input::STRING); $clientId = $this->_input->filterSingle('client_id', XenForo_Input::STRING); $data = array(); $data[$cmd] = 0; $client = $clientModel->getClientById($clientId); $visitorObj = XenForo_Visitor::getInstance(); $visitorArray = $visitorObj->toArray(); if (!empty($client) and $visitorArray['user_id'] > 0) { switch ($cmd) { case 'authorized': $scope = $this->_input->filterSingle('scope', XenForo_Input::STRING); $requestedScopes = bdApi_Template_Helper_Core::getInstance()->scopeSplit($scope); if (empty($requestedScopes)) { // no scope requested, check for scope `read` $requestedScopes[] = bdApi_Model_OAuth2::SCOPE_READ; } $requestedScopesAccepted = array(); if ($data[$cmd] === 0 and $clientModel->canAutoAuthorize($client, $scope)) { // this client has auto authorize setting for the requested scope // response with authorized = 1 // note: we don't have (and don't need) an access token for now // but in case the client application request authorization, it // will be granted automatically anyway $requestedScopesAccepted = $requestedScopes; $data[$cmd] = 1; } if ($data[$cmd] === 0) { // start looking for accepted scopes $userScopes = $userScopeModel->getUserScopes($client['client_id'], $visitorArray['user_id']); foreach ($requestedScopes as $scope) { foreach ($userScopes as $userScope) { if ($userScope['scope'] === $scope) { $requestedScopesAccepted[] = $scope; } } } if (count($requestedScopes) === count($requestedScopesAccepted)) { $data[$cmd] = 1; } } if ($data[$cmd] === 1) { if (!empty($scope)) { // some actual scopes were requested, return user data according to those scopes $session = new bdApi_Session(); $session->fakeStart($client, $visitorObj, $requestedScopesAccepted); $visitorPrepared = $userModel->prepareApiDataForUser($visitorArray); $data = array_merge($visitorPrepared, $data); } else { // just checking for connection status, return user_id only $data['user_id'] = $visitorArray['user_id']; } } // switch ($cmd) break; } } $clientModel->signApiData($client, $data); $viewParams = array('callback' => $callback, 'cmd' => $cmd, 'client_id' => $clientId, 'data' => $data); $this->_routeMatch->setResponseType('raw'); return $this->responseView('bdApi_ViewPublic_Misc_Api_Data', '', $viewParams); }
public function getScope() { $storage = $this->storage; if ($storage instanceof bdApi_OAuth2_Storage) { if ($this->getUserId() > 0) { return $storage->getModel()->getAutoAndUserScopes($this->getClientId(), $this->getUserId()); } else { return bdApi_Template_Helper_Core::getInstance()->scopeJoin($storage->getModel()->getSystemSupportedScopes()); } } return ''; }
public function fakeStart(array $client, XenForo_Visitor $visitor, array $scopes) { $this->_oauthToken = array('token_id' => 0, 'client_id' => $client['client_id'], 'token_text' => '', 'expire_date' => XenForo_Application::$time, 'issue_date' => XenForo_Application::$time, 'user_id' => $visitor['user_id'], 'scope' => bdApi_Template_Helper_Core::getInstance()->scopeJoin($scopes)); $this->changeUserId($visitor['user_id']); $this->set('scopes', $scopes); XenForo_Application::set('_bdApi_session', $this); }
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)); } }