From f7ed3ce5df2f883213b79e0c5742b6b2fe58dac5 Mon Sep 17 00:00:00 2001 From: Manoj L Date: Thu, 16 May 2019 14:38:57 +0530 Subject: [PATCH 1/6] Issue #19 feat: Add support for sign-in / sign-up using JFBConnect --- src/language/en-GB/en-GB.plg_api_users.ini | 13 + src/users.php | 24 +- src/users/jfbconnect.php | 406 +++++++++++++++++++++ src/users/user.php | 12 +- src/users/users.php | 12 +- 5 files changed, 447 insertions(+), 20 deletions(-) create mode 100644 src/users/jfbconnect.php diff --git a/src/language/en-GB/en-GB.plg_api_users.ini b/src/language/en-GB/en-GB.plg_api_users.ini index 1e75e9c..641e4ac 100755 --- a/src/language/en-GB/en-GB.plg_api_users.ini +++ b/src/language/en-GB/en-GB.plg_api_users.ini @@ -1,3 +1,11 @@ +; @package API +; @subpackage plg_api_users +; +; @author Techjoomla +; @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. +; @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL +; Note All ini files need to be saved as UTF-8 + PLG_API_USERS="API - Users" PLG_API_USERS_DESCRIPTION="This plugin exposes users to the Joomla! API. Supports creation, listing and login for users." PLG_API_USERS_BAD_REQUEST_MESSAGE="Bad request" @@ -15,3 +23,8 @@ PLG_API_USERS_UNSUPPORTED_METHOD_POST="unsupported method,please use get method" PLG_API_USERS_USERS="users/" PLG_API_USERS_IN_DELETE="in delete" PLG_API_USERS_IN_POST="in post" + +; Since v2.1.0 +PLG_API_USERS_JFBCONNECT_NOT_INSTALLED="JFBConnect not installed or not enabled" +PLG_API_USERS_JFBCONNECT_MISSING_PROVIDER="Provider not sent" +PLG_API_USERS_JFBCONNECT_MISSING_ACCESS_TOKEN="Access token not sent" diff --git a/src/users.php b/src/users.php index bd8e736..824ebe3 100644 --- a/src/users.php +++ b/src/users.php @@ -1,12 +1,15 @@ - * @link http://www.techjoomla.com -*/ + * @package API + * @subpackage plg_api_users + * + * @author Techjoomla + * @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ -defined('_JEXEC') or die( 'Restricted access' ); +// No direct access. +defined('_JEXEC') or die('Restricted access'); jimport('joomla.plugin.plugin'); @@ -17,15 +20,16 @@ public function __construct(&$subject, $config = array()) parent::__construct($subject, $config = array()); ApiResource::addIncludePath(dirname(__FILE__).'/users'); - - /*load language file for plugin frontend*/ - $lang = JFactory::getLanguage(); + + // Load language file for plugin frontend + $lang = JFactory::getLanguage(); $lang->load('plg_api_users', JPATH_ADMINISTRATOR,'',true); - + // Set the login resource to be public $this->setResourceAccess('login', 'public','get'); $this->setResourceAccess('users', 'public', 'post'); $this->setResourceAccess('config', 'public', 'get'); $this->setResourceAccess('user', 'public', 'post'); + $this->setResourceAccess('jfbconnect', 'public', 'post'); } } diff --git a/src/users/jfbconnect.php b/src/users/jfbconnect.php new file mode 100644 index 0000000..2cb73ca --- /dev/null +++ b/src/users/jfbconnect.php @@ -0,0 +1,406 @@ + + * @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +/** + * @package JFBConnect + * @copyright (c) 2009-2019 by SourceCoast - All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + * @version Release v8.1.0 + * @build-date 2019/04/03 + */ + +// No direct access. +defined('_JEXEC') or die('Restricted access'); + +require_once JPATH_SITE . '/components/com_api/vendors/php-jwt/src/JWT.php'; + +use Firebase\JWT\JWT; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; + +JModelLegacy::addIncludePath(JPATH_SITE . 'components/com_api/models'); +require_once JPATH_ADMINISTRATOR . '/components/com_api/models/key.php'; +require_once JPATH_ADMINISTRATOR . '/components/com_api/models/keys.php'; + +/** + * UsersApiResourceJfbconnect class + * + * @since 2.0.1 + */ +class UsersApiResourceJfbconnect extends ApiResource +{ + public $provider = ''; + + public $accessToken = ''; + + /** + * GET method for this resource + * + * @return mixed + * + * @since 2.0.1 + */ + public function get() + { + // Validate if JFB is installed + $this->validateInstall(); + + // $this->plugin->setResponse(JText::_('PLG_API_USERS_UNSUPPORTED_METHOD')); + + ApiError::raiseError(405, JText::_('PLG_API_USERS_UNSUPPORTED_METHOD')); + } + + /** + * GET method for this resource + * + * @return mixed + * + * @since 2.0.1 + */ + public function post() + { + // Validate if JFB is installed + $this->validateInstall(); + + // Init vars + $app = JFactory::getApplication(); + $providerName = $app->input->json->get('provider', '', 'STRING'); + $accessToken = $app->input->json->get('access_token', '', 'STRING'); + + if (empty($providerName)) + { + ApiError::raiseError(400, JText::_('PLG_API_USERS_JFBCONNECT_MISSING_PROVIDER')); + } + + if (empty($accessToken)) + { + ApiError::raiseError(400, JText::_('PLG_API_USERS_JFBCONNECT_MISSING_ACCESS_TOKEN')); + } + + // Get provider object + $provider = $this->jfbGetProvider($providerName); + + // Based on: JFB code from components/com_jfbconnect/controllers/authenticate.php callback() + + /*try + { + $provider->client->authenticate(); + } + catch (Exception $e) + { + ApiError::raiseError(400, JText::_('api auth error')); + }*/ + + /*echo '
provider class is: ' . get_class($provider); + $methods = get_class_methods($provider); + foreach($methods as $method) { echo $method; echo "
";} + */ + + // Look for if JFB user mapping exists, get jUserId + $jUserId = $this->jfbGetJoomlaUserId($provider, $accessToken); + + // If user not found, try registering new user + if (!$jUserId) + { + $jUserId = $this->jfbRegisterUser($provider, $accessToken); + } + + $this->plugin->setResponse($this->generateApiToken($jUserId)); + } + + /** + * Validates if JFBConnect is installed and enabled + * + * @return boolean + * + * @since v2.0.1 + */ + private function validateInstall() + { + jimport('joomla.filesystem.file'); + + // Check if JFB is installed and enabled + if (JFile::exists(JPATH_ROOT . '/components/com_jfbconnect/jfbconnect.php') + && JComponentHelper::isEnabled('com_jfbconnect', true)) + { + return true; + } + + ApiError::raiseError(500, JText::_('PLG_API_USERS_JFBCONNECT_NOT_INSTALLED')); + + return false; + } + + /** + * Returns JFBConnect provider class object + * + * @param string $providerName Provider name eg - google / facebook + * + * @return object + * + * @since 2.0.1 + */ + private function jfbGetProvider($providerName) + { + // Based on: JFB code from components/com_jfbconnect/controllers/authenticate.php getProvider() + if ($providerName) + { + $provider = JFBCFactory::provider($providerName); + + if (empty($provider->name)) + { + ApiError::raiseError(500, JText::_('Invalid provider')); + } + + return $provider; + } + + return; + } + + /** + * Returns Joomla user id from jfb user mapping + * + * @param object $provider JFBCOnnect provider class object + * + * @param string $accessToken Provider access token + * + * @return int + * + * @since 2.0.1 + */ + public function jfbGetJoomlaUserId($provider, $accessToken) + { + $jUserId = 0; + + if (strtolower($provider->name) == 'google') + { + // Based on: JFB code from components/com_jfbconnect/libraries/provider/google.php -> setupAuthentication() + // Google client needs access token as array + $accessToken = array('access_token' => $accessToken); + $provider->client->setToken($accessToken); + } + elseif (strtolower($provider->name) == 'facebook') + { + // Based on: JFB code from administrator/assets/facebook-api/base_facebook.php -> setAccessToken() + $provider->client->setAccessToken($accessToken); + } + + // Based on: JFB code from components/com_jfbconnect/controllers/login.php login() + $providerUserId = $provider->getProviderUserId(); + $userMapModel = JFBCFactory::usermap(); + + // Check if they have a Joomla user and log that user in. If not, create them one + $jUserId = $userMapModel->getJoomlaUserId($providerUserId, strtolower($provider->name)); + + return $jUserId; + } + + /** + * Register new user using JFB + * + * @param object $provider JFBCOnnect provider class object + * + * @return int + * + * @since 2.0.1 + */ + private function jfbRegisterUser($provider) + { + // Declare vars needed for JFB code to work + BaseDatabaseModel::addIncludePath(JPATH_SITE . '/components/com_jfbconnect/models'); + $loginRegisterModel = JModelLegacy::getInstance('LoginRegister', 'JFBConnectModel'); + $userMapModel = JFBCFactory::usermap(); + $app = JFactory::getApplication(); + $providerUserId = $provider->getProviderUserId(); + $jUserId = 0; + + // START - Use JFB code + // Based on: JFB code from components/com_jfbconnect/controllers/login.php login() + + $profile = $provider->profile->fetchProfile($providerUserId, array('email')); + $providerEmail = $profile->get('email', null); + + // Check if automatic email mapping is allowed, and see if that email is registered + // AND the Facebook user doesn't already have a Joomla account + if (!$provider->initialRegistration && JFBCFactory::config()->getSetting('facebook_auto_map_by_email')) + { + if ($providerEmail != null) + { + $jUserEmailId = $userMapModel->getJoomlaUserIdFromEmail($providerEmail); + + if (!empty($jUserEmailId)) + { + // Found a user with the same email address + // do final check to make sure there isn't a FB account already mapped to it + $tempId = $userMapModel->getProviderUserId($jUserEmailId, strtolower($provider->name)); + + if (!$tempId) + { + JFBConnectUtilities::clearJFBCNewMappingEnabled(); + + if ($userMapModel->map($jUserEmailId, $providerUserId, strtolower($provider->name), $provider->client->getToken())) + { + JFBCFactory::log(JText::sprintf('COM_JFBCONNECT_MAP_USER_SUCCESS', $provider->name)); + + // Update the temp jId so that we login below + $jUserId = $jUserEmailId; + } + else + { + JFBCFactory::log(JText::sprintf('COM_JFBCONNECT_MAP_USER_FAIL', $provider->name)); + } + } + } + } + } + + /* + * check if user registration is turn off + * !allowUserRegistration and !social_registration => registration not allowed + * !allowUserRegistration and social_registration => registration allowed + * allowUserRegistration and !social_registration => registration not allowed + * JComponentHelper::getParams('com_users')->get('allowUserRegistration') check is not needed since + * we prioritized the JFBConnect social registration config + */ + + if (JFBCFactory::config()->getSetting('social_registration') == 0 && !$jUserId) + { + JFBCFactory::log(JText::_('COM_JFBCONNECT_MSG_USER_REGISTRATION_DISABLED'), 'notice'); + + // Commmented code below for com_api plugin + // $app->redirect(JRoute::_('index.php?option=com_users&view=login', false)); + + return false; + } + + // Check if no mapping, and Automatic Registration is set. If so, auto-create the new user. + if (!$jUserId && JFBCFactory::config()->getSetting('automatic_registration')) + { + // User is not in system, should create their account automatically + if ($loginRegisterModel->autoCreateUser($providerUserId, $provider)) + { + $jUserId = $userMapModel->getJoomlaUserId($providerUserId, strtolower($provider->name)); + } + } + + // END - use JFB code + + return $jUserId; + } + + /** + * Generate API token + * + * @param INT $userId user id + * + * @return mixed + * + * @since 2.0.1 + */ + private function generateApiToken($userId) + { + // Validate + $obj = new stdclass; + + if ($userId == null) + { + $obj->code = 403; + $obj->message = JText::_('PLG_API_USERS_USER_NOT_FOUND_MESSAGE'); + + return $obj; + } + + // Init vars + $app = JFactory::getApplication(); + $keyModel = new ApiModelKey; + $keysModel = new ApiModelKeys; + $key = null; + + // Get existing key for $userId user + $keysModel->setState('user_id', $userId); + $existingKey = $keysModel->getItems(); + $existingKey = (!empty($existingKey)) ? $existingKey[count($existingKey) - count($existingKey)] : $existingKey; + + if (!empty($existingKey)) + { + $key = $existingKey->hash; + } + // If key not found, create new + elseif ($key == null || empty($key)) + { + // Create new key for user + $data = array ( + 'userid' => $userId, + 'domain' => '' , + 'state' => 1, + 'id' => '', + 'task' => 'save', + 'c' => 'key', + 'ret' => 'index.php?option=com_api&view=keys', + 'option' => 'com_api', + JSession::getFormToken() => 1 + ); + + $result = $keyModel->save($data); + + if (!$result) + { + return false; + } + + // Load api key table + JTable::addIncludePath(JPATH_ROOT . '/administrator/components/com_api/tables'); + $table = JTable::getInstance('Key', 'ApiTable'); + $table->load(array('userid' => $userId)); + $key = $table->hash; + } + + if (!empty($key)) + { + $obj->auth = $key; + $obj->code = '200'; + + // Set user details for response + $obj->id = $userId; + $obj->name = JFactory::getUser($userId)->name; + $obj->username = JFactory::getUser($userId)->username; + $obj->email = JFactory::getUser($userId)->email; + + // Generate claim for jwt + $data = [ + "id" => trim($userId) + + /*"iat" => '', + "exp" => '', + "aud" => '', + "sub" => ''"*/ + ]; + + // Using HS256 algo to generate JWT + $jwt = JWT::encode($data, trim($key), 'HS256'); + + if (isset($jwt) && $jwt != '') + { + $obj->jwt = $jwt; + } + else + { + $obj->jwt = false; + } + } + else + { + $obj->code = 403; + $obj->message = JText::_('PLG_API_USERS_BAD_REQUEST_MESSAGE'); + } + + return ($obj); + } +} diff --git a/src/users/user.php b/src/users/user.php index bee8825..50bacf7 100644 --- a/src/users/user.php +++ b/src/users/user.php @@ -1,13 +1,15 @@ + * @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL */ // No direct access. -defined('_JEXEC') or die(); +defined('_JEXEC') or die('Restricted access'); /** * User Api. diff --git a/src/users/users.php b/src/users/users.php index 9b6c313..86e51c0 100644 --- a/src/users/users.php +++ b/src/users/users.php @@ -1,13 +1,15 @@ + * @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL */ + // No direct access. -defined('_JEXEC') or die; +defined('_JEXEC') or die('Restricted access'); jimport('joomla.user.user'); jimport('joomla.plugin.plugin'); From 10dabf843fa113ba51e279bbb363e580ccce7a3a Mon Sep 17 00:00:00 2001 From: Manoj L Date: Thu, 16 May 2019 15:00:08 +0530 Subject: [PATCH 2/6] Issue #19 fix: Scrutinizer fixes --- src/users/jfbconnect.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/users/jfbconnect.php b/src/users/jfbconnect.php index 2cb73ca..1418ff6 100644 --- a/src/users/jfbconnect.php +++ b/src/users/jfbconnect.php @@ -108,7 +108,7 @@ public function post() // If user not found, try registering new user if (!$jUserId) { - $jUserId = $this->jfbRegisterUser($provider, $accessToken); + $jUserId = $this->jfbRegisterUser($provider); } $this->plugin->setResponse($this->generateApiToken($jUserId)); @@ -177,8 +177,6 @@ private function jfbGetProvider($providerName) */ public function jfbGetJoomlaUserId($provider, $accessToken) { - $jUserId = 0; - if (strtolower($provider->name) == 'google') { // Based on: JFB code from components/com_jfbconnect/libraries/provider/google.php -> setupAuthentication() @@ -217,7 +215,6 @@ private function jfbRegisterUser($provider) BaseDatabaseModel::addIncludePath(JPATH_SITE . '/components/com_jfbconnect/models'); $loginRegisterModel = JModelLegacy::getInstance('LoginRegister', 'JFBConnectModel'); $userMapModel = JFBCFactory::usermap(); - $app = JFactory::getApplication(); $providerUserId = $provider->getProviderUserId(); $jUserId = 0; @@ -275,9 +272,11 @@ private function jfbRegisterUser($provider) JFBCFactory::log(JText::_('COM_JFBCONNECT_MSG_USER_REGISTRATION_DISABLED'), 'notice'); // Commmented code below for com_api plugin + // $app->redirect(JRoute::_('index.php?option=com_users&view=login', false)); + // return false; - return false; + return 0; } // Check if no mapping, and Automatic Registration is set. If so, auto-create the new user. @@ -298,7 +297,7 @@ private function jfbRegisterUser($provider) /** * Generate API token * - * @param INT $userId user id + * @param int $userId user id * * @return mixed * @@ -318,7 +317,6 @@ private function generateApiToken($userId) } // Init vars - $app = JFactory::getApplication(); $keyModel = new ApiModelKey; $keysModel = new ApiModelKeys; $key = null; From 6af5ca1cf2823129cb477d1b54c23e8e321d7473 Mon Sep 17 00:00:00 2001 From: Manoj L Date: Thu, 16 May 2019 16:28:45 +0530 Subject: [PATCH 3/6] Issue #19 fix: Scrutinizer fixes --- src/users/jfbconnect.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/users/jfbconnect.php b/src/users/jfbconnect.php index 1418ff6..2b9e48d 100644 --- a/src/users/jfbconnect.php +++ b/src/users/jfbconnect.php @@ -160,8 +160,6 @@ private function jfbGetProvider($providerName) return $provider; } - - return; } /** @@ -220,7 +218,6 @@ private function jfbRegisterUser($provider) // START - Use JFB code // Based on: JFB code from components/com_jfbconnect/controllers/login.php login() - $profile = $provider->profile->fetchProfile($providerUserId, array('email')); $providerEmail = $profile->get('email', null); From 8ffd8b3de6f5a2f50a859dda320bc1434090e466 Mon Sep 17 00:00:00 2001 From: Manoj L Date: Wed, 26 Jun 2019 11:08:29 +0530 Subject: [PATCH 4/6] Issue #19 feat: Add support for sign-in / sign-up using JFBConnect --- src/language/en-GB/en-GB.plg_api_users.ini | 5 - src/users.php | 20 +- src/users/jfbconnect.php | 401 --------------------- src/users/login.php | 74 ++-- 4 files changed, 56 insertions(+), 444 deletions(-) delete mode 100644 src/users/jfbconnect.php diff --git a/src/language/en-GB/en-GB.plg_api_users.ini b/src/language/en-GB/en-GB.plg_api_users.ini index 641e4ac..17739ba 100755 --- a/src/language/en-GB/en-GB.plg_api_users.ini +++ b/src/language/en-GB/en-GB.plg_api_users.ini @@ -23,8 +23,3 @@ PLG_API_USERS_UNSUPPORTED_METHOD_POST="unsupported method,please use get method" PLG_API_USERS_USERS="users/" PLG_API_USERS_IN_DELETE="in delete" PLG_API_USERS_IN_POST="in post" - -; Since v2.1.0 -PLG_API_USERS_JFBCONNECT_NOT_INSTALLED="JFBConnect not installed or not enabled" -PLG_API_USERS_JFBCONNECT_MISSING_PROVIDER="Provider not sent" -PLG_API_USERS_JFBCONNECT_MISSING_ACCESS_TOKEN="Access token not sent" diff --git a/src/users.php b/src/users.php index 824ebe3..7de517b 100644 --- a/src/users.php +++ b/src/users.php @@ -13,23 +13,33 @@ jimport('joomla.plugin.plugin'); -class plgAPIUsers extends ApiPlugin +/** + * Users plgAPI class + * + * @since 1.0.0 + */ +class PlgAPIUsers extends ApiPlugin { + /** + * Constructor + * + * @param string &$subject subject + * @param string $config config + */ public function __construct(&$subject, $config = array()) { parent::__construct($subject, $config = array()); - ApiResource::addIncludePath(dirname(__FILE__).'/users'); + ApiResource::addIncludePath(dirname(__FILE__) . '/users'); // Load language file for plugin frontend $lang = JFactory::getLanguage(); - $lang->load('plg_api_users', JPATH_ADMINISTRATOR,'',true); + $lang->load('plg_api_users', JPATH_ADMINISTRATOR, '', true); // Set the login resource to be public - $this->setResourceAccess('login', 'public','get'); + $this->setResourceAccess('login', 'public', 'get'); $this->setResourceAccess('users', 'public', 'post'); $this->setResourceAccess('config', 'public', 'get'); $this->setResourceAccess('user', 'public', 'post'); - $this->setResourceAccess('jfbconnect', 'public', 'post'); } } diff --git a/src/users/jfbconnect.php b/src/users/jfbconnect.php deleted file mode 100644 index 2b9e48d..0000000 --- a/src/users/jfbconnect.php +++ /dev/null @@ -1,401 +0,0 @@ - - * @copyright Copyright (C) 2009 - 2019 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL - */ - -/** - * @package JFBConnect - * @copyright (c) 2009-2019 by SourceCoast - All Rights Reserved - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL - * @version Release v8.1.0 - * @build-date 2019/04/03 - */ - -// No direct access. -defined('_JEXEC') or die('Restricted access'); - -require_once JPATH_SITE . '/components/com_api/vendors/php-jwt/src/JWT.php'; - -use Firebase\JWT\JWT; -use Joomla\CMS\MVC\Model\BaseDatabaseModel; - -JModelLegacy::addIncludePath(JPATH_SITE . 'components/com_api/models'); -require_once JPATH_ADMINISTRATOR . '/components/com_api/models/key.php'; -require_once JPATH_ADMINISTRATOR . '/components/com_api/models/keys.php'; - -/** - * UsersApiResourceJfbconnect class - * - * @since 2.0.1 - */ -class UsersApiResourceJfbconnect extends ApiResource -{ - public $provider = ''; - - public $accessToken = ''; - - /** - * GET method for this resource - * - * @return mixed - * - * @since 2.0.1 - */ - public function get() - { - // Validate if JFB is installed - $this->validateInstall(); - - // $this->plugin->setResponse(JText::_('PLG_API_USERS_UNSUPPORTED_METHOD')); - - ApiError::raiseError(405, JText::_('PLG_API_USERS_UNSUPPORTED_METHOD')); - } - - /** - * GET method for this resource - * - * @return mixed - * - * @since 2.0.1 - */ - public function post() - { - // Validate if JFB is installed - $this->validateInstall(); - - // Init vars - $app = JFactory::getApplication(); - $providerName = $app->input->json->get('provider', '', 'STRING'); - $accessToken = $app->input->json->get('access_token', '', 'STRING'); - - if (empty($providerName)) - { - ApiError::raiseError(400, JText::_('PLG_API_USERS_JFBCONNECT_MISSING_PROVIDER')); - } - - if (empty($accessToken)) - { - ApiError::raiseError(400, JText::_('PLG_API_USERS_JFBCONNECT_MISSING_ACCESS_TOKEN')); - } - - // Get provider object - $provider = $this->jfbGetProvider($providerName); - - // Based on: JFB code from components/com_jfbconnect/controllers/authenticate.php callback() - - /*try - { - $provider->client->authenticate(); - } - catch (Exception $e) - { - ApiError::raiseError(400, JText::_('api auth error')); - }*/ - - /*echo '
provider class is: ' . get_class($provider); - $methods = get_class_methods($provider); - foreach($methods as $method) { echo $method; echo "
";} - */ - - // Look for if JFB user mapping exists, get jUserId - $jUserId = $this->jfbGetJoomlaUserId($provider, $accessToken); - - // If user not found, try registering new user - if (!$jUserId) - { - $jUserId = $this->jfbRegisterUser($provider); - } - - $this->plugin->setResponse($this->generateApiToken($jUserId)); - } - - /** - * Validates if JFBConnect is installed and enabled - * - * @return boolean - * - * @since v2.0.1 - */ - private function validateInstall() - { - jimport('joomla.filesystem.file'); - - // Check if JFB is installed and enabled - if (JFile::exists(JPATH_ROOT . '/components/com_jfbconnect/jfbconnect.php') - && JComponentHelper::isEnabled('com_jfbconnect', true)) - { - return true; - } - - ApiError::raiseError(500, JText::_('PLG_API_USERS_JFBCONNECT_NOT_INSTALLED')); - - return false; - } - - /** - * Returns JFBConnect provider class object - * - * @param string $providerName Provider name eg - google / facebook - * - * @return object - * - * @since 2.0.1 - */ - private function jfbGetProvider($providerName) - { - // Based on: JFB code from components/com_jfbconnect/controllers/authenticate.php getProvider() - if ($providerName) - { - $provider = JFBCFactory::provider($providerName); - - if (empty($provider->name)) - { - ApiError::raiseError(500, JText::_('Invalid provider')); - } - - return $provider; - } - } - - /** - * Returns Joomla user id from jfb user mapping - * - * @param object $provider JFBCOnnect provider class object - * - * @param string $accessToken Provider access token - * - * @return int - * - * @since 2.0.1 - */ - public function jfbGetJoomlaUserId($provider, $accessToken) - { - if (strtolower($provider->name) == 'google') - { - // Based on: JFB code from components/com_jfbconnect/libraries/provider/google.php -> setupAuthentication() - // Google client needs access token as array - $accessToken = array('access_token' => $accessToken); - $provider->client->setToken($accessToken); - } - elseif (strtolower($provider->name) == 'facebook') - { - // Based on: JFB code from administrator/assets/facebook-api/base_facebook.php -> setAccessToken() - $provider->client->setAccessToken($accessToken); - } - - // Based on: JFB code from components/com_jfbconnect/controllers/login.php login() - $providerUserId = $provider->getProviderUserId(); - $userMapModel = JFBCFactory::usermap(); - - // Check if they have a Joomla user and log that user in. If not, create them one - $jUserId = $userMapModel->getJoomlaUserId($providerUserId, strtolower($provider->name)); - - return $jUserId; - } - - /** - * Register new user using JFB - * - * @param object $provider JFBCOnnect provider class object - * - * @return int - * - * @since 2.0.1 - */ - private function jfbRegisterUser($provider) - { - // Declare vars needed for JFB code to work - BaseDatabaseModel::addIncludePath(JPATH_SITE . '/components/com_jfbconnect/models'); - $loginRegisterModel = JModelLegacy::getInstance('LoginRegister', 'JFBConnectModel'); - $userMapModel = JFBCFactory::usermap(); - $providerUserId = $provider->getProviderUserId(); - $jUserId = 0; - - // START - Use JFB code - // Based on: JFB code from components/com_jfbconnect/controllers/login.php login() - $profile = $provider->profile->fetchProfile($providerUserId, array('email')); - $providerEmail = $profile->get('email', null); - - // Check if automatic email mapping is allowed, and see if that email is registered - // AND the Facebook user doesn't already have a Joomla account - if (!$provider->initialRegistration && JFBCFactory::config()->getSetting('facebook_auto_map_by_email')) - { - if ($providerEmail != null) - { - $jUserEmailId = $userMapModel->getJoomlaUserIdFromEmail($providerEmail); - - if (!empty($jUserEmailId)) - { - // Found a user with the same email address - // do final check to make sure there isn't a FB account already mapped to it - $tempId = $userMapModel->getProviderUserId($jUserEmailId, strtolower($provider->name)); - - if (!$tempId) - { - JFBConnectUtilities::clearJFBCNewMappingEnabled(); - - if ($userMapModel->map($jUserEmailId, $providerUserId, strtolower($provider->name), $provider->client->getToken())) - { - JFBCFactory::log(JText::sprintf('COM_JFBCONNECT_MAP_USER_SUCCESS', $provider->name)); - - // Update the temp jId so that we login below - $jUserId = $jUserEmailId; - } - else - { - JFBCFactory::log(JText::sprintf('COM_JFBCONNECT_MAP_USER_FAIL', $provider->name)); - } - } - } - } - } - - /* - * check if user registration is turn off - * !allowUserRegistration and !social_registration => registration not allowed - * !allowUserRegistration and social_registration => registration allowed - * allowUserRegistration and !social_registration => registration not allowed - * JComponentHelper::getParams('com_users')->get('allowUserRegistration') check is not needed since - * we prioritized the JFBConnect social registration config - */ - - if (JFBCFactory::config()->getSetting('social_registration') == 0 && !$jUserId) - { - JFBCFactory::log(JText::_('COM_JFBCONNECT_MSG_USER_REGISTRATION_DISABLED'), 'notice'); - - // Commmented code below for com_api plugin - - // $app->redirect(JRoute::_('index.php?option=com_users&view=login', false)); - // return false; - - return 0; - } - - // Check if no mapping, and Automatic Registration is set. If so, auto-create the new user. - if (!$jUserId && JFBCFactory::config()->getSetting('automatic_registration')) - { - // User is not in system, should create their account automatically - if ($loginRegisterModel->autoCreateUser($providerUserId, $provider)) - { - $jUserId = $userMapModel->getJoomlaUserId($providerUserId, strtolower($provider->name)); - } - } - - // END - use JFB code - - return $jUserId; - } - - /** - * Generate API token - * - * @param int $userId user id - * - * @return mixed - * - * @since 2.0.1 - */ - private function generateApiToken($userId) - { - // Validate - $obj = new stdclass; - - if ($userId == null) - { - $obj->code = 403; - $obj->message = JText::_('PLG_API_USERS_USER_NOT_FOUND_MESSAGE'); - - return $obj; - } - - // Init vars - $keyModel = new ApiModelKey; - $keysModel = new ApiModelKeys; - $key = null; - - // Get existing key for $userId user - $keysModel->setState('user_id', $userId); - $existingKey = $keysModel->getItems(); - $existingKey = (!empty($existingKey)) ? $existingKey[count($existingKey) - count($existingKey)] : $existingKey; - - if (!empty($existingKey)) - { - $key = $existingKey->hash; - } - // If key not found, create new - elseif ($key == null || empty($key)) - { - // Create new key for user - $data = array ( - 'userid' => $userId, - 'domain' => '' , - 'state' => 1, - 'id' => '', - 'task' => 'save', - 'c' => 'key', - 'ret' => 'index.php?option=com_api&view=keys', - 'option' => 'com_api', - JSession::getFormToken() => 1 - ); - - $result = $keyModel->save($data); - - if (!$result) - { - return false; - } - - // Load api key table - JTable::addIncludePath(JPATH_ROOT . '/administrator/components/com_api/tables'); - $table = JTable::getInstance('Key', 'ApiTable'); - $table->load(array('userid' => $userId)); - $key = $table->hash; - } - - if (!empty($key)) - { - $obj->auth = $key; - $obj->code = '200'; - - // Set user details for response - $obj->id = $userId; - $obj->name = JFactory::getUser($userId)->name; - $obj->username = JFactory::getUser($userId)->username; - $obj->email = JFactory::getUser($userId)->email; - - // Generate claim for jwt - $data = [ - "id" => trim($userId) - - /*"iat" => '', - "exp" => '', - "aud" => '', - "sub" => ''"*/ - ]; - - // Using HS256 algo to generate JWT - $jwt = JWT::encode($data, trim($key), 'HS256'); - - if (isset($jwt) && $jwt != '') - { - $obj->jwt = $jwt; - } - else - { - $obj->jwt = false; - } - } - else - { - $obj->code = 403; - $obj->message = JText::_('PLG_API_USERS_BAD_REQUEST_MESSAGE'); - } - - return ($obj); - } -} diff --git a/src/users/login.php b/src/users/login.php index ba2b5b8..3abf088 100644 --- a/src/users/login.php +++ b/src/users/login.php @@ -12,16 +12,8 @@ defined('_JEXEC') or die('Restricted access'); require_once JPATH_SITE . '/components/com_api/vendors/php-jwt/src/JWT.php'; - use Firebase\JWT\JWT; -jimport('joomla.plugin.plugin'); -jimport('joomla.html.html'); -jimport('joomla.application.component.controller'); -jimport('joomla.application.component.model'); -jimport('joomla.user.helper'); -jimport('joomla.user.user'); -jimport('joomla.application.component.helper'); JModelLegacy::addIncludePath(JPATH_SITE . 'components/com_api/models'); require_once JPATH_SITE . '/components/com_api/libraries/authentication/user.php'; @@ -64,34 +56,43 @@ public function post() */ public function keygen() { - // Init variable - $obj = new stdclass; - $umodel = new JUser; - $user = $umodel->getInstance(); - + // Init variables + $obj = new stdclass; $app = JFactory::getApplication(); $username = $app->input->get('username', 0, 'STRING'); $user = JFactory::getUser(); - $id = JUserHelper::getUserId($username); - if ($id == null) + if ($username) { - $model = FD::model('Users'); - $id = $model->getUserId('email', $username); + $umodel = new JUser; + $user = $umodel->getInstance(); + + $userId = JUserHelper::getUserId($username); + + if ($userId == null) + { + $keysModel = FD::model('Users'); + $userId = $keysModel->getUserId('email', $username); + } + } + else + { + $userId = $user->id; } - $kmodel = new ApiModelKey; - $model = new ApiModelKeys; - $key = null; + // Init vars + $keyModel = new ApiModelKey; + $keysModel = new ApiModelKeys; + $key = null; // Get login user hash - // $kmodel->setState('user_id', $user->id); + // $keyModel->setState('user_id', $user->id); - // $kmodel->setState('user_id', $id); - // $log_hash = $kmodel->getList(); - $model->setState('user_id', $id); - $log_hash = $model->getItems(); + // $keyModel->setState('user_id', $id); + // $log_hash = $keyModel->getList(); + $keysModel->setState('user_id', $userId); + $log_hash = $keysModel->getItems(); $log_hash = (!empty($log_hash)) ? $log_hash[count($log_hash) - count($log_hash)] : $log_hash; @@ -103,7 +104,7 @@ public function keygen() { // Create new key for user $data = array ( - 'userid' => $user->id, + 'userid' => $userId, 'domain' => '' , 'state' => 1, 'id' => '', @@ -114,7 +115,7 @@ public function keygen() JSession::getFormToken() => 1 ); - $result = $kmodel->save($data); + $result = $keyModel->save($data); // $key = $result->hash; @@ -126,7 +127,7 @@ public function keygen() // Load api key table JTable::addIncludePath(JPATH_ROOT . '/administrator/components/com_api/tables'); $table = JTable::getInstance('Key', 'ApiTable'); - $table->load(array('userid' => $user->id)); + $table->load(array('userid' => $userId)); $key = $table->hash; // Add new key in easysocial table @@ -144,12 +145,18 @@ public function keygen() $obj->code = '200'; // $obj->id = $user->id; + // $obj->id = $id; - $obj->id = $id; + // Set user details for response + $obj->id = $userId; + $obj->name = JFactory::getUser($userId)->name; + $obj->username = JFactory::getUser($userId)->username; + $obj->email = JFactory::getUser($userId)->email; // Generate claim for jwt $data = [ - "id" => trim($id), + "id" => trim($userId), + /*"iat" => '', "exp" => '', "aud" => '', @@ -191,9 +198,10 @@ public function updateEauth ($user = null, $key = null) { require_once JPATH_ADMINISTRATOR . '/components/com_easysocial/includes/foundry.php'; - $model = FD::model('Users'); - $id = $model->getUserId('username', $user->username); - $user = FD::user($id); + $keysModel = FD::model('Users'); + $id = $keysModel->getUserId('username', $user->username); + $user = FD::user($id); + $user->alias = $user->username; $user->auth = $key; $user->store(); From 1055d0a3d886d0b286e0a441b3a365c21920691d Mon Sep 17 00:00:00 2001 From: praneettekdi Date: Mon, 14 Feb 2022 10:16:34 +0530 Subject: [PATCH 5/6] Bug #179490 fix: Anyone can login just by passing the username to the login api using Bearer Token (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ttpllt46 <“praneet_s@tekditechnologies.com”> --- src/users/login.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/users/login.php b/src/users/login.php index 3abf088..7d4b9be 100644 --- a/src/users/login.php +++ b/src/users/login.php @@ -59,7 +59,19 @@ public function keygen() // Init variables $obj = new stdclass; $app = JFactory::getApplication(); - $username = $app->input->get('username', 0, 'STRING'); + $username = $app->input->get('username', '', 'STRING'); + $password = $app->input->get('password', '', 'STRING'); + + // Authenticate User + jimport('joomla.user.authentication'); + + $authenticate = JAuthentication::getInstance(); + $response = $authenticate->authenticate(array( 'username' => $username, 'password' => $password )); + + if ($response->status != JAuthentication::STATUS_SUCCESS) + { + ApiError::raiseError("403", JText::_('JLIB_LOGIN_AUTHENTICATE')); + } $user = JFactory::getUser(); From f9bb5f096ea7ec12ab380ddfc995dd371e04875a Mon Sep 17 00:00:00 2001 From: praneettekdi Date: Mon, 14 Feb 2022 19:18:15 +0530 Subject: [PATCH 6/6] Task #179501 feat: Impersonate Login API (#33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Task #179501 feat: Impersonate Login API * Task #179501 feat: Impersonate Login API * Task #179501 feat: Impersonate Login API * Task #179501 feat: Impersonate Login API * Task #179501 feat: Impersonate Login API * Task #179501 feat: Impersonate Login API * Update impersonatelogin.php Co-authored-by: ttpllt46 <“praneet_s@tekditechnologies.com”> Co-authored-by: Manoj L --- src/users/impersonatelogin.php | 261 +++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 src/users/impersonatelogin.php diff --git a/src/users/impersonatelogin.php b/src/users/impersonatelogin.php new file mode 100644 index 0000000..87018fd --- /dev/null +++ b/src/users/impersonatelogin.php @@ -0,0 +1,261 @@ + + * @copyright Copyright (C) 2009 - 2022 Techjoomla, Tekdi Technologies Pvt. Ltd. All rights reserved. + * @license GNU GPLv2 + */ + +// No direct access. +defined('_JEXEC') or die('Restricted access'); + +require_once JPATH_SITE . '/components/com_api/vendors/php-jwt/src/JWT.php'; +use Firebase\JWT\JWT; + +use Joomla\CMS\Factory; +use Joomla\CMS\User\UserHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Table\Table; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Component\ComponentHelper; + +JModelLegacy::addIncludePath(JPATH_SITE . 'components/com_api/models'); +require_once JPATH_SITE . '/components/com_api/libraries/authentication/user.php'; +require_once JPATH_SITE . '/components/com_api/libraries/authentication/login.php'; +require_once JPATH_ADMINISTRATOR . '/components/com_api/models/key.php'; +require_once JPATH_ADMINISTRATOR . '/components/com_api/models/keys.php'; + +/** + * Impersonate Login API resource class + * + * @package API + * @since 1.6.0 + */ +class UsersApiResourceImpersonateLogin extends ApiResource +{ + /** + * Get method + * + * @return object + */ + public function get() + { + $this->plugin->setResponse(Text::_('PLG_API_USERS_GET_METHOD_NOT_ALLOWED_MESSAGE')); + } + + /** + * Post method + * + * @return object + */ + public function post() + { + $this->plugin->setResponse($this->keygen()); + } + + /** + * Generate key method + * + * @return object|boolean + */ + public function keygen() + { + // Init variables + $obj = new stdclass; + $jinput = Factory::getApplication()->input; + $xImpersonate = $jinput->server->get('X-Impersonate', '', 'STRING'); + $httpXImpersonate = $jinput->server->get('HTTP_X_IMPERSONATE', '', 'STRING'); + + if (!empty($xImpersonate)) + { + $userToImpersonate = $xImpersonate; + } + elseif (!empty($httpXImpersonate)) + { + $userToImpersonate = $httpXImpersonate; + } + + if (preg_match('/email:(\S+)/', $userToImpersonate, $matches)) + { + $userId = $this->getUserByEmail($matches[1]); + } + elseif (preg_match('/username:(\S+)/', $userToImpersonate, $matches)) + { + $userId = UserHelper::getUserId($matches[1]); + } + elseif (is_numeric($userToImpersonate)) + { + $userId = $userToImpersonate; + } + else + { + ApiError::raiseError("403", Text::_('PLG_API_USERS_BAD_REQUEST_MESSAGE')); + + return false; + } + + if ($userId && $email) + { + $model = FD::model('Users'); + $userId = $model->getUserId('email', $userId); + } + + if (!$userId) + { + ApiError::raiseError("403", Text::_('PLG_API_USERS_BAD_REQUEST_MESSAGE')); + + return; + } + + // Init vars + $keyModel = new ApiModelKey; + $keysModel = new ApiModelKeys; + $key = null; + + // Get login user hash + // $keyModel->setState('user_id', $user->id); + + // $keyModel->setState('user_id', $id); + // $log_hash = $keyModel->getList(); + $keysModel->setState('user_id', $userId); + $log_hash = $keysModel->getItems(); + + $log_hash = (!empty($log_hash)) ? $log_hash[count($log_hash) - count($log_hash)] : $log_hash; + + if (!empty($log_hash)) + { + $key = $log_hash->hash; + } + elseif ($key == null || empty($key)) + { + // Create new key for user + $data = array ( + 'userid' => $userId, + 'domain' => '' , + 'state' => 1, + 'id' => '', + 'task' => 'save', + 'c' => 'key', + 'ret' => 'index.php?option=com_api&view=keys', + 'option' => 'com_api', + Session::getFormToken() => 1 + ); + + $result = $keyModel->save($data); + + // $key = $result->hash; + + if (!$result) + { + return false; + } + + // Load api key table + Table::addIncludePath(JPATH_ROOT . '/administrator/components/com_api/tables'); + $table = Table::getInstance('Key', 'ApiTable'); + $table->load(array('userid' => $userId)); + $key = $table->hash; + + // Add new key in easysocial table + $easyblog = JPATH_ROOT . '/administrator/components/com_easyblog/easyblog.php'; + + if (file_exists($easyblog) && ComponentHelper::isEnabled('com_easysocial', true)) + { + $this->updateEauth($user, $key); + } + } + + if (!empty($key)) + { + $obj->auth = $key; + $obj->code = '200'; + + // $obj->id = $user->id; + // $obj->id = $id; + + // Set user details for response + $obj->id = $userId; + $obj->name = Factory::getUser($userId)->name; + $obj->username = Factory::getUser($userId)->username; + $obj->email = Factory::getUser($userId)->email; + + // Generate claim for jwt + $data = [ + "id" => trim($userId), + + /*"iat" => '', + "exp" => '', + "aud" => '', + "sub" => ''"*/ + ]; + + // Using HS256 algo to generate JWT + $jwt = JWT::encode($data, trim($key), 'HS256'); + + if (isset($jwt) && $jwt != '') + { + $obj->jwt = $jwt; + } + else + { + $obj->jwt = false; + } + } + else + { + $obj->code = 403; + $obj->message = Text::_('PLG_API_USERS_BAD_REQUEST_MESSAGE'); + } + + return ($obj); + } + + /** + * Method to update Easyblog auth keys + * + * @param mixed $user User object + * @param mixed $key Key + * + * @return integer + * + * @since 1.6 + */ + public function updateEauth ($user = null, $key = null) + { + require_once JPATH_ADMINISTRATOR . '/components/com_easysocial/includes/foundry.php'; + + $keysModel = FD::model('Users'); + $id = $keysModel->getUserId('username', $user->username); + $user = FD::user($id); + + $user->alias = $user->username; + $user->auth = $key; + $user->store(); + + return $id; + } + + /** + * Function to fetch user id by email + * + * @param string $email User email + * + * @return integer User Id. + * + * @since 1.0 + */ + private function getUserByEmail($email) + { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__users')) + ->where($db->quoteName('email') . ' = ' . $db->quote($email)); + $db->setQuery($query); + $user = $db->loadResult(); + + return $user; + } +}