diff --git a/e107_core/controllers/system/xup.php b/e107_core/controllers/system/xup.php index 04fe190ce..fcd8203ea 100644 --- a/e107_core/controllers/system/xup.php +++ b/e107_core/controllers/system/xup.php @@ -20,8 +20,18 @@ class core_system_xup_controller extends eController { var $backUrl = null; - - + /** + * @var SocialLoginConfigManager + */ + private $social_login_config_manager; + + public function __construct(eRequest $request, eResponse $response = null) + { + parent::__construct($request, $response); + require_once(e_PLUGIN."social/SocialLoginConfigManager.php"); + $this->social_login_config_manager = new SocialLoginConfigManager(e107::getConfig()); + } + public function init() { //$back = 'system/xup/test'; @@ -113,9 +123,11 @@ class core_system_xup_controller extends eController echo ' '.LAN_XUP_ERRM_11.' '.(e107::getUser()->isUser() && !empty($profileData) ? 'true' : 'false'); - $testUrl = SITEURL."?route=system/xup/test"; - $providers = e107::getPref('social_login', array()); - + $testUrl = SITEURL."?route=system/xup/test"; + require_once(e_PLUGIN . "social/SocialLoginConfigManager.php"); + $manager = new SocialLoginConfigManager(e107::getConfig()); + $providers = $manager->getValidConfiguredProviderConfigs(); + foreach($providers as $key=>$var) { if($var['enabled'] == 1) diff --git a/e107_core/shortcodes/batch/signup_shortcodes.php b/e107_core/shortcodes/batch/signup_shortcodes.php index a7e6ee732..3f871b958 100755 --- a/e107_core/shortcodes/batch/signup_shortcodes.php +++ b/e107_core/shortcodes/batch/signup_shortcodes.php @@ -75,26 +75,34 @@ class signup_shortcodes extends e_shortcode if(!empty($pref)) { $text = ""; - $providers = e107::getPref('social_login'); + $manager = new SocialLoginConfigManager(e107::getConfig()); + $providers = $manager->getValidConfiguredProviderConfigs(); foreach($providers as $p=>$v) { - $p = strtolower($p); if($v['enabled'] == 1) { // $text .= ""; $ic = strtolower($p); - - if($ic == 'live') + + switch ($ic) { - $ic = 'windows'; + case 'windowslive': + $ic = 'windows'; + break; } - - // 'signup' Creates a new XUP user if not found, otherwise it logs the person in. - - $button = (defset('FONTAWESOME')) ? $tp->toGlyph('fa-'.$ic, array('size'=>$size, 'fw'=>true)) : ""; + + // 'signup' Creates a new XUP user if not found, otherwise it logs the person in. + + if (defset('FONTAWESOME') && in_array($ic, e107::getMedia()->getGlyphs())) + $button = "" . $tp->toGlyph('fa-' . $ic, array('size' => $size, 'fw' => true)) . ""; + elseif (is_file(e107::getFolder('images') . "xup/{$ic}.png")) + $button = ""; + else + $button = "$p"; + $text .= " ".$button." "; } //TODO different icon options. see: http://zocial.smcllns.com/ @@ -116,8 +124,9 @@ class signup_shortcodes extends e_shortcode if(!empty($pref)) { $text = ""; - $providers = e107::pref('core', 'social_login'); - + $manager = new SocialLoginConfigManager(e107::getConfig()); + $providers = $manager->getValidConfiguredProviderConfigs(); + $size = empty($parm['size']) ? '2x' : $parm['size']; $class = empty($parm['class']) ? 'btn btn-primary' : $parm['class'] ; @@ -129,22 +138,25 @@ class signup_shortcodes extends e_shortcode foreach($providers as $p=>$v) { - - - $p = strtolower($p); if($v['enabled'] == 1) { $ic = strtolower($p); - - if($ic == 'live') + + switch ($ic) { - $ic = 'windows'; + case 'windowslive': + $ic = 'windows'; + break; } - $button = (defset('FONTAWESOME')) ? "".$tp->toGlyph('fa-'.$ic, array('size'=>$size, 'fw'=>true))."" : ""; - - $text .= " ".$button." "; - } + if (defset('FONTAWESOME') && in_array($ic, e107::getMedia()->getGlyphs())) + $button = "" . $tp->toGlyph('fa-' . $ic, array('size' => $size, 'fw' => true)) . ""; + elseif (is_file(e107::getFolder('images') . "xup/{$ic}.png")) + $button = ""; + else + $button = "$p"; + $text .= " " . $button . " "; + } //TODO different icon options. see: http://zocial.smcllns.com/ } diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php index a7b62dce9..c5b671d2c 100644 --- a/e107_handlers/e107_class.php +++ b/e107_handlers/e107_class.php @@ -286,6 +286,18 @@ class e107 //register_shutdown_function(array($this, 'destruct')); } + private static function die_http_400() + { + header('HTTP/1.0 400 Bad Request', true, 400); + header('Content-Type: text/plain'); + if (deftrue('e_DEBUG')) + { + echo "Bad Request: "; + debug_print_backtrace(0, 1); + } + exit(); + } + /** * Cloning is not allowed * @@ -1695,14 +1707,22 @@ class e107 * Create a new Hybridauth object based on the provided configuration * * @return Hybridauth\Hybridauth + * @throws \Hybridauth\Exception\InvalidArgumentException + * @deprecated v2.3.0 Use the e_user_provider interfaces instead (e107::getUser()->getProvider()). + * Hybridauth features are only available if the user is associated with a social login. + * @see e107::getUser() for getting a user object that may or may not have a social login. + * @see e_user_provider for social login features, only if enabled on the user. */ public static function getHybridAuth($config = null) { if(null === $config) { + require_once(e_PLUGIN . "social/SocialLoginConfigManager.php"); + $manager = new SocialLoginConfigManager(e107::getConfig()); + $config = array( 'callback' => self::getUrl()->create('system/xup/login', array(), array('full' => true)), - 'providers' => self::getPref('social_login', array()), + 'providers' => $manager->getValidConfiguredProviderConfigs(), 'debug_mode' => false, 'debug_file' => '' ); @@ -3938,57 +3958,32 @@ class e107 $regex = "/(base64_decode|chr|php_uname|fwrite|fopen|fputs|passthru|popen|proc_open|shell_exec|exec|proc_nice|proc_terminate|proc_get_status|proc_close|pfsockopen|apache_child_terminate|posix_kill|posix_mkfifo|posix_setpgid|posix_setsid|posix_setuid|phpinfo) *?\((.*) ?\;?/i"; if(preg_match($regex,$input)) { - header('HTTP/1.0 400 Bad Request', true, 400); - if(deftrue('e_DEBUG')) - { - echo "Bad Request: ".__METHOD__." : ". __LINE__; - } - exit(); + self::die_http_400(); } // Check for XSS JS $regex = "/(document\.location|document\.write|document\.cookie)/i"; if(preg_match($regex,$input)) { - header('HTTP/1.0 400 Bad Request', true, 400); - if(deftrue('e_DEBUG')) - { - echo "Bad Request: ".__METHOD__." : ". __LINE__; - } - exit(); + self::die_http_400(); } // Suspicious HTML. if(strpos($input, ' and quotes. @@ -1081,95 +1081,69 @@ Following fields auto-filled in code as required: } } +/** + * Social login provider + */ class e_user_provider { /** * @var string */ protected $_provider; - + /** * Hybridauth adapter * @var \Hybridauth\Adapter\AdapterInterface */ public $adapter; - + /** * Hybridauth object * @var Hybridauth\Hybridauth */ - public $hybridauth; + protected $hybridauth; protected $_config = array(); - - public function __construct($provider, $config = array()) + /** + * @var SocialLoginConfigManager + */ + protected $social_login_config_manager; + + public function __construct($provider = null, $config = array()) { - if(!empty($config)) + require_once(e_PLUGIN . "social/SocialLoginConfigManager.php"); + $this->social_login_config_manager = new SocialLoginConfigManager(e107::getConfig()); + + if (!empty($config)) { $this->_config = $config; } - else + else { $this->_config = array( - "callback" => e107::getUrl()->create('system/xup/login', array(), array('full' => true)), - "providers" => e107::getPref('social_login', array()), - "debug_mode" => 'error', - "debug_file" => e_LOG."hybridAuth.log" + "callback" => e107::getUrl()->create( + 'system/xup/login', + array('provider' => $provider), + array('full' => true, 'encode' => false) + ), + "providers" => $this->social_login_config_manager->getValidConfiguredProviderConfigs(), + "debug_mode" => 'error', + "debug_file" => e_LOG . "hybridAuth.log" ); - + } - - $this->hybridauth = e107::getHybridAuth($this->_config); + + $this->hybridauth = new Hybridauth\Hybridauth($this->_config); $this->setProvider($provider); } - + public function setProvider($provider) { - $provider = strtolower($provider); - - switch($provider) - { - case 'aol': - $provider = 'AOL'; - break; - - case 'googleopenid': - $provider = 'GoogleOpenID'; - break; - - case 'linkedin': - $provider = 'LinkedIn'; - break; - - case 'myspace': - $provider = 'MySpace'; - break; - - case 'openid': - $provider = 'OpenID'; - break; - - default: - $provider = ucfirst($provider); - break; - } - - if(isset($this->_config['providers'][$provider]) && $this->_config['providers'][$provider]['enabled']) - { - if($this->_config['providers'][$provider]['enabled'] && vartrue($this->_config['providers'][$provider]['keys'])) - { - $valid = true; - foreach ($this->_config['providers'][$provider]['keys'] as $_key => $_value) - { - if(empty($_value)) $valid = false; - } - if($valid) $this->_provider = $provider; - } - } + $this->_provider = $provider; } - private function log($class,$method,$line) + private function log($class, $method, $line) { - // e107::getLog()->add('XUP Debug', ($class.':'.$method.'-'.$line), E_LOG_INFORMATIVE, "XUP_DEBUG"); + // e107::getLog()->add('XUP Debug', ($class.':'.$method.'-'.$line), E_LOG_INFORMATIVE, "XUP_DEBUG"); } @@ -1178,110 +1152,179 @@ class e_user_provider # system/xup/login by default $this->_config['callback'] = $url; } - + public function getProvider() { // $this->log(__CLASS__, __METHOD__, __LINE__); return $this->_provider; } - + public function getConfig() { return $this->_config; } - + public function getUserProfile() { - if($this->adapter) + if ($this->adapter) { - return $this->adapter->getUserProfile(); + try + { + return $this->adapter->getUserProfile(); + } + catch (\Hybridauth\Exception\Exception $e) + { + return null; + } } return null; } - + public function userId() { - - if($this->adapter && $this->adapter->getUserProfile()->identifier) + if ($profile = $this->getUserProfile()) { - return $this->getProvider().'_'.$this->adapter->getUserProfile()->identifier; + return $this->getProvider() . '_' . $profile->identifier; } return null; } - + /** - * XUP Signup Method (falls-back to XUP login when existing user is detected). - * May be used as a simple XUP login link for existing and non-existing users. + * Get the social login providers for which we have adapters + * + * This function is slow! Please cache the output instead of calling it multiple times. + * + * @return array String list of supported providers. Empty if Hybridauth is broken. + */ + public static function getSupportedProviders() + { + $providers = []; + + try + { + $reflector = new ReflectionClass('Hybridauth\Hybridauth'); + } + catch (ReflectionException $e) + { + return $providers; + } + $hybridauth_path = $reflector->getFileName(); + $hybridauth_providers_path = dirname($hybridauth_path) . "/Provider/"; + $fs_iterator = new FilesystemIterator($hybridauth_providers_path); + foreach ($fs_iterator as $file) + { + if (!$file->isFile()) continue; + + $provider_source_code = file_get_contents($file); + $provider_source_tokens = token_get_all($provider_source_code); + for ($token_index = 0; isset($provider_source_tokens[$token_index]); $token_index++) + { + if (!isset($provider_source_tokens[$token_index][0])) continue; + + if (T_CLASS === $provider_source_tokens[$token_index][0]) + { + $token_index += 2; + $providers[] = $provider_source_tokens[$token_index][1]; + } + } + } + + sort($providers); + return $providers; + } + + /** + * Get the type of provider from a provider name + * @param $providerName string Name of the supported social login provider + * @return string|bool "OAuth1", "OAuth2", or "OpenID". If false, the provider name is invalid. + * Other values are technically possible but not supported. + */ + public static function getTypeOf($providerName) + { + $class_name = "Hybridauth\Provider\\{$providerName}"; + $parent_class = get_parent_class($class_name); + if (!$parent_class) return false; + + $parent_class_split = explode("\\", get_parent_class($class_name)); + $type = end($parent_class_split); + if ($type == "AbstractAdapter") return $providerName; + if (!in_array($type, ['OAuth1', 'OAuth2', 'OpenID'])) return self::getTypeOf($type); + return $type; + } + + /** + * XUP Signup Method (falls-back to XUP login when existing user is detected). + * May be used as a simple XUP login link for existing and non-existing users. */ public function signup($redirectUrl = true, $loginAfterSuccess = true, $emailAfterSuccess = true) { - if(!e107::getPref('social_login_active', false)) + if (!e107::getPref('social_login_active', false)) { - throw new Exception( "Signup failed! This feature is disabled.", 100); // TODO lan + throw new Exception("Signup failed! This feature is disabled.", 100); // TODO lan } - - if(!$this->getProvider()) + + if (!$this->getProvider()) { - throw new Exception( "Signup failed! Wrong provider.", 2); // TODO lan + throw new Exception("Signup failed! Wrong provider.", 2); // TODO lan } - - if($redirectUrl) + + if ($redirectUrl) { - if(true === $redirectUrl) + if (true === $redirectUrl) { $redirectUrl = SITEURL; } - elseif(strpos($redirectUrl, 'http://') !== 0 && strpos($redirectUrl, 'https://') !== 0) + elseif (strpos($redirectUrl, 'http://') !== 0 && strpos($redirectUrl, 'https://') !== 0) { $redirectUrl = e107::getUrl()->create($redirectUrl); } } - if(e107::getUser()->isUser()) + if (e107::getUser()->isUser()) { - if($redirectUrl) + if ($redirectUrl) { e107::getRedirect()->redirect($redirectUrl); } return false; - // throw new Exception( "Signup failed! User already signed in. ", 1); // TODO lan + // throw new Exception( "Signup failed! User already signed in. ", 1); // TODO lan } - + $this->adapter = $this->hybridauth->authenticate($this->getProvider()); $profile = $this->adapter->getUserProfile(); $this->log(__CLASS__, __METHOD__, __LINE__); // returned back, if success... - if($profile->identifier) + if ($profile->identifier) { $sql = e107::getDb(); $userMethods = e107::getUserSession(); - + $plainPwd = $userMethods->generateRandomString('************'); // auto plain passwords - + // TODO - auto login name, shouldn't be used if system set to user_email login... - $userdata['user_loginname'] = $this->getProvider().$userMethods->generateUserLogin(e107::getPref('predefinedLoginName', '_..#..#..#')); - $userdata['user_email'] = $sql->escape($profile->emailVerified ? $profile->emailVerified : $profile->email); - $userdata['user_name'] = $sql->escape($profile->displayName); - $userdata['user_login'] = $userdata['user_name']; - $userdata['user_customtitle'] = ''; // not used - $userdata['user_password'] = $userMethods->HashPassword($plainPwd, $userdata['user_loginname']); // pwd - $userdata['user_sess'] = ''; // - $userdata['user_image'] = $profile->photoURL; // avatar - $userdata['user_signature'] = ''; // not used - $userdata['user_hideemail'] = 1; // hide it by default - $userdata['user_xup'] = $sql->escape($this->userId()); + $userdata['user_loginname'] = $this->getProvider() . $userMethods->generateUserLogin(e107::getPref('predefinedLoginName', '_..#..#..#')); + $userdata['user_email'] = $sql->escape($profile->emailVerified ? $profile->emailVerified : $profile->email); + $userdata['user_name'] = $sql->escape($profile->displayName); + $userdata['user_login'] = $userdata['user_name']; + $userdata['user_customtitle'] = ''; // not used + $userdata['user_password'] = $userMethods->HashPassword($plainPwd, $userdata['user_loginname']); // pwd + $userdata['user_sess'] = ''; // + $userdata['user_image'] = $profile->photoURL; // avatar + $userdata['user_signature'] = ''; // not used + $userdata['user_hideemail'] = 1; // hide it by default + $userdata['user_xup'] = $sql->escape($this->userId()); $pref = e107::pref('core'); - if(!empty($pref['initial_user_classes'])) + if (!empty($pref['initial_user_classes'])) { $userdata['user_class'] = $pref['initial_user_classes']; } - elseif(!empty($pref['user_new_period'])) + elseif (!empty($pref['user_new_period'])) { $userdata['user_class'] = e_UC_NEWUSER; } @@ -1290,37 +1333,38 @@ class e_user_provider $userdata['user_class'] = ''; } - // print_a($userdata); - - + // print_a($userdata); + + // user_name, user_xup, user_email and user_loginname shouldn't match - $insert = (!empty($userdata['user_email'])) ? "OR user_email='".$userdata['user_email']."' " : ""; + $insert = (!empty($userdata['user_email'])) ? "OR user_email='" . $userdata['user_email'] . "' " : ""; $this->log(__CLASS__, __METHOD__, __LINE__); - - if($uid = $sql->retrieve("user", "user_id", "user_xup='".$sql->escape($this->userId())."' ".$insert." OR user_loginname='{$userdata['user_loginname']}' OR user_name='{$userdata['user_name']}'")) + + if ($uid = $sql->retrieve("user", "user_id", "user_xup='" . $sql->escape($this->userId()) . "' " . $insert . " OR user_loginname='{$userdata['user_loginname']}' OR user_name='{$userdata['user_name']}'")) { // $this->login($redirectUrl); // auto-login e107::getUser()->loginProvider($this->userId()); - if($redirectUrl) + if ($redirectUrl) { e107::getRedirect()->redirect($redirectUrl); } - + return false; // throw new Exception( "Signup failed! User already exists. Please use 'login' instead.", 3); } - - if(empty($userdata['user_email']) && e107::getPref('disable_emailcheck', 0)==0) // Allow it if set-up that way. + + if (empty($userdata['user_email']) && e107::getPref('disable_emailcheck', 0) == 0) // Allow it if set-up that way. { // Twitter will not provide email addresses. - // throw new Exception( "Signup failed! Can't access user email - registration without an email is impossible.".print_a($userdata,true), 4); // TODO lan + // throw new Exception( "Signup failed! Can't access user email - registration without an email is impossible.".print_a($userdata,true), 4); // TODO lan } - + // other fields - $now = time(); + $now = time(); $userdata['user_id'] = null; + $userdata['user_image'] = ''; $userdata['user_join'] = $now; $userdata['user_lastvisit'] = 0; $userdata['user_currentvisit'] = 0; @@ -1340,12 +1384,12 @@ class e_user_provider $user->getExtendedModel(); // init //$user->setEditor(e107::getSystemUser(1, false)); $user->save(true); - + // user model error - if($user->hasError()) + if ($user->hasError()) { e107::getLog()->add('XUP Signup Failure', $userdata, E_LOG_WARNING, "XUP_SIGNUP"); - throw new Exception($user->renderMessages(), 5); + throw new Exception($user->renderMessages(), 5); } @@ -1354,35 +1398,35 @@ class e_user_provider $userdata = $user->getData(); $userdata['provider'] = $this->getProvider(); - $userdata['callback_data'] = $profile; - - // e107::getEvent()->trigger('userveri', $userdata); // Trigger New verified user. - - e107::getEvent()->trigger('user_xup_signup', $userdata); - - $ret = e107::getEvent()->trigger('usersupprov', $userdata); // XXX - it's time to pass objects instead of array? + $userdata['callback_data'] = $profile; + + // e107::getEvent()->trigger('userveri', $userdata); // Trigger New verified user. + + e107::getEvent()->trigger('user_xup_signup', $userdata); + + $ret = e107::getEvent()->trigger('usersupprov', $userdata); // XXX - it's time to pass objects instead of array? + + if (true === $ret) return $this; - if(true === $ret) return $this; - // send email - if($emailAfterSuccess && !empty($userdata['user_email'])) + if ($emailAfterSuccess && !empty($userdata['user_email'])) { - $user->set('user_password', $plainPwd)->email('signup'); + $user->set('user_password', $plainPwd)->email('signup'); } - + e107::getUser()->setProvider($this); - + // auto login - if($loginAfterSuccess) + if ($loginAfterSuccess) { e107::getUser()->loginProvider($this->userId()); // if not proper after-login, return true so user can see login screen } - - if($redirectUrl) + + if ($redirectUrl) { e107::getRedirect()->redirect($redirectUrl); } - + return true; } @@ -1392,70 +1436,68 @@ class e_user_provider } - public function login($redirectUrl = true) { - if(!e107::getPref('social_login_active', false)) + if (!e107::getPref('social_login_active', false)) { - throw new Exception( "Signup failed! This feature is disabled.", 100); // TODO lan + throw new Exception("Signup failed! This feature is disabled.", 100); // TODO lan } - - if(!$this->getProvider()) + + if (!$this->getProvider()) { - throw new Exception( "Login failed! Wrong provider.", 22); // TODO lan + throw new Exception("Login failed! Wrong provider.", 22); // TODO lan } - - if($redirectUrl) + + if ($redirectUrl) { - if(true === $redirectUrl) + if (true === $redirectUrl) { $redirectUrl = SITEURL; } - elseif(strpos($redirectUrl, 'http://') !== 0 && strpos($redirectUrl, 'https://') !== 0) + elseif (strpos($redirectUrl, 'http://') !== 0 && strpos($redirectUrl, 'https://') !== 0) { $redirectUrl = e107::getUrl()->create($redirectUrl); } } - if(e107::getUser()->isUser()) + if (e107::getUser()->isUser()) { - if($redirectUrl) + if ($redirectUrl) { e107::getRedirect()->redirect($redirectUrl); } - return true; + return true; } - + $this->adapter = $this->hybridauth->authenticate($this->getProvider()); $check = e107::getUser()->setProvider($this)->loginProvider($this->userId()); - if($redirectUrl) + if ($redirectUrl) { e107::getRedirect()->redirect($redirectUrl); } - + return $check; } - public function init() { - if(!e107::getPref('social_login_active', false)) + if (!e107::getPref('social_login_active', false)) { return; } $this->adapter = null; $providerId = $this->_provider; - if($providerId && $this->hybridauth->isConnectedWith($providerId)) + if ($providerId && $this->hybridauth->isConnectedWith($providerId)) { $this->adapter = $this->hybridauth->getAdapter($providerId); } } - + public function logout() { if ( @@ -1465,16 +1507,16 @@ class e_user_provider ) return true; try { - $this->adapter->logout(); + $this->adapter->disconnect(); $this->adapter = null; } - catch(Exception $e) + catch (Exception $e) { return $e->getMessage(); } return true; } - + } diff --git a/e107_handlers/user_model.php b/e107_handlers/user_model.php index 5241e02a1..17d6c85ea 100644 --- a/e107_handlers/user_model.php +++ b/e107_handlers/user_model.php @@ -1497,12 +1497,13 @@ class e_user extends e_user_model private $_parent_config = null; /** - * @var Hybrid_Provider_Model + * @var e_user_provider|null */ protected $_provider; public function __construct() { + parent::__construct(); $this->setSessionData() // retrieve data from current session ->load() // load current user from DB ->setEditor($this); // reference to self @@ -1531,7 +1532,7 @@ class e_user extends e_user_model /** * Init external user login/signup provider - * @return e_system_user + * @return e_user */ public function initProvider() { @@ -1544,11 +1545,13 @@ class e_user extends e_user_model $this->_provider = new e_user_provider($providerId); $this->_provider->init(); } + + return $this; } /** * Get external user provider - * @return Hybrid_Provider_Model + * @return e_user_provider|null */ public function getProvider() { @@ -1750,10 +1753,17 @@ class e_user extends e_user_model foreach ($connected as $providerId) { $adapter = $hybrid->getAdapter($providerId); - - if(!$adapter->getUserProfile()->identifier) continue; - $profile = $adapter->getUserProfile(); + try + { + $profile = $adapter->getUserProfile(); + } + catch (\Hybridauth\Exception\Exception $e) + { + continue; + } + + if (!$profile->identifier) continue; $userdata['user_name'] = $sql->escape($profile->displayName); $userdata['user_image'] = $profile->photoURL; // avatar @@ -1762,7 +1772,8 @@ class e_user extends e_user_model $id = $providerId.'_'.$profile->identifier; $where[] = "user_xup='".$sql->escape($id)."'"; } - + // no active session found + if(empty($where)) return $this; $where = implode(' OR ', $where); if($sql->select('user', 'user_id, user_name, user_email, user_image, user_password, user_xup', $where)) diff --git a/e107_plugins/social/SocialLoginConfigManager.php b/e107_plugins/social/SocialLoginConfigManager.php new file mode 100644 index 000000000..d512af199 --- /dev/null +++ b/e107_plugins/social/SocialLoginConfigManager.php @@ -0,0 +1,223 @@ +config = $config; + } + + /** + * Checks whether the specified social login provider is enabled + * @param $providerName string The un-normalized name of the provider to check + * @return bool Whether the specified provider is enabled + */ + public function isProviderEnabled($providerName) + { + $result = $this->getProviderConfig($providerName, "/enabled"); + return (bool)$result; + } + + /** + * Disable and remove the specified social login provider + * @param $providerName string The un-normalized name of the provider to forget + */ + public function forgetProvider($providerName) + { + $this->config->removePref(self::SOCIAL_LOGIN_PREF . '/' . $this->normalizeProviderName($providerName)); + } + + /** + * Overwrite the entire social login provider configuration with the specified options + * + * Does not commit to database. + * + * @param $providerName string The un-normalized name of the social login provider + * @param $options array Associative array of options + * $options['enabled'] bool Whether the social login provider is enabled + * $options['keys'] array Authentication app keys + * $options['keys']['id'] string The OAuth1 client key or OAuth2 client ID + * $options['keys']['secret'] string The OAuth1 or OAuth2 client secret + * $options['scope'] string OAuth2 scopes, space-delimited + * @see SocialLoginConfigManager::saveProviderConfig() to commit to database. + * + */ + public function setProviderConfig($providerName, $options) + { + $config = $this->getSocialLoginConfig(); + if (!is_array($config)) $this->config->set(self::SOCIAL_LOGIN_PREF, []); + + self::array_unset_empty_recursive($options); + + if (empty($options)) $this->forgetProvider($providerName); + else $this->config->setPref( + self::SOCIAL_LOGIN_PREF . '/' . $this->normalizeProviderName($providerName), + $options + ); + } + + private static function array_unset_empty_recursive(&$array) + { + foreach ($array as $key => &$value) + { + if (is_array($value)) + { + $arraySize = self::array_unset_empty_recursive($value); + if (!$arraySize) + { + unset($array[$key]); + } + } + else if (empty($array[$key])) + { + unset($array[$key]); + } + } + return count($array); + } + + public function saveProviderConfig() + { + $this->config->save(true, false, false); + } + + /** + * Get the social login provider configuration currently stored in the database + * @param $providerName string The un-normalized name of the social login provider + * @param $path string Nested array keys, slash-delimited ("/") + * @return array|mixed The configuration of the specified provider, or the value of the $path + */ + public function getProviderConfig($providerName, $path = "") + { + if (empty($path)) $path = ""; + elseif (substr($path, 0, 1) !== "/") $path = "/$path"; + + $pref = $this->config->getPref( + self::SOCIAL_LOGIN_PREF . '/' . $this->normalizeProviderName($providerName) . $path + ); + + return $pref; + } + + /** + * Get configs of providers that are supported and configured + * @return array Associative array where the key is the denormalized provider name and the value is its config + */ + public function getValidConfiguredProviderConfigs() + { + $supported_providers = $this->getSupportedProviders(); + $configured_providers = $this->getConfiguredProviders(); + $unsupported_providers = array_diff($configured_providers, $supported_providers); + $configured_providers = array_diff($configured_providers, $unsupported_providers); + + $provider_configs = []; + foreach ($configured_providers as $configured_provider) + { + $provider_configs[$configured_provider] = + $this->getProviderConfig($configured_provider); + } + + return $provider_configs; + } + + /** + * Get the social login providers for which we have adapters + * @return array String list of supported providers. Empty if Hybridauth is broken. + */ + public function getSupportedProviders() + { + if ($this->supportedProvidersCache === null) + $this->supportedProvidersCache = e_user_provider::getSupportedProviders(); + return $this->supportedProvidersCache; + } + + /** + * Get the type of provider from a provider name + * @param $providerName string Name of the supported social login provider + * @return string|bool "OAuth1", "OAuth2", or "OpenID". If false, the provider name is invalid. + * Other values are technically possible but not supported. + */ + public function getTypeOfProvider($providerName) + { + return e_user_provider::getTypeOf($providerName); + } + + /** + * Get the providers that are currently configured in the core preferences + * @return array String list of configured provider names + */ + public function getConfiguredProviders() + { + $output = []; + $social_login_config = $this->getSocialLoginConfig(); + $configured_providers = array_keys($social_login_config); + foreach ($configured_providers as $configured_provider) + { + $output[] = $this->denormalizeProviderName($configured_provider); + } + sort($output); + return $output; + } + + protected function getSocialLoginConfig() + { + return $this->config->get(self::SOCIAL_LOGIN_PREF); + } + + /** + * Turn a provider name into one fit for storage in the database (core preferences) + * @return string Normalized social login provider name + */ + public function normalizeProviderName($providerName) + { + $normalizedProviderName = $providerName; + foreach ($this->getSupportedProviders() as $providerProperCaps) + { + if (mb_strtolower($providerName) == mb_strtolower($providerProperCaps)) + { + $normalizedProviderName = $providerProperCaps; + break; + } + } + $providerType = $this->getTypeOfProvider($normalizedProviderName); + $normalizedProviderName = preg_replace('/(OpenID|OAuth1|OAuth2)$/i', '', $normalizedProviderName); + if (empty($normalizedProviderName) && !empty($providerType) || $providerName == $providerType) + return $providerType; + elseif ($providerType) + return "{$normalizedProviderName}-{$providerType}"; + return $providerName; + } + + /** + * Turn a normalized provider name into a Hybridauth-compatible adapter name + * @param $normalizedProviderName string Provider name stored in the database + * @return string Hybridauth-compatible adapter name. May not necessarily exist in Hybridauth. + */ + public function denormalizeProviderName($normalizedProviderName) + { + list($provider_name, $provider_type) = array_pad(explode("-", $normalizedProviderName), 2, ""); + if ($provider_type != $this->getTypeOfProvider($provider_name)) $provider_name .= $provider_type; + return $provider_name; + } +} \ No newline at end of file diff --git a/e107_plugins/social/admin_config.php b/e107_plugins/social/admin_config.php index 84435b7ca..986c46f60 100644 --- a/e107_plugins/social/admin_config.php +++ b/e107_plugins/social/admin_config.php @@ -3,7 +3,7 @@ // Generated e107 Plugin Admin Area require_once('../../class2.php'); -if (!getperms('P')) +if (!getperms('P')) { e107::redirect('admin'); exit; @@ -15,63 +15,63 @@ e107::lan('social',false, true); class social_adminarea extends e_admin_dispatcher { - protected $modes = array( - + protected $modes = array( + 'main' => array( 'controller' => 'social_ui', 'path' => null, 'ui' => 'social_form_ui', 'uipath' => null ), - - ); - - + + ); + + protected $adminMenu = array( // 'main/list' => array('caption'=> LAN_MANAGE, 'perm' => 'P'), // 'main/create' => array('caption'=> LAN_CREATE, 'perm' => 'P'), 'main/configure' => array('caption'=> LAN_CONFIGURE, 'perm' => 'P'), - 'main/prefs' => array('caption'=> LAN_PREFS, 'perm' => 'P'), + 'main/prefs' => array('caption'=> LAN_PREFS, 'perm' => 'P'), ); protected $adminMenuAliases = array( - 'main/edit' => 'main/list' - ); - + 'main/edit' => 'main/list' + ); + protected $menuTitle = LAN_PLUGIN_SOCIAL_NAME; } +require_once("SocialLoginConfigManager.php"); - class social_ui extends e_admin_ui { - + protected $pluginTitle = LAN_PLUGIN_SOCIAL_NAME; protected $pluginName = 'social'; // protected $eventName = 'social-social'; // remove comment to enable event triggers in admin. // protected $table = 'social'; // protected $pid = 'interview_id'; - protected $perPage = 10; + protected $perPage = 10; protected $batchDelete = true; // protected $batchCopy = true; // protected $sortField = 'somefield_order'; // protected $orderStep = 10; // protected $tabs = array('Tabl 1','Tab 2'); // Use 'tab'=>0 OR 'tab'=>1 in the $fields below to enable. - + // protected $listQry = "SELECT * FROM `#tableName` WHERE field != '' "; // Example Custom Query. LEFT JOINS allowed. Should be without any Order or Limit. - + protected $listOrder = ''; - + protected $fields = array(); - + protected $fieldpref = array(); - + protected $preftabs = array(LAN_LOGIN, LAN_SOCIAL_ADMIN_14, LAN_SOCIAL_ADMIN_15, LAN_SOCIAL_ADMIN_16, LAN_SOCIAL_ADMIN_17, LAN_SOCIAL_ADMIN_37); @@ -107,16 +107,25 @@ class social_ui extends e_admin_ui ); protected $social_logins = array(); + /** + * @var SocialLoginConfigManager + */ + protected $social_login_config_manager; protected $social_external = array(); public function init() { + $this->social_login_config_manager = new SocialLoginConfigManager(e107::getConfig()); + if(!empty($_POST['save_social']) ) { $cfg = e107::getConfig(); - $cfg->setPref('social_login', $_POST['social_login']); + foreach ($_POST['social_login'] as $provider_name => $raw_updated_social_login) + { + $this->social_login_config_manager->setProviderConfig($provider_name, $raw_updated_social_login); + } $cfg->setPref('social_login_active', $_POST['social_login_active']); $cfg->setPref('xurl', $_POST['xurl']); $cfg->save(true, true, true); @@ -132,96 +141,6 @@ class social_ui extends e_admin_ui { $this->prefs['sharing_providers']['writeParms']['optArray'][$k] = $k; } - // print_a($bla); - - - -// Single/ Social Login / / copied from hybridAuth config.php so it's easy to add more. -// Used Below. - - $this->social_logins = array ( - // openid providers - - - "AOL" => array ( - "enabled" => true - ), - - "Facebook" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ), - "trustForwarded" => false, - // A comma-separated list of permissions you want to request from the user. See the Facebook docs for a full list of available permissions: http://developers.facebook.com/docs/reference/api/permissions. - "scope" => "email", - - // The display context to show the authentication page. Options are: page, popup, iframe, touch and wap. Read the Facebook docs for more details: http://developers.facebook.com/docs/reference/dialogs#display. Default: page - "display" => "" - ), - - "Foursquare" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ) - ), - - "Github" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ), - "scope" => "user:email", - ), - - "Google" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ), - "scope" => "email" - ), -/* - "Instagram" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ) - ),*/ - - "LinkedIn" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ) - ), - - // windows live - "Live" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ) - ), - - /* - "MySpace" => array ( - "enabled" => true, - "keys" => array ( "key" => "", "secret" => "" ) - ), - */ - - "OpenID" => array ( - "enabled" => true - ), - - "Steam" => array ( - "enabled" => true, - "keys" => array ( "key" => "" ) - ), - - "Twitter" => array ( - "enabled" => true, - "keys" => array ( "key" => "", "secret" => "" ), - "includeEmail" => true, - ), - - - "Yahoo" => array ( - "enabled" => true, - "keys" => array ( "id" => "", "secret" => "" ), - ), - - - ); - $this->social_external = array( "Facebook" => "https://developers.facebook.com/apps", @@ -230,7 +149,7 @@ class social_ui extends e_admin_ui "Live" => "https://manage.dev.live.com/ApplicationOverview.aspx", "LinkedIn" => "https://www.linkedin.com/secure/developer", "Foursquare" => "https://www.foursquare.com/oauth/", - "Github" => "https://github.com/settings/applications/new", + "GitHub" => "https://github.com/settings/applications/new", "Steam" => "http://steamcommunity.com/dev/apikey", "Instagram" => "http://instagram.com/developer" ); @@ -238,14 +157,14 @@ class social_ui extends e_admin_ui } - + // ------- Customize Create -------- - - public function beforeCreate($new_data) + + public function beforeCreate($new_data, $old_data) { return $new_data; } - + public function afterCreate($new_data, $old_data, $id) { // do something @@ -253,12 +172,12 @@ class social_ui extends e_admin_ui public function onCreateError($new_data, $old_data) { - // do something - } - - + // do something + } + + // ------- Customize Update -------- - + public function beforeUpdate($new_data, $old_data, $id) { return $new_data; @@ -266,14 +185,14 @@ class social_ui extends e_admin_ui public function afterUpdate($new_data, $old_data, $id) { - // do something + // do something } - + public function onUpdateError($new_data, $old_data, $id) { - // do something - } - + // do something + } + function renderHelp() { $this->testUrl = SITEURL."?route=system/xup/test"; @@ -288,18 +207,19 @@ class social_ui extends e_admin_ui } - // optional - a custom page. + // optional - a custom page. public function configurePage() { $ns = e107::getRender(); $frm = e107::getForm(); $pref = e107::pref('core'); - - - - // e107::getMessage()->addInfo($notice); - + require_once("social_setup.php"); + $social_setup = new social_setup(); + if ($social_setup->upgrade_required()) + { + return "

" . LAN_SOCIAL_UPDATE_REQUIRED . "

"; + } $text = "@@ -315,125 +235,34 @@ class social_ui extends e_admin_ui
".LAN_SOCIAL_ADMIN_07."
- + "; + $supported_providers = $this->social_login_config_manager->getSupportedProviders(); + $configured_providers = $this->social_login_config_manager->getConfiguredProviders(); + $unconfigured_providers = array_diff($supported_providers, $configured_providers); + $unsupported_providers = array_diff($configured_providers, $supported_providers); + $configured_providers = array_diff($configured_providers, $unsupported_providers); + $text .= $this->generateSocialLoginSection( + LAN_SOCIAL_LOGIN_SECTION_UNSUPPORTED, + LAN_SOCIAL_LOGIN_SECTION_UNSUPPORTED_DESCRIPTION, + $unsupported_providers + ); + $text .= $this->generateSocialLoginSection( + LAN_SOCIAL_LOGIN_SECTION_CONFIGURED, + LAN_SOCIAL_LOGIN_SECTION_CONFIGURED_DESCRIPTION, + $configured_providers + ); + $text .= $this->generateSocialLoginSection( + LAN_SOCIAL_LOGIN_SECTION_UNCONFIGURED, + LAN_SOCIAL_LOGIN_SECTION_UNCONFIGURED_DESCRIPTION, + $unconfigured_providers + ); - - - + $text .= "
- - - - - - - - - - - - - - - - - - - "; - - if(!is_array($pref['social_login'])) - { - $pref['social_login'] = array(); - } - - foreach($this->social_logins as $prov=>$val) - { - $textKeys = ''; - $textScope = ''; - $keyCount= array(); - $label = varset($this->social_external[$prov]) ? "".$prov."" : $prov; - $radio_label = strtolower($prov); - $text .= " - - - - "; - foreach($val as $k=>$v) - { - - - switch ($k) { - case 'enabled': - $eopt = array('class'=>'e-expandit'); - $textEnabled = $frm->radio_switch('social_login['.$prov.'][enabled]', vartrue($pref['social_login'][$prov]['enabled']),'','',$eopt); - break; - - case 'keys': - // $cls = vartrue($pref['single_login'][$prov]['keys'][$tk]) ? "class='e-hideme'" : ''; - $sty = vartrue($pref['social_login'][$prov]['keys'][vartrue($tk)]) ? "" : "e-hideme"; - // $text .= "
"; - foreach($v as $tk=>$idk) - { - $eopt = array( 'size'=>'block-level'); - $keyCount[] = 1; - $textKeys .= "
"; - } - // $text .= ""; - - break; - - case 'scope': - $eopt = array( 'size'=>'block-level'); - $keyCount[] = 1; - $textScope .= ""; - break; - - default: - - break; - } - - - - - } - - - - if(empty($keyCount)) - { - $textKeys = ""; - } - elseif(count($keyCount) == 1) - { - $textKeys .= ""; - } - elseif(count($keyCount) == 2) - { - $textKeys .= ""; - } - - $text .= $textKeys.$textScope.""; - - $text .= " - - "; - } - - - - - - - - $text .= "
".LAN_SOCIAL_ADMIN_04."".LAN_SOCIAL_ADMIN_05."".LAN_SOCIAL_ADMIN_06."".LAN_SOCIAL_ADMIN_38."".LAN_SOCIAL_ADMIN_03."
".$frm->text('social_login['.$prov.'][keys]['.$tk.']', vartrue($pref['social_login'][$prov]['keys'][$tk]), 100, $eopt)."".$frm->text('social_login['.$prov.'][scope]', vartrue($pref['social_login'][$prov]['scope']), 100, $eopt)."      ".$textEnabled."
-
"; - - // ------------------------------- // // @@ -454,7 +283,7 @@ class social_ui extends e_admin_ui 'twitter' => array('label'=>"Twitter", "placeholder"=>"eg. https://twitter.com/e107"), 'youtube' => array('label'=>"Youtube", "placeholder"=>"eg.https://youtube.com/e107Inc"), 'linkedin' => array('label'=>"LinkedIn", "placeholder"=>"eg. http://www.linkedin.com/groups?home=&gid=1782682"), - 'github' => array('label'=>"Github", "placeholder"=>"eg. https://github.com/e107inc"), + 'github' => array('label'=>"GitHub", "placeholder"=>"eg. https://github.com/e107inc"), 'flickr' => array('label'=>"Flickr", "placeholder"=>""), 'instagram' => array('label'=>"Instagram", "placeholder"=>""), 'pinterest' => array('label'=>"Pinterest", "placeholder"=>""), @@ -506,17 +335,133 @@ class social_ui extends e_admin_ui return $ret; } - + /** + * @param $text + * @param array $provider_names + * @return string + */ + private function generateSocialLoginSection($section_name, $section_explanation, $provider_names) + { + if (empty($provider_names)) return ""; + + $text = " + + +

$section_name

+

$section_explanation

+ + + + + + + + + + + + + + + + + + + + "; + + foreach ($provider_names as $provider_name) + { + $text .= $this->generateSocialLoginRow($provider_name); + } + + $text .= "
" . LAN_SOCIAL_ADMIN_04 . "" . LAN_SOCIAL_ADMIN_AUTH_TYPE . "" . LAN_SOCIAL_ADMIN_05 . "" . LAN_SOCIAL_ADMIN_06 . "" . LAN_SOCIAL_ADMIN_38 . "" . LAN_SOCIAL_ADMIN_03 . "
+ + "; + + return $text; + } + + /** + * @param $provider_name + * @return string Text to append + */ + private function generateSocialLoginRow($provider_name) + { + $slcm = $this->social_login_config_manager; + $provider_type = $slcm->getTypeOfProvider($provider_name); + if (empty($provider_type)) $provider_type = "" . LAN_SOCIAL_ADMIN_AUTH_TYPE_UNKNOWN . ""; + + $normalized_provider_name = $slcm->normalizeProviderName($provider_name); + list($pretty_provider_name,) = array_pad(explode("-", $normalized_provider_name), 2, ""); + + $frm = e107::getForm(); + $textKeys = ''; + $textScope = ''; + $label = varset($this->social_external[$provider_name]) ? "" . $pretty_provider_name . "" : $pretty_provider_name; + $radio_label = strtolower($provider_name); + $text = " + + + $provider_type + "; + + if ($provider_type == "OpenID") + { + $openid_identifier = $slcm->getProviderConfig($provider_name, '/openid_identifier'); + $frm_options = ['size' => 'block-level']; + if (empty($openid_identifier)) + { + try + { + $class = "\Hybridauth\Provider\\$provider_name"; + $reflection = new ReflectionClass($class); + $properties = $reflection->getDefaultProperties(); + $frm_options['placeholder'] = $properties['openidIdentifier']; + } + catch (Exception $e) + { + $openid_identifier = ""; + } + } + $textKeys .= "" . + $frm->text("social_login[$provider_name][openid_identifier]", $openid_identifier, 256, $frm_options) . + ""; + } + else + { + $textKeys .= "" . $frm->text("social_login[$provider_name][keys][id]", $slcm->getProviderConfig($provider_name, '/keys/id'), 128, ['size' => 'block-level']); + $textKeys .= "" . $frm->text("social_login[$provider_name][keys][secret]", $slcm->getProviderConfig($provider_name, '/keys/secret'), 128, ['size' => 'block-level']); + if ($provider_type == "OAuth2" || $slcm->getProviderConfig($provider_name, '/scope')) + { + $textKeys .= "" . $frm->text("social_login[$provider_name][scope]", $slcm->getProviderConfig($provider_name, '/scope'), 128, ['size' => 'block-level']); + } + else + { + $textKeys .= ""; + } + } + + $textEnabled = $frm->radio_switch("social_login[$provider_name][enabled]", $slcm->isProviderEnabled($provider_name), '', '', ['class' => 'e-expandit']); + + $text .= $textKeys . $textScope . "" . $textEnabled . ""; + + $text .= " + + "; + + return $text; + } } - + class social_form_ui extends e_admin_form_ui { -} - - +} + + new social_adminarea(); require_once(e_ADMIN."auth.php"); diff --git a/e107_plugins/social/languages/English/English_admin.php b/e107_plugins/social/languages/English/English_admin.php index d20309ac5..eaee772cc 100644 --- a/e107_plugins/social/languages/English/English_admin.php +++ b/e107_plugins/social/languages/English/English_admin.php @@ -50,3 +50,28 @@ define("LAN_SOCIAL_ADMIN_39", "Providers"); define("LAN_SOCIAL_ADMIN_40", "Update User Display Name"); define("LAN_SOCIAL_ADMIN_41", "Update User Avatar"); define("LAN_SOCIAL_ADMIN_42", "Custom Image"); +define("LAN_SOCIAL_ADMIN_AUTH_TYPE", "Type"); +define("LAN_SOCIAL_ADMIN_AUTH_TYPE_UNKNOWN", "Unknown"); + +define("LAN_SOCIAL_UPDATE_REQUIRED", + "A database update is required to continue using this plugin." +); + +define("LAN_SOCIAL_LOGIN_SECTION_UNSUPPORTED", "Broken Configured Providers"); +define("LAN_SOCIAL_LOGIN_SECTION_CONFIGURED", "Manage Existing Providers"); +define("LAN_SOCIAL_LOGIN_SECTION_UNCONFIGURED", "Add New Providers"); + +define("LAN_SOCIAL_LOGIN_SECTION_UNSUPPORTED_DESCRIPTION", + "These social login providers were configured in the past but no longer have an adapter that can support them. " . + "This may be due to them no longer existing or being replaced by a different provider." +); +define("LAN_SOCIAL_LOGIN_SECTION_CONFIGURED_DESCRIPTION", + "These social login providers are currently configured. " . + "If the master switch \"" . LAN_SOCIAL_ADMIN_02 . "\" is toggled on, each provider in this table that is " . + "also toggled on may be used for user registration and login. If you empty the fields of a provider here and save, " . + "it will move to the \"" . LAN_SOCIAL_LOGIN_SECTION_UNCONFIGURED . "\" section." +); +define("LAN_SOCIAL_LOGIN_SECTION_UNCONFIGURED_DESCRIPTION", + "These are the available social login providers, which have not been configured. " . + "Once you configure and save a provider here, it will move to the \"" . LAN_SOCIAL_LOGIN_SECTION_CONFIGURED . "\" section." +); \ No newline at end of file diff --git a/e107_plugins/social/social_setup.php b/e107_plugins/social/social_setup.php new file mode 100644 index 000000000..8e13b84aa --- /dev/null +++ b/e107_plugins/social/social_setup.php @@ -0,0 +1,87 @@ +getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF); + $normalizedProviderNames = array_keys($providerConfig); + foreach ($normalizedProviderNames as $normalizedProviderName) + { + $actualNormalizedProviderName = + $manager->normalizeProviderName($manager->denormalizeProviderName($normalizedProviderName)); + if ($actualNormalizedProviderName !== $normalizedProviderName) return true; + } + return false; + } + + public function upgrade_pre() + { + $coreConfig = e107::getConfig(); + $logger = e107::getMessage(); + $manager = new SocialLoginConfigManager($coreConfig); + + $providerConfig = $coreConfig->getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF); + + foreach ($providerConfig as $oldNormalizedProviderName => $oldOptions) + { + $denormalizedProviderName = $manager->denormalizeProviderName($oldNormalizedProviderName); + $denormalizedProviderName = $this->upgradeDenormalizedProviderQuirks($denormalizedProviderName); + $actualNormalizedProviderName = $manager->normalizeProviderName($denormalizedProviderName); + + $newOptions = $oldOptions; + if (isset($newOptions['keys']['key'])) + { + $newOptions['keys']['id'] = $newOptions['keys']['key']; + unset($newOptions['keys']['key']); + } + + if ($newOptions != $oldOptions) + { + $manager->setProviderConfig($denormalizedProviderName, $newOptions); + $logger->addSuccess( + "Updated configuration format of social login provider $denormalizedProviderName" + ); + } + + if ($actualNormalizedProviderName !== $oldNormalizedProviderName) + { + $manager->setProviderConfig($denormalizedProviderName, $newOptions); + $coreConfig->removePref( + SocialLoginConfigManager::SOCIAL_LOGIN_PREF . '/' . $oldNormalizedProviderName + ); + $logger->addSuccess( + "Updated name of social login provider $oldNormalizedProviderName → $actualNormalizedProviderName" + ); + } + } + + $manager->saveProviderConfig(); + } + + private function upgradeDenormalizedProviderQuirks($denormalizedProviderName) + { + switch ($denormalizedProviderName) + { + case 'AOL': + $denormalizedProviderName = 'AOLOpenID'; + break; + case 'Live': + $denormalizedProviderName = 'WindowsLive'; + break; + } + return $denormalizedProviderName; + } +} \ No newline at end of file diff --git a/e107_tests/tests/unit/e_user_providerTest.php b/e107_tests/tests/unit/e_user_providerTest.php new file mode 100644 index 000000000..30392747b --- /dev/null +++ b/e107_tests/tests/unit/e_user_providerTest.php @@ -0,0 +1,60 @@ +e_user_provider = $this->make('e_user_provider'); + } + catch (Exception $e) + { + $this->fail("Couldn't load e_user_provider object: {$e}"); + } + } + + public function testGetSupportedProviders() + { + $result = e_user_provider::getSupportedProviders(); + $this->assertIsArray($result); + $this->assertContains("Facebook", $result); + $this->assertContains("Twitter", $result); + $this->assertCount(42, $result, + "The number of Hybridauth providers has changed! If this is intentional, note the change " . + "in Hybridauth providers in the release changelog and update the count in this test." + ); + } + + public function testGetProviderType() + { + $result = e_user_provider::getTypeOf("NotARealProvider"); + $this->assertFalse($result); + + $result = e_user_provider::getTypeOf("Steam"); + $this->assertEquals("OpenID", $result); + + $result = e_user_provider::getTypeOf("StackExchangeOpenID"); + $this->assertEquals("OpenID", $result); + + $result = e_user_provider::getTypeOf("Twitter"); + $this->assertEquals("OAuth1", $result); + + $result = e_user_provider::getTypeOf("WordPress"); + $this->assertEquals("OAuth2", $result); + } +} diff --git a/e107_tests/tests/unit/plugins/social/SocialLoginConfigManagerTest.php b/e107_tests/tests/unit/plugins/social/SocialLoginConfigManagerTest.php new file mode 100644 index 000000000..3117c1983 --- /dev/null +++ b/e107_tests/tests/unit/plugins/social/SocialLoginConfigManagerTest.php @@ -0,0 +1,215 @@ +pref = $this->make('e_pref'); + $this->pref->set('social_login', [ + 'Twitter-OAuth1' => [ + 'enabled' => true, + 'keys' => [ + 'id' => 'ID', + 'secret' => 'SECRET', + ], + ], + 'StackExchange-OpenID' => [ + 'enabled' => false, + ], + 'GitHub-OAuth2' => [ + 'enabled' => true, + 'keys' => [ + 'id' => 'ID', + 'secret' => 'SECRET', + ], + 'scope' => 'identity', + ], + 'OpenID' => [ + 'enabled' => true, + ], + ]); + $this->manager = new SocialLoginConfigManager($this->pref); + } + + public function testIsProviderEnabled() + { + $this->assertTrue($this->manager->isProviderEnabled('Twitter')); + $this->assertFalse($this->manager->isProviderEnabled('StackExchangeOpenID')); + $this->assertTrue($this->manager->isProviderEnabled('GitHub')); + $this->assertTrue($this->manager->isProviderEnabled('OpenID')); + } + + public function testForgetProvider() + { + $this->manager->forgetProvider('OpenID'); + $result = $this->manager->getConfiguredProviders(); + $this->assertCount(3, $result); + + $this->manager->forgetProvider('StackExchangeOpenID'); + $result = $this->manager->getConfiguredProviders(); + $this->assertCount(2, $result); + + $this->manager->forgetProvider('FakeProvider'); + $result = $this->manager->getConfiguredProviders(); + $this->assertCount(2, $result); + } + + public function testSetProviderConfig() + { + $this->manager->setProviderConfig('MyEnabledProvider', ['enabled' => true]); + $result = $this->manager->getConfiguredProviders(); + $this->assertContains('MyEnabledProvider', $result); + $this->assertTrue($this->manager->isProviderEnabled('MyEnabledProvider')); + + $this->manager->setProviderConfig('MyDisabledProvider', ['garbage' => 'nonsense']); + $result = $this->manager->getConfiguredProviders(); + $this->assertContains('MyDisabledProvider', $result); + $this->assertFalse($this->manager->isProviderEnabled('MyDisabledProvider')); + } + + public function testSetProviderConfigForgetsProviderIfEmpty() + { + $this->manager->setProviderConfig('EmptyProvider', [ + 'enabled' => null, + 'keys' => [ + 'id' => '', + 'secret' => 0, + ], + 'scope' => false, + ]); + $result = $this->manager->getConfiguredProviders(); + $this->assertNotContains('EmptyProvider', $result); + } + + public function testSetProviderConfigDiscardsEmptyOptions() + { + $this->manager->setProviderConfig('MiscProvider', [ + 'enabled' => true, + 'openid_identifier' => '', + 'keys' => [ + 'id' => null, + 'secret' => 0, + ], + 'scope' => false, + ]); + $result = $this->manager->getProviderConfig('MiscProvider'); + $this->assertEquals(['enabled' => true], $result); + } + + public function testGetProviderConfig() + { + $result = $this->manager->getProviderConfig('Twitter'); + $this->assertTrue($result['enabled']); + $this->assertArrayHasKey('keys', $result); + $this->assertArrayNotHasKey('scope', $result); + + $result = $this->manager->getProviderConfig('Twitter', 'keys/id'); + $this->assertEquals('ID', $result); + + $result = $this->manager->getProviderConfig('Twitter', '/keys/secret'); + $this->assertEquals('SECRET', $result); + + $result = $this->manager->getProviderConfig('Twitter', '/fake'); + $this->assertNull($result); + + $result = $this->manager->getProviderConfig('StackExchangeOpenID'); + $this->assertFalse($result['enabled']); + $this->assertArrayNotHasKey('keys', $result); + $this->assertArrayNotHasKey('scope', $result); + + $result = $this->manager->getProviderConfig('GitHub'); + $this->assertEquals('identity', $result['scope']); + } + + public function testGetConfiguredProviders() + { + $result = $this->manager->getConfiguredProviders(); + + $this->assertCount(4, $result); + $this->assertContains('Twitter', $result); + $this->assertContains('StackExchangeOpenID', $result); + $this->assertContains('GitHub', $result); + $this->assertContains('OpenID', $result); + } + + public function testNormalizeProviderNameFixesCapitalization() + { + $output = $this->manager->normalizeProviderName("Github"); + $this->assertEquals("GitHub-OAuth2", $output); + } + + public function testNormalizeProviderNamePassesThroughUnknownName() + { + $output = $this->manager->normalizeProviderName("iPhone"); + $this->assertEquals("iPhone", $output); + } + + public function testNormalizeProviderNameRemovesTypeFromName() + { + $output = $this->manager->normalizeProviderName("StackExchangeOpenID"); + $this->assertEquals("StackExchange-OpenID", $output); + + $output = $this->manager->normalizeProviderName("aolOPENid"); + $this->assertEquals("AOL-OpenID", $output); + } + + public function testNormalizeProviderNameFindsCorrectType() + { + $output = $this->manager->normalizeProviderName("StackExchange"); + $this->assertEquals("StackExchange-OAuth2", $output); + + $output = $this->manager->normalizeProviderName("Telegram"); + $this->assertEquals("Telegram", $output); + } + + public function testNormalizeProviderNameGeneric() + { + $output = $this->manager->normalizeProviderName("openid"); + $this->assertEquals("OpenID", $output); + } + + public function testNormalizeProviderNameFakeGeneric() + { + $output = $this->manager->normalizeProviderName("OAuth2"); + $this->assertEquals("OAuth2", $output); + } + + public function testDenormalizeProviderName() + { + $output = $this->manager->denormalizeProviderName("OpenID"); + $this->assertEquals("OpenID", $output); + + $output = $this->manager->denormalizeProviderName("StackExchange-OAuth1"); + $this->assertEquals("StackExchangeOAuth1", $output); + + $output = $this->manager->denormalizeProviderName("StackExchange-OAuth2"); + $this->assertEquals("StackExchange", $output); + + $output = $this->manager->denormalizeProviderName("StackExchange-OpenID"); + $this->assertEquals("StackExchangeOpenID", $output); + } +} \ No newline at end of file diff --git a/e107_tests/tests/unit/plugins/social/social_setupTest.php b/e107_tests/tests/unit/plugins/social/social_setupTest.php new file mode 100644 index 000000000..e06854d39 --- /dev/null +++ b/e107_tests/tests/unit/plugins/social/social_setupTest.php @@ -0,0 +1,127 @@ +set(SocialLoginConfigManager::SOCIAL_LOGIN_PREF, SOCIAL_LOGIN_LEGACY_DATA); + $social_setup = new social_setup(); + $this->assertTrue($social_setup->upgrade_required()); + $this->assertIsArray(e107::getConfig()->getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF . "/AOL")); + $this->assertIsNotArray(e107::getConfig()->getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF . "/AOL-OpenID")); + + $social_setup->upgrade_pre(); + $this->assertFalse($social_setup->upgrade_required()); + $this->assertIsNotArray(e107::getConfig()->getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF . "/AOL")); + $this->assertIsArray(e107::getConfig()->getPref(SocialLoginConfigManager::SOCIAL_LOGIN_PREF . "/AOL-OpenID")); + } +} +const SOCIAL_LOGIN_LEGACY_DATA = +array( + 'FakeProviderNeverExisted' => + array( + 'enabled' => '1', + ), + 'AOL' => + array( + 'enabled' => '1', + ), + 'Facebook' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'scope' => 'c', + 'enabled' => '1', + ), + 'Foursquare' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'enabled' => '1', + ), + 'Github' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'scope' => 'c', + 'enabled' => '1', + ), + 'Google' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'scope' => 'c', + 'enabled' => '1', + ), + 'LinkedIn' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'enabled' => '1', + ), + 'Live' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'enabled' => '1', + ), + 'OpenID' => + array( + 'enabled' => '1', + ), + 'Steam' => + array( + 'keys' => + array( + 'key' => 'a', + ), + 'enabled' => '1', + ), + 'Twitter' => + array( + 'keys' => + array( + 'key' => 'a', + 'secret' => 'b', + ), + 'enabled' => '1', + ), + 'Yahoo' => + array( + 'keys' => + array( + 'id' => 'a', + 'secret' => 'b', + ), + 'enabled' => '1', + ), +); \ No newline at end of file