diff --git a/e107_admin/prefs.php b/e107_admin/prefs.php index 4d0b7454f..497e9fdda 100644 --- a/e107_admin/prefs.php +++ b/e107_admin/prefs.php @@ -58,7 +58,12 @@ if(isset($_POST['submit_resetdisplaynames'])) /* UPDATE PREFERENCES */ if(isset($_POST['updateprefs'])) { + + + unset($_POST['updateprefs'], $_POST['sitelanguage']); + + print_a($_POST); $_POST['cookie_name'] = str_replace(array(" ", "."), "_", $_POST['cookie_name']); $_POST['cookie_name'] = preg_replace("#[^a-zA-Z0-9_]#", "", $_POST['cookie_name']); @@ -660,6 +665,139 @@ $text .= " "; + +// Single Login / / copied from hybridAuth config.php so it's easy to add more. +// Used Below. + +$single_logins = array ( + // openid providers + "OpenID" => array ( + "enabled" => true + ), + + "Yahoo" => array ( + "enabled" => true + ), + + "AOL" => array ( + "enabled" => true + ), + + "Google" => array ( + "enabled" => true, + "keys" => array ( "id" => "", "secret" => "" ), + "scope" => "" + ), + + "Facebook" => array ( + "enabled" => true, + "keys" => array ( "id" => "", "secret" => "" ), + + // 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" => "", + + // 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" => "" + ), + + "Twitter" => array ( + "enabled" => true, + "keys" => array ( "key" => "", "secret" => "" ) + ), + + // windows live + "Live" => array ( + "enabled" => true, + "keys" => array ( "id" => "", "secret" => "" ) + ), + + "MySpace" => array ( + "enabled" => true, + "keys" => array ( "key" => "", "secret" => "" ) + ), + + "LinkedIn" => array ( + "enabled" => true, + "keys" => array ( "key" => "", "secret" => "" ) + ), + + "Foursquare" => array ( + "enabled" => true, + "keys" => array ( "id" => "", "secret" => "" ) + ) + ); + + + + + + +$text .= " +
+ Single Login Options +
Note: This section is not functional at the moment
+ + + + + + + + + + "; + + foreach($single_logins as $prov=>$val) + { + $text .= " + + + + + "; + } + + + +$text .= " + +
Enable Single Logins + ".$frm->radio_switch('single_login_active', $pref['single_login_active'])." +
".$prov." + "; + foreach($val as $k=>$v) + { + switch ($k) { + case 'enabled': + $eopt = array('class'=>'e-expandit'); + $text .= $frm->radio_switch('single_login['.$prov.'][enabled]', $pref['single_login'][$prov]['enabled'],'','',$eopt); + break; + + case 'keys': + // $cls = vartrue($pref['single_login'][$prov]['keys'][$tk]) ? "class='e-hideme'" : ''; + $sty = vartrue($pref['single_login'][$prov]['keys'][$tk]) ? "" : "display:none"; + $text .= "
"; + foreach($v as $tk=>$idk) + { + $opt['placeholder'] = $tk; + $text .= "
".$frm->text('single_login['.$prov.'][keys]['.$tk.']', $pref['single_login'][$prov]['keys'][$tk],20,$opt); + } + $text .= "
"; + + break; + + default: + + break; + } + } + + $text .= "
+ ".pref_submit('single-login')." +
+"; + + // Signup options ===========================. @@ -1256,39 +1394,17 @@ $text .= " ); - //TODO FIXME - remove JS framework dependency from front-end and backend. - // ie. no JS errors when prototype.js is completely disabled. - // no JS error with only 'e107 Core Minimum' is enabled. - // e107 Core Minimum should function independently of framework. + //TODO FIXME // ie. e107 Core Minimum: JS similar to e107 v1.0 should be loaded "e_js.php" (no framwork dependency) // with basic functions like SyncWithServerTime() and expandit(), externalLinks() etc. - - //TODO Move paths into js_manager and store only keys like 'prototype-local', 'core-minimum' in prefs. - /* - $js_types = array( - array('name'=> 'Prototype (local)', 'path'=> 'prototype/prototype.js') , - array('name'=> 'Scriptaculous (local)', 'path'=> 'scriptaculous/scriptaculous.js'), - array('name'=> 'Scriptaculous-effects (local)', 'path'=> 'scriptaculous/effects.js'), - array('name'=> 'Prototype (CDN)', 'path'=> 'https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js'), - array('name'=> 'Scriptaculous + Effects (CDN)', 'path'=> 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/scriptaculous.js?load=effects'), - array('name'=> 'jQuery (CDN)', 'path'=> 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'), - array('name'=> 'jQuery UI (CDN)', 'path'=> 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js'), - array('name'=> 'e107 Core Minimum', 'path'=> 'e107.js'), - array('name'=> 'e107 Plugin Scripts (TO-DO)', 'path'=> ''), // all js that has been added by Plugins. - array('name'=> 'e107 Theme Scripts (TO-DO)', 'path'=> ''), // all js that has been added by Themes. - ); - */ - - // e107 minimum loaded by default once dependency is removed. + $js_types = array( array('id' => 'prototype', 'name'=> 'Prototype (local)'), - // array('id' => 'prototype-cdn', 'name'=> 'Prototype (CDN)'), - array('id' => 'jquery', 'name'=> 'jQuery (local)'), - // array('id' => 'jquery-cdn', 'name'=> 'jQuery (CDN)') + array('id' => 'jquery', 'name'=> 'jQuery (local)'), ); - + //TODO // separate switch for CDN.. or automatic fall-back. @@ -1484,6 +1600,7 @@ function prefs_adminmenu() $var['core-prefs-date']['text'] = PRFLAN_21; $var['core-prefs-registration']['text'] = PRFLAN_28; $var['core-prefs-signup']['text'] = PRFLAN_19; + $var['core-prefs-single-login']['text'] = "Single-Login"; $var['core-prefs-textpost']['text'] = PRFLAN_101; $var['core-prefs-security']['text'] = PRFLAN_47; $var['core-prefs-comments']['text'] = PRFLAN_210; diff --git a/e107_core/shortcodes/batch/signup_shortcodes.php b/e107_core/shortcodes/batch/signup_shortcodes.php index 52022fe77..769142ba8 100755 --- a/e107_core/shortcodes/batch/signup_shortcodes.php +++ b/e107_core/shortcodes/batch/signup_shortcodes.php @@ -40,6 +40,31 @@ class signup_shortcodes extends e_shortcode } } + function sc_signup_xup() // show it to those who were using xup + { + return $this->sc_single_login(); + } + + + function sc_single_login() + { + $pref['single_login_active'] = 1; + + if(vartrue($pref['single_login_active'])) + { + $text = ""; + $providers = array('facebook','twitter','google','yahoo'); + foreach($providers as $p) + { + $text .= ""; + } + + $text .= "
"; + return $text; + // return $tp->parseTemplate($SIGNUP_XUP_FORM, TRUE, $signup_shortcodes); + } + } + function sc_signup_form_open() { diff --git a/e107_files/jslib/core/all.jquery.js b/e107_files/jslib/core/all.jquery.js index 100b2f658..754fdc8db 100644 --- a/e107_files/jslib/core/all.jquery.js +++ b/e107_files/jslib/core/all.jquery.js @@ -3,14 +3,77 @@ $(document).ready(function() $(".e-hideme").hide(); $(".e-expandit").show(); - + + + // default 'toggle'. $(".e-expandit").click(function () { + var href = ($(this).is("a")) ? $(this).attr("href") : ''; + + if(href === "#" || href == "") + { + idt = $(this).nextAll("div"); + $(idt).toggle("slow"); + return true; + } + var id = $(this).attr("href"); $(id).toggle("slow"); return false; }); + // On + $(".e-expandit-on").click(function () { + + var href = ($(this).is("a")) ? $(this).attr("href") : ''; + + if(href === "#" || href == "") + { + idt = $(this).nextAll("div"); + $(idt).show("slow"); + return true; // must be true or radio buttons etc. won't work + } + + if($(this).attr("data-expandit")) + { + var id = $(this).attr("data-expandit"); + } + else + { + var id = $(this).attr("href"); + } + + $(id).show("slow"); + return false; + }); + + // Off. + $(".e-expandit-off").click(function () { + + var href = ($(this).is("a")) ? $(this).attr("href") : ''; + + if(href === "#" || href == "") + { + idt = $(this).nextAll("div"); + $(idt).hide("slow"); + return true; // must be true or radio buttons etc. won't work + } + + if($(this).attr("data-expandit")) + { + var id = $(this).attr("data-expandit"); + } + else + { + var id = $(this).attr("href"); + } + + $(id).hide("slow"); + return false; + }); + + + // Dates -------------------------------------------------- $("input.e-date").each(function() { diff --git a/e107_handlers/form_handler.php b/e107_handlers/form_handler.php index 1454c6852..f218449b5 100644 --- a/e107_handlers/form_handler.php +++ b/e107_handlers/form_handler.php @@ -735,10 +735,19 @@ class e_form } - function radio_switch($name, $checked_enabled = false, $label_enabled = '', $label_disabled = '') + function radio_switch($name, $checked_enabled = false, $label_enabled = '', $label_disabled = '',$options=array()) { - return $this->radio($name, 1, $checked_enabled)."".$this->label($label_enabled ? $label_enabled : LAN_ENABLED, $name, 1)."   - ".$this->radio($name, 0, !$checked_enabled)."".$this->label($label_disabled ? $label_disabled : LAN_DISABLED, $name, 0); + $options_on = array(); + $options_off = array(); + + if($options['class'] == 'e-expandit') // See admin->prefs 'Single Login' for an example. + { + $options_on = array('class' => 'e-expandit-on'); + $options_off = array('class' => 'e-expandit-off'); + } + + return $this->radio($name, 1, $checked_enabled, $options_on)."".$this->label($label_enabled ? $label_enabled : LAN_ENABLED, $name, 1)."   + ".$this->radio($name, 0, !$checked_enabled, $options_off)."".$this->label($label_disabled ? $label_disabled : LAN_DISABLED, $name, 0); } diff --git a/e107_handlers/hybridauth/Hybrid/Auth.php b/e107_handlers/hybridauth/Hybrid/Auth.php new file mode 100644 index 000000000..8b4202301 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Auth.php @@ -0,0 +1,408 @@ += 5.3.0) http://php.net/manual/en/exception.getprevious.php + if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) && ($p instanceof Exception) ) { + throw new Exception( $m, $c, $p ); + } + else{ + throw new Exception( $m, $c ); + } + } + + Hybrid_Logger::info( "Hybrid_Auth initialize: no error found. initialization succeed." ); + + // Endof initialize + } + + // -------------------------------------------------------------------- + + /** + * Hybrid storage system accessor + * + * Users sessions are stored using HybridAuth storage system ( HybridAuth 2.0 handle PHP Session only) and can be acessed directly by + * Hybrid_Auth::storage()->get($key) to retrieves the data for the given key, or calling + * Hybrid_Auth::storage()->set($key, $value) to store the key => $value set. + */ + public static function storage() + { + return Hybrid_Auth::$store; + } + + // -------------------------------------------------------------------- + + /** + * Get hybridauth session data. + */ + function getSessionData() + { + return Hybrid_Auth::storage()->getSessionData(); + } + + // -------------------------------------------------------------------- + + /** + * restore hybridauth session data. + */ + function restoreSessionData( $sessiondata = NULL ) + { + Hybrid_Auth::storage()->restoreSessionData( $sessiondata ); + } + + // -------------------------------------------------------------------- + + /** + * Try to authenticate the user with a given provider. + * + * If the user is already connected we just return and instance of provider adapter, + * ELSE, try to authenticate and authorize the user with the provider. + * + * $params is generally an array with required info in order for this provider and HybridAuth to work, + * like : + * hauth_return_to: URL to call back after authentication is done + * openid_identifier: The OpenID identity provider identifier + * google_service: can be "Users" for Google user accounts service or "Apps" for Google hosted Apps + */ + public static function authenticate( $providerId, $params = NULL ) + { + Hybrid_Logger::info( "Enter Hybrid_Auth::authenticate( $providerId )" ); + + // if user not connected to $providerId then try setup a new adapter and start the login process for this provider + if( ! Hybrid_Auth::storage()->get( "hauth_session.$providerId.is_logged_in" ) ){ + Hybrid_Logger::info( "Hybrid_Auth::authenticate( $providerId ), User not connected to the provider. Try to authenticate.." ); + + $provider_adapter = Hybrid_Auth::setup( $providerId, $params ); + + $provider_adapter->login(); + } + + // else, then return the adapter instance for the given provider + else{ + Hybrid_Logger::info( "Hybrid_Auth::authenticate( $providerId ), User is already connected to this provider. Return the adapter instance." ); + + return Hybrid_Auth::getAdapter( $providerId ); + } + } + + // -------------------------------------------------------------------- + + /** + * Return the adapter instance for an authenticated provider + */ + public static function getAdapter( $providerId = NULL ) + { + Hybrid_Logger::info( "Enter Hybrid_Auth::getAdapter( $providerId )" ); + + return Hybrid_Auth::setup( $providerId ); + } + + // -------------------------------------------------------------------- + + /** + * Setup an adapter for a given provider + */ + public static function setup( $providerId, $params = NULL ) + { + Hybrid_Logger::debug( "Enter Hybrid_Auth::setup( $providerId )", $params ); + + if( ! $params ){ + $params = Hybrid_Auth::storage()->get( "hauth_session.$providerId.id_provider_params" ); + + Hybrid_Logger::debug( "Hybrid_Auth::setup( $providerId ), no params given. Trying to get the sotred for this provider.", $params ); + } + + if( ! $params ){ + $params = ARRAY(); + + Hybrid_Logger::info( "Hybrid_Auth::setup( $providerId ), no stored params found for this provider. Initialize a new one for new session" ); + } + + if( ! isset( $params["hauth_return_to"] ) ){ + $params["hauth_return_to"] = Hybrid_Auth::getCurrentUrl(); + } + + Hybrid_Logger::debug( "Hybrid_Auth::setup( $providerId ). HybridAuth Callback URL set to: ", $params["hauth_return_to"] ); + + # instantiate a new IDProvider Adapter + $provider = new Hybrid_Provider_Adapter(); + + $provider->factory( $providerId, $params ); + + return $provider; + } + + // -------------------------------------------------------------------- + + /** + * Check if the current user is connected to a given provider + */ + public static function isConnectedWith( $providerId ) + { + return (bool) Hybrid_Auth::storage()->get( "hauth_session.{$providerId}.is_logged_in" ); + } + + // -------------------------------------------------------------------- + + /** + * Return array listing all authenticated providers + */ + public static function getConnectedProviders() + { + $idps = array(); + + foreach( Hybrid_Auth::$config["providers"] as $idpid => $params ){ + if( Hybrid_Auth::isConnectedWith( $idpid ) ){ + $idps[] = $idpid; + } + } + + return $idps; + } + + // -------------------------------------------------------------------- + + /** + * Return array listing all enabled providers as well as a flag if you are connected. + */ + public static function getProviders() + { + $idps = array(); + + foreach( Hybrid_Auth::$config["providers"] as $idpid => $params ){ + if($params['enabled']) { + $idps[$idpid] = array( 'connected' => false ); + + if( Hybrid_Auth::isConnectedWith( $idpid ) ){ + $idps[$idpid]['connected'] = true; + } + } + } + + return $idps; + } + + // -------------------------------------------------------------------- + + /** + * A generic function to logout all connected provider at once + */ + public static function logoutAllProviders() + { + $idps = Hybrid_Auth::getConnectedProviders(); + + foreach( $idps as $idp ){ + $adapter = Hybrid_Auth::getAdapter( $idp ); + + $adapter->logout(); + } + } + + // -------------------------------------------------------------------- + + /** + * Utility function, redirect to a given URL with php header or using javascript location.href + */ + public static function redirect( $url, $mode = "PHP" ) + { + Hybrid_Logger::info( "Enter Hybrid_Auth::redirect( $url, $mode )" ); + + if( $mode == "PHP" ){ + header( "Location: $url" ) ; + } + elseif( $mode == "JS" ){ + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo 'Redirecting, please wait...'; + echo ''; + echo ''; + } + + die(); + } + + // -------------------------------------------------------------------- + + /** + * Utility function, return the current url. TRUE to get $_SERVER['REQUEST_URI'], FALSE for $_SERVER['PHP_SELF'] + */ + public static function getCurrentUrl( $request_uri = true ) + { + if( + isset( $_SERVER['HTTPS'] ) && ( $_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1 ) + || isset( $_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' + ){ + $protocol = 'https://'; + } + else { + $protocol = 'http://'; + } + + $url = $protocol . $_SERVER['SERVER_NAME']; + + // use port if non default + $url .= + isset( $_SERVER['SERVER_PORT'] ) + &&( ($protocol === 'http://' && $_SERVER['SERVER_PORT'] != 80) || ($protocol === 'https://' && $_SERVER['SERVER_PORT'] != 443) ) + ? ':' . $_SERVER['SERVER_PORT'] + : ''; + + if( $request_uri ){ + $url .= $_SERVER['REQUEST_URI']; + } + else{ + $url .= $_SERVER['PHP_SELF']; + } + + // return current url + return $url; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Endpoint.php b/e107_handlers/hybridauth/Hybrid/Endpoint.php new file mode 100644 index 000000000..478673b3a --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Endpoint.php @@ -0,0 +1,242 @@ +here we need to recreate the $_REQUEST + if ( strrpos( $_SERVER["QUERY_STRING"], '?' ) ) { + $_SERVER["QUERY_STRING"] = str_replace( "?", "&", $_SERVER["QUERY_STRING"] ); + + parse_str( $_SERVER["QUERY_STRING"], $_REQUEST ); + } + + Hybrid_Endpoint::$request = $_REQUEST; + } + + // If windows_live_channel requested, we return our windows_live WRAP_CHANNEL_URL + if ( isset( Hybrid_Endpoint::$request["get"] ) && Hybrid_Endpoint::$request["get"] == "windows_live_channel" ) { + Hybrid_Endpoint::processWindowsLiveChannel(); + } + + // If openid_policy requested, we return our policy document + if ( isset( Hybrid_Endpoint::$request["get"] ) && Hybrid_Endpoint::$request["get"] == "openid_policy" ) { + Hybrid_Endpoint::processOpenidPolicy(); + } + + // If openid_xrds requested, we return our XRDS document + if ( isset( Hybrid_Endpoint::$request["get"] ) && Hybrid_Endpoint::$request["get"] == "openid_xrds" ) { + Hybrid_Endpoint::processOpenidXRDS(); + } + + // If we get a hauth.start + if ( isset( Hybrid_Endpoint::$request["hauth_start"] ) && Hybrid_Endpoint::$request["hauth_start"] ) { + Hybrid_Endpoint::processAuthStart(); + } + // Else if hauth.done + elseif ( isset( Hybrid_Endpoint::$request["hauth_done"] ) && Hybrid_Endpoint::$request["hauth_done"] ) { + Hybrid_Endpoint::processAuthDone(); + } + // Else we advertise our XRDS document, something supposed to be done from the Realm URL page + else { + Hybrid_Endpoint::processOpenidRealm(); + } + } + + /** + * Process Windows Live channel request + */ + public static function processWindowsLiveChannel() + { + $output = file_get_contents( dirname(__FILE__) . "/resources/windows_live_channel.html" ); + print $output; + die(); + } + + /** + * Process OpenID policy request + */ + public static function processOpenidPolicy() + { + $output = file_get_contents( dirname(__FILE__) . "/resources/openid_policy.html" ); + print $output; + die(); + } + + /** + * Process OpenID XRDS request + */ + public static function processOpenidXRDS() + { + header("Content-Type: application/xrds+xml"); + + $output = str_replace + ( + "{RETURN_TO_URL}", + str_replace( + array("<", ">", "\"", "'", "&"), array("<", ">", """, "'", "&"), + Hybrid_Auth::getCurrentUrl( false ) + ), + file_get_contents( dirname(__FILE__) . "/resources/openid_xrds.xml" ) + ); + print $output; + die(); + } + + /** + * Process OpenID realm request + */ + public static function processOpenidRealm() + { + $output = str_replace + ( + "{X_XRDS_LOCATION}", + htmlentities( Hybrid_Auth::getCurrentUrl( false ), ENT_QUOTES, 'UTF-8' ) . "?get=openid_xrds&v=" . Hybrid_Auth::$version, + file_get_contents( dirname(__FILE__) . "/resources/openid_realm.html" ) + ); + print $output; + die(); + } + + /** + * define:endpoint step 3. + */ + public static function processAuthStart() + { + Hybrid_Endpoint::authInit(); + + $provider_id = trim( strip_tags( Hybrid_Endpoint::$request["hauth_start"] ) ); + + # check if page accessed directly + if( ! Hybrid_Auth::storage()->get( "hauth_session.$provider_id.hauth_endpoint" ) ) { + Hybrid_Logger::error( "Endpoint: hauth_endpoint parameter is not defined on hauth_start, halt login process!" ); + + header( "HTTP/1.0 404 Not Found" ); + + die( "You cannot access this page directly." ); + } + + # define:hybrid.endpoint.php step 2. + $hauth = Hybrid_Auth::setup( $provider_id ); + + # if REQUESTed hauth_idprovider is wrong, session not created, etc. + if( ! $hauth ) { + Hybrid_Logger::error( "Endpoint: Invalide parameter on hauth_start!" ); + + header( "HTTP/1.0 404 Not Found" ); + die( "Invalide parameter! Please return to the login page and try again." ); + } + + try { + Hybrid_Logger::info( "Endpoint: call adapter [{$provider_id}] loginBegin()" ); + + $hauth->adapter->loginBegin(); + } + catch ( Exception $e ) { + Hybrid_Logger::error( "Exception:" . $e->getMessage(), $e ); + Hybrid_Error::setError( $e->getMessage(), $e->getCode(), $e->getTraceAsString(), $e ); + + $hauth->returnToCallbackUrl(); + } + + die(); + } + + /** + * define:endpoint step 3.1 and 3.2 + */ + public static function processAuthDone() + { + Hybrid_Endpoint::authInit(); + + $provider_id = trim( strip_tags( Hybrid_Endpoint::$request["hauth_done"] ) ); + + $hauth = Hybrid_Auth::setup( $provider_id ); + + if( ! $hauth ) { + Hybrid_Logger::error( "Endpoint: Invalide parameter on hauth_done!" ); + + $hauth->adapter->setUserUnconnected(); + + header("HTTP/1.0 404 Not Found"); + die( "Invalide parameter! Please return to the login page and try again." ); + } + + try { + Hybrid_Logger::info( "Endpoint: call adapter [{$provider_id}] loginFinish() " ); + + $hauth->adapter->loginFinish(); + } + catch( Exception $e ){ + Hybrid_Logger::error( "Exception:" . $e->getMessage(), $e ); + Hybrid_Error::setError( $e->getMessage(), $e->getCode(), $e->getTraceAsString(), $e ); + + $hauth->adapter->setUserUnconnected(); + } + + Hybrid_Logger::info( "Endpoint: job done. retrun to callback url." ); + + $hauth->returnToCallbackUrl(); + die(); + } + + public static function authInit() + { + // print_a($_SESSION); + + + if ( ! Hybrid_Endpoint::$initDone) { + Hybrid_Endpoint::$initDone = TRUE; + + // Start a new session + if ( ! session_id() ){ + session_start(); + } + + # Init Hybrid_Auth + try { + // Check if Hybrid_Auth session already exist + if ( ! isset( $_SESSION["HA::CONFIG"] ) ) { + header( "HTTP/1.0 404 Not Found" ); + die( "You cannot access this page directly." ); + } + + Hybrid_Auth::initialize( unserialize( $_SESSION["HA::CONFIG"] ) ); + } + catch ( Exception $e ){ + Hybrid_Logger::error( "Endpoint: Error while trying to init Hybrid_Auth" ); + + header( "HTTP/1.0 404 Not Found" ); + // print_a($_SESSION); + // echo "e=".$e; + die( "Oophs. Error!" ); + } + } + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Error.php b/e107_handlers/hybridauth/Hybrid/Error.php new file mode 100644 index 000000000..0f4a1046e --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Error.php @@ -0,0 +1,84 @@ +set( "hauth_session.error.status" , 1 ); + Hybrid_Auth::storage()->set( "hauth_session.error.message" , $message ); + Hybrid_Auth::storage()->set( "hauth_session.error.code" , $code ); + Hybrid_Auth::storage()->set( "hauth_session.error.trace" , $trace ); + Hybrid_Auth::storage()->set( "hauth_session.error.previous", $previous ); + } + + /** + * clear the last error + */ + public static function clearError() + { + Hybrid_Logger::info( "Enter Hybrid_Error::clearError()" ); + + Hybrid_Auth::storage()->delete( "hauth_session.error.status" ); + Hybrid_Auth::storage()->delete( "hauth_session.error.message" ); + Hybrid_Auth::storage()->delete( "hauth_session.error.code" ); + Hybrid_Auth::storage()->delete( "hauth_session.error.trace" ); + Hybrid_Auth::storage()->delete( "hauth_session.error.previous" ); + } + + /** + * Checks to see if there is a an error. + * + * @return boolean True if there is an error. + */ + public static function hasError() + { + return (bool) Hybrid_Auth::storage()->get( "hauth_session.error.status" ); + } + + /** + * return error message + */ + public static function getErrorMessage() + { + return Hybrid_Auth::storage()->get( "hauth_session.error.message" ); + } + + /** + * return error code + */ + public static function getErrorCode() + { + return Hybrid_Auth::storage()->get( "hauth_session.error.code" ); + } + + /** + * return string detailled error backtrace as string. + */ + public static function getErrorTrace() + { + return Hybrid_Auth::storage()->get( "hauth_session.error.trace" ); + } + + /** + * @return string detailled error backtrace as string. + */ + public static function getErrorPrevious() + { + return Hybrid_Auth::storage()->get( "hauth_session.error.previous" ); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Logger.php b/e107_handlers/hybridauth/Hybrid/Logger.php new file mode 100644 index 000000000..e7630fe44 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Logger.php @@ -0,0 +1,68 @@ +format(DATE_ATOM); + + file_put_contents( + Hybrid_Auth::$config["debug_file"], + "DEBUG -- " . $_SERVER['REMOTE_ADDR'] . " -- " . $datetime . " -- " . $message . " -- " . print_r($object, true) . "\n", + FILE_APPEND + ); + } + } + + public static function info( $message ) + { + if( Hybrid_Auth::$config["debug_mode"] ){ + $datetime = new DateTime(); + $datetime = $datetime->format(DATE_ATOM); + + file_put_contents( + Hybrid_Auth::$config["debug_file"], + "INFO -- " . $_SERVER['REMOTE_ADDR'] . " -- " . $datetime . " -- " . $message . "\n", + FILE_APPEND + ); + } + } + + public static function error($message, $object = NULL) + { + if( Hybrid_Auth::$config["debug_mode"] ){ + $datetime = new DateTime(); + $datetime = $datetime->format(DATE_ATOM); + + file_put_contents( + Hybrid_Auth::$config["debug_file"], + "ERROR -- " . $_SERVER['REMOTE_ADDR'] . " -- " . $datetime . " -- " . $message . " -- " . print_r($object, true) . "\n", + FILE_APPEND + ); + } + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Provider_Adapter.php b/e107_handlers/hybridauth/Hybrid/Provider_Adapter.php new file mode 100644 index 000000000..81a161272 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Provider_Adapter.php @@ -0,0 +1,283 @@ +id = $id; + $this->params = $params; + $this->id = $this->getProviderCiId( $this->id ); + $this->config = $this->getConfigById( $this->id ); + + # check the IDp id + if( ! $this->id ){ + throw new Exception( "No provider ID specified.", 2 ); + } + + # check the IDp config + if( ! $this->config ){ + throw new Exception( "Unknown Provider ID, check your configuration file.", 3 ); + } + + # check the IDp adapter is enabled + if( ! $this->config["enabled"] ){ + throw new Exception( "The provider '{$this->id}' is not enabled.", 3 ); + } + + # include the adapter wrapper + if( isset( $this->config["wrapper"] ) && is_array( $this->config["wrapper"] ) ){ + require_once $this->config["wrapper"]["path"]; + + if( ! class_exists( $this->config["wrapper"]["class"] ) ){ + throw new Exception( "Unable to load the adapter class.", 3 ); + } + + $this->wrapper = $this->config["wrapper"]["class"]; + } + else{ + require_once Hybrid_Auth::$config["path_providers"] . $this->id . ".php" ; + + $this->wrapper = "Hybrid_Providers_" . $this->id; + } + + # create the adapter instance, and pass the current params and config + $this->adapter = new $this->wrapper( $this->id, $this->config, $this->params ); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Hybrid_Provider_Adapter::login(), prepare the user session and the authentification request + * for index.php + */ + function login() + { + Hybrid_Logger::info( "Enter Hybrid_Provider_Adapter::login( {$this->id} ) " ); + + if( ! $this->adapter ){ + throw new Exception( "Hybrid_Provider_Adapter::login() should not directly used." ); + } + + // clear all unneeded params + foreach( Hybrid_Auth::$config["providers"] as $idpid => $params ){ + Hybrid_Auth::storage()->delete( "hauth_session.{$idpid}.hauth_return_to" ); + Hybrid_Auth::storage()->delete( "hauth_session.{$idpid}.hauth_endpoint" ); + Hybrid_Auth::storage()->delete( "hauth_session.{$idpid}.id_provider_params" ); + } + + // make a fresh start + $this->logout(); + + # get hybridauth base url + $HYBRID_AUTH_URL_BASE = Hybrid_Auth::$config["base_url"]; + + # we make use of session_id() as storage hash to identify the current user + # using session_regenerate_id() will be a problem, but .. + $this->params["hauth_token"] = session_id(); + + # set request timestamp + $this->params["hauth_time"] = time(); + + # for default HybridAuth endpoint url hauth_login_start_url + # auth.start required the IDp ID + # auth.time optional login request timestamp + $this->params["login_start"] = $HYBRID_AUTH_URL_BASE . ( strpos( $HYBRID_AUTH_URL_BASE, '?' ) ? '&' : '?' ) . "hauth.start={$this->id}&hauth.time={$this->params["hauth_time"]}"; + + # for default HybridAuth endpoint url hauth_login_done_url + # auth.done required the IDp ID + $this->params["login_done"] = $HYBRID_AUTH_URL_BASE . ( strpos( $HYBRID_AUTH_URL_BASE, '?' ) ? '&' : '?' ) . "hauth.done={$this->id}"; + + Hybrid_Auth::storage()->set( "hauth_session.{$this->id}.hauth_return_to" , $this->params["hauth_return_to"] ); + Hybrid_Auth::storage()->set( "hauth_session.{$this->id}.hauth_endpoint" , $this->params["login_done"] ); + Hybrid_Auth::storage()->set( "hauth_session.{$this->id}.id_provider_params" , $this->params ); + + // store config to be used by the end point + $_SESSION["HA::CONFIG"] = serialize( Hybrid_Auth::$config ); + + // move on + Hybrid_Logger::debug( "Hybrid_Provider_Adapter::login( {$this->id} ), redirect the user to login_start URL.", $this->params ); + + Hybrid_Auth::redirect( $this->params["login_start"] ); + } + + // -------------------------------------------------------------------- + + /** + * let hybridauth forget all about the user for the current provider + */ + function logout() + { + $this->adapter->logout(); + } + + // -------------------------------------------------------------------- + + /** + * return true if the user is connected to the current provider + */ + public function isUserConnected() + { + return $this->adapter->isUserConnected(); + } + + // -------------------------------------------------------------------- + + /** + * handle : + * getUserProfile() + * getUserContacts() + * getUserActivity() + * setUserStatus() + */ + public function __call( $name, $arguments ) + { + Hybrid_Logger::info( "Enter Hybrid_Provider_Adapter::$name(), Provider: {$this->id}" ); + + if ( ! $this->isUserConnected() ){ + throw new Exception( "User not connected to the provider {$this->id}.", 7 ); + } + + if ( ! method_exists( $this->adapter, $name ) ){ + throw new Exception( "Call to undefined function Hybrid_Providers_{$this->id}::$name()." ); + } + + if( count( $arguments ) ){ + return $this->adapter->$name( $arguments[0] ); + } + else{ + return $this->adapter->$name(); + } + } + + // -------------------------------------------------------------------- + + /** + * If the user is connected, then return the access_token and access_token_secret + * if the provider api use oauth + */ + public function getAccessToken() + { + if( ! $this->adapter->isUserConnected() ){ + Hybrid_Logger::error( "User not connected to the provider." ); + + throw new Exception( "User not connected to the provider.", 7 ); + } + + return + ARRAY( + "access_token" => $this->adapter->token( "access_token" ) , // OAuth access token + "access_token_secret" => $this->adapter->token( "access_token_secret" ), // OAuth access token secret + "refresh_token" => $this->adapter->token( "refresh_token" ) , // OAuth refresh token + "expires_in" => $this->adapter->token( "expires_in" ) , // OPTIONAL. The duration in seconds of the access token lifetime + "expires_at" => $this->adapter->token( "expires_at" ) , // OPTIONAL. Timestamp when the access_token expire. if not provided by the social api, then it should be calculated: expires_at = now + expires_in + ); + } + + // -------------------------------------------------------------------- + + /** + * Naive getter of the current connected IDp API client + */ + function api() + { + if( ! $this->adapter->isUserConnected() ){ + Hybrid_Logger::error( "User not connected to the provider." ); + + throw new Exception( "User not connected to the provider.", 7 ); + } + + return $this->adapter->api; + } + + // -------------------------------------------------------------------- + + /** + * redirect the user to hauth_return_to (the callback url) + */ + function returnToCallbackUrl() + { + // get the stored callback url + $callback_url = Hybrid_Auth::storage()->get( "hauth_session.{$this->id}.hauth_return_to" ); + + // remove some unneed'd stored data + Hybrid_Auth::storage()->delete( "hauth_session.{$this->id}.hauth_return_to" ); + Hybrid_Auth::storage()->delete( "hauth_session.{$this->id}.hauth_endpoint" ); + Hybrid_Auth::storage()->delete( "hauth_session.{$this->id}.id_provider_params" ); + + // back to home + Hybrid_Auth::redirect( $callback_url ); + } + + // -------------------------------------------------------------------- + + /** + * return the provider config by id + */ + function getConfigById( $id ) + { + if( isset( Hybrid_Auth::$config["providers"][$id] ) ){ + return Hybrid_Auth::$config["providers"][$id]; + } + + return NULL; + } + + // -------------------------------------------------------------------- + + /** + * return the provider config by id; insensitive + */ + function getProviderCiId( $id ) + { + foreach( Hybrid_Auth::$config["providers"] as $idpid => $params ){ + if( strtolower( $idpid ) == strtolower( $id ) ){ + return $idpid; + } + } + + return NULL; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Provider_Model.php b/e107_handlers/hybridauth/Hybrid/Provider_Model.php new file mode 100644 index 000000000..2c9134fbe --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Provider_Model.php @@ -0,0 +1,231 @@ +params = Hybrid_Auth::storage()->get( "hauth_session.$providerId.id_provider_params" ); + } + else{ + $this->params = $params; + } + + // idp id + $this->providerId = $providerId; + + // set HybridAuth endpoint for this provider + $this->endpoint = Hybrid_Auth::storage()->get( "hauth_session.$providerId.hauth_endpoint" ); + + // idp config + $this->config = $config; + + // new user instance + $this->user = new Hybrid_User(); + $this->user->providerId = $providerId; + + // initialize the current provider adapter + $this->initialize(); + + Hybrid_Logger::debug( "Hybrid_Provider_Model::__construct( $providerId ) initialized. dump current adapter instance: ", serialize( $this ) ); + } + + // -------------------------------------------------------------------- + + /** + * IDp wrappers initializer + * + * The main job of wrappers initializer is to performs (depend on the IDp api client it self): + * - include some libs nedded by this provider, + * - check IDp key and secret, + * - set some needed parameters (stored in $this->params) by this IDp api client + * - create and setup an instance of the IDp api client on $this->api + */ + abstract protected function initialize(); + + // -------------------------------------------------------------------- + + /** + * begin login + */ + abstract protected function loginBegin(); + + // -------------------------------------------------------------------- + + /** + * finish login + */ + abstract protected function loginFinish(); + + // -------------------------------------------------------------------- + + /** + * generic logout, just erase current provider adapter stored data to let Hybrid_Auth all forget about it + */ + function logout() + { + Hybrid_Logger::info( "Enter [{$this->providerId}]::logout()" ); + + $this->clearTokens(); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * grab the user profile from the IDp api client + */ + function getUserProfile() + { + Hybrid_Logger::error( "HybridAuth do not provide users contats list for {$this->providerId} yet." ); + + throw new Exception( "Provider does not support this feature.", 8 ); + } + + // -------------------------------------------------------------------- + + /** + * load the current logged in user contacts list from the IDp api client + */ + function getUserContacts() + { + Hybrid_Logger::error( "HybridAuth do not provide users contats list for {$this->providerId} yet." ); + + throw new Exception( "Provider does not support this feature.", 8 ); + } + + // -------------------------------------------------------------------- + + /** + * return the user activity stream + */ + function getUserActivity( $stream ) + { + Hybrid_Logger::error( "HybridAuth do not provide user's activity stream for {$this->providerId} yet." ); + + throw new Exception( "Provider does not support this feature.", 8 ); + } + + // -------------------------------------------------------------------- + + /** + * return the user activity stream + */ + function setUserStatus( $status ) + { + Hybrid_Logger::error( "HybridAuth do not provide user's activity stream for {$this->providerId} yet." ); + + throw new Exception( "Provider does not support this feature.", 8 ); + } + + // -------------------------------------------------------------------- + + /** + * return true if the user is connected to the current provider + */ + public function isUserConnected() + { + return (bool) Hybrid_Auth::storage()->get( "hauth_session.{$this->providerId}.is_logged_in" ); + } + + // -------------------------------------------------------------------- + + /** + * set user to connected + */ + public function setUserConnected() + { + Hybrid_Logger::info( "Enter [{$this->providerId}]::setUserConnected()" ); + + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.is_logged_in", 1 ); + } + + // -------------------------------------------------------------------- + + /** + * set user to unconnected + */ + public function setUserUnconnected() + { + Hybrid_Logger::info( "Enter [{$this->providerId}]::setUserUnconnected()" ); + + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.is_logged_in", 0 ); + } + + // -------------------------------------------------------------------- + + /** + * get or set a token + */ + public function token( $token, $value = NULL ) + { + if( $value === NULL ){ + return Hybrid_Auth::storage()->get( "hauth_session.{$this->providerId}.token.$token" ); + } + else{ + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.token.$token", $value ); + } + } + + // -------------------------------------------------------------------- + + /** + * delete a stored token + */ + public function deleteToken( $token ) + { + Hybrid_Auth::storage()->delete( "hauth_session.{$this->providerId}.token.$token" ); + } + + // -------------------------------------------------------------------- + + /** + * clear all existen tokens for this provider + */ + public function clearTokens() + { + Hybrid_Auth::storage()->deleteMatch( "hauth_session.{$this->providerId}." ); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth1.php b/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth1.php new file mode 100644 index 000000000..0a9aae09c --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth1.php @@ -0,0 +1,156 @@ + "OK: Success!", + 304 => "Not Modified: There was no new data to return.", + 400 => "Bad Request: The request was invalid.", + 401 => "Unauthorized.", + 403 => "Forbidden: The request is understood, but it has been refused.", + 404 => "Not Found: The URI requested is invalid or the resource requested does not exists.", + 406 => "Not Acceptable.", + 500 => "Internal Server Error: Something is broken.", + 502 => "Bad Gateway.", + 503 => "Service Unavailable." + ); + + if( ! $code && $this->api ) + $code = $this->api->http_code; + + if( isset( $http_status_codes[ $code ] ) ) + return $code . " " . $http_status_codes[ $code ]; + } + + // -------------------------------------------------------------------- + + /** + * adapter initializer + */ + function initialize() + { + // 1 - check application credentials + if ( ! $this->config["keys"]["key"] || ! $this->config["keys"]["secret"] ){ + throw new Exception( "Your application key and secret are required in order to connect to {$this->providerId}.", 4 ); + } + + // 2 - include OAuth lib and client + require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth.php"; + require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth1Client.php"; + + // 3.1 - setup access_token if any stored + if( $this->token( "access_token" ) ){ + $this->api = new OAuth1Client( + $this->config["keys"]["key"], $this->config["keys"]["secret"], + $this->token( "access_token" ), $this->token( "access_token_secret" ) + ); + } + + // 3.2 - setup request_token if any stored, in order to exchange with an access token + elseif( $this->token( "request_token" ) ){ + $this->api = new OAuth1Client( + $this->config["keys"]["key"], $this->config["keys"]["secret"], + $this->token( "request_token" ), $this->token( "request_token_secret" ) + ); + } + + // 3.3 - instanciate OAuth client with client credentials + else{ + $this->api = new OAuth1Client( $this->config["keys"]["key"], $this->config["keys"]["secret"] ); + } + } + + // -------------------------------------------------------------------- + + /** + * begin login step + */ + function loginBegin() + { + $tokens = $this->api->requestToken( $this->endpoint ); + + // request tokens as recived from provider + $this->request_tokens_raw = $tokens; + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ), 5 ); + } + + if ( ! isset( $tokens["oauth_token"] ) ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid oauth token.", 5 ); + } + + $this->token( "request_token" , $tokens["oauth_token"] ); + $this->token( "request_token_secret", $tokens["oauth_token_secret"] ); + + # redirect the user to the provider authentication url + Hybrid_Auth::redirect( $this->api->authorizeUrl( $tokens ) ); + } + + // -------------------------------------------------------------------- + + /** + * finish login step + */ + function loginFinish() + { + $oauth_token = (array_key_exists('oauth_token',$_REQUEST))?$_REQUEST['oauth_token']:""; + $oauth_verifier = (array_key_exists('oauth_verifier',$_REQUEST))?$_REQUEST['oauth_verifier']:""; + + if ( ! $oauth_token || ! $oauth_verifier ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid oauth verifier.", 5 ); + } + + // request an access token + $tokens = $this->api->accessToken( $oauth_verifier ); + + // access tokens as recived from provider + $this->access_tokens_raw = $tokens; + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ), 5 ); + } + + // we should have an access_token, or else, something has gone wrong + if ( ! isset( $tokens["oauth_token"] ) ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid access token.", 5 ); + } + + // we no more need to store requet tokens + $this->deleteToken( "request_token" ); + $this->deleteToken( "request_token_secret" ); + + // sotre access_token for later user + $this->token( "access_token" , $tokens['oauth_token'] ); + $this->token( "access_token_secret" , $tokens['oauth_token_secret'] ); + + // set user as logged in to the current provider + $this->setUserConnected(); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth2.php b/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth2.php new file mode 100644 index 000000000..806787090 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Provider_Model_OAuth2.php @@ -0,0 +1,171 @@ + "OK: Success!", + 304 => "Not Modified: There was no new data to return.", + 400 => "Bad Request: The request was invalid.", + 401 => "Unauthorized.", + 403 => "Forbidden: The request is understood, but it has been refused.", + 404 => "Not Found: The URI requested is invalid or the resource requested does not exists.", + 406 => "Not Acceptable.", + 500 => "Internal Server Error: Something is broken.", + 502 => "Bad Gateway.", + 503 => "Service Unavailable." + ); + + if( ! $code && $this->api ) + $code = $this->api->http_code; + + if( isset( $http_status_codes[ $code ] ) ) + return $code . " " . $http_status_codes[ $code ]; + } + + // -------------------------------------------------------------------- + + /** + * adapter initializer + */ + function initialize() + { + if ( ! $this->config["keys"]["id"] || ! $this->config["keys"]["secret"] ){ + throw new Exception( "Your application id and secret are required in order to connect to {$this->providerId}.", 4 ); + } + + // override requested scope + if( isset( $this->config["scope"] ) && ! empty( $this->config["scope"] ) ){ + $this->scope = $this->config["scope"]; + } + + // include OAuth2 client + require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth2Client.php"; + + // create a new OAuth2 client instance + $this->api = new OAuth2Client( $this->config["keys"]["id"], $this->config["keys"]["secret"], $this->endpoint ); + + // If we have an access token, set it + if( $this->token( "access_token" ) ){ + $this->api->access_token = $this->token( "access_token" ); + $this->api->refresh_token = $this->token( "refresh_token" ); + $this->api->access_token_expires_in = $this->token( "expires_in" ); + $this->api->access_token_expires_at = $this->token( "expires_at" ); + } + } + + // -------------------------------------------------------------------- + + /** + * begin login step + */ + function loginBegin() + { + // redirect the user to the provider authentication url + Hybrid_Auth::redirect( $this->api->authorizeUrl( array( "scope" => $this->scope ) ) ); + } + + // -------------------------------------------------------------------- + + /** + * finish login step + */ + function loginFinish() + { + $error = (array_key_exists('error',$_REQUEST))?$_REQUEST['error']:""; + + // check for errors + if ( $error ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an error: $error", 5 ); + } + + // try to authenicate user + $code = (array_key_exists('code',$_REQUEST))?$_REQUEST['code']:""; + + try{ + $this->api->authenticate( $code ); + } + catch( Exception $e ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 ); + } + + // check if authenticated + if ( ! $this->api->access_token ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid access token.", 5 ); + } + + // store tokens + $this->token( "access_token" , $this->api->access_token ); + $this->token( "refresh_token", $this->api->refresh_token ); + $this->token( "expires_in" , $this->api->access_token_expires_in ); + $this->token( "expires_at" , $this->api->access_token_expires_at ); + + // set user connected locally + $this->setUserConnected(); + } + + function refreshToken() + { + // have an access token? + if( $this->api->access_token ){ + + // have to refresh? + if( $this->api->refresh_token && $this->api->access_token_expires_at ){ + + // expired? + if( $this->api->access_token_expires_at <= time() ){ + $response = $this->api->refreshToken( array( "refresh_token" => $this->api->refresh_token ) ); + + if( ! isset( $response->access_token ) || ! $response->access_token ){ + // set the user as disconnected at this point and throw an exception + $this->setUserUnconnected(); + + throw new Exception( "The Authorization Service has return an invalid response while requesting a new access token. " . (string) $response->error ); + } + + // set new access_token + $this->api->access_token = $response->access_token; + + if( isset( $response->refresh_token ) ) + $this->api->refresh_token = $response->refresh_token; + + if( isset( $response->expires_in ) ){ + $this->api->access_token_expires_in = $response->expires_in; + + // even given by some idp, we should calculate this + $this->api->access_token_expires_at = time() + $response->expires_in; + } + } + } + + // re store tokens + $this->token( "access_token" , $this->api->access_token ); + $this->token( "refresh_token", $this->api->refresh_token ); + $this->token( "expires_in" , $this->api->access_token_expires_in ); + $this->token( "expires_at" , $this->api->access_token_expires_at ); + } + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Provider_Model_OpenID.php b/e107_handlers/hybridauth/Hybrid/Provider_Model_OpenID.php new file mode 100644 index 000000000..b01fce9ef --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Provider_Model_OpenID.php @@ -0,0 +1,169 @@ +public $openidIdentifier = ""; + * + * Hybrid_Provider_Model_OpenID use LightOpenID lib which can be found on + * Hybrid/thirdparty/OpenID/LightOpenID.php + */ +class Hybrid_Provider_Model_OpenID extends Hybrid_Provider_Model +{ + /* Openid provider identifier */ + public $openidIdentifier = ""; + + // -------------------------------------------------------------------- + + /** + * adapter initializer + */ + function initialize() + { + if( isset( $this->params["openid_identifier"] ) ){ + $this->openidIdentifier = $this->params["openid_identifier"]; + } + + // include LightOpenID lib + require_once Hybrid_Auth::$config["path_libraries"] . "OpenID/LightOpenID.php"; + + $this->api = new LightOpenID( parse_url( Hybrid_Auth::$config["base_url"], PHP_URL_HOST) ); + } + + // -------------------------------------------------------------------- + + /** + * begin login step + */ + function loginBegin() + { + if( empty( $this->openidIdentifier ) ){ + throw new Exception( "OpenID adapter require the identity provider identifier 'openid_identifier' as an extra parameter.", 4 ); + } + + $this->api->identity = $this->openidIdentifier; + $this->api->returnUrl = $this->endpoint; + $this->api->required = ARRAY( + 'namePerson/first' , + 'namePerson/last' , + 'namePerson/friendly' , + 'namePerson' , + + 'contact/email' , + + 'birthDate' , + 'birthDate/birthDay' , + 'birthDate/birthMonth' , + 'birthDate/birthYear' , + + 'person/gender' , + 'pref/language' , + + 'contact/postalCode/home', + 'contact/city/home' , + 'contact/country/home' , + + 'media/image/default' , + ); + + # redirect the user to the provider authentication url + Hybrid_Auth::redirect( $this->api->authUrl() ); + } + + // -------------------------------------------------------------------- + + /** + * finish login step + */ + function loginFinish() + { + # if user don't garant acess of their data to your site, halt with an Exception + if( $this->api->mode == 'cancel'){ + throw new Exception( "Authentification failed! User has canceled authentication!", 5 ); + } + + # if something goes wrong + if( ! $this->api->validate() ){ + throw new Exception( "Authentification failed. Invalid request recived!", 5 ); + } + + # fetch recived user data + $response = $this->api->getAttributes(); + + # sotre the user profile + $this->user->profile->identifier = $this->api->identity; + + $this->user->profile->firstName = (array_key_exists("namePerson/first",$response))?$response["namePerson/first"]:""; + $this->user->profile->lastName = (array_key_exists("namePerson/last",$response))?$response["namePerson/last"]:""; + $this->user->profile->displayName = (array_key_exists("namePerson",$response))?$response["namePerson"]:""; + $this->user->profile->email = (array_key_exists("contact/email",$response))?$response["contact/email"]:""; + $this->user->profile->language = (array_key_exists("pref/language",$response))?$response["pref/language"]:""; + $this->user->profile->country = (array_key_exists("contact/country/home",$response))?$response["contact/country/home"]:""; + $this->user->profile->zip = (array_key_exists("contact/postalCode/home",$response))?$response["contact/postalCode/home"]:""; + $this->user->profile->gender = (array_key_exists("person/gender",$response))?$response["person/gender"]:""; + $this->user->profile->photoURL = (array_key_exists("media/image/default",$response))?$response["media/image/default"]:""; + + $this->user->profile->birthDay = (array_key_exists("birthDate/birthDay",$response))?$response["birthDate/birthDay"]:""; + $this->user->profile->birthMonth = (array_key_exists("birthDate/birthMonth",$response))?$response["birthDate/birthMonth"]:""; + $this->user->profile->birthYear = (array_key_exists("birthDate/birthDate",$response))?$response["birthDate/birthDate"]:""; + + if( ! $this->user->profile->displayName ) { + $this->user->profile->displayName = trim( $this->user->profile->lastName . " " . $this->user->profile->firstName ); + } + + if( isset( $response['namePerson/friendly'] ) && ! empty( $response['namePerson/friendly'] ) && ! $this->user->profile->displayName ) { + $this->user->profile->displayName = (array_key_exists("namePerson/friendly",$response))?$response["namePerson/friendly"]:"" ; + } + + if( isset( $response['birthDate'] ) && ! empty( $response['birthDate'] ) && ! $this->user->profile->birthDay ) { + list( $birthday_year, $birthday_month, $birthday_day ) = (array_key_exists('birthDate',$response))?$response['birthDate']:""; + + $this->user->profile->birthDay = (int) $birthday_day; + $this->user->profile->birthMonth = (int) $birthday_month; + $this->user->profile->birthYear = (int) $birthday_year; + } + + if( ! $this->user->profile->displayName ){ + $this->user->profile->displayName = trim( $this->user->profile->firstName . " " . $this->user->profile->lastName ); + } + + if( $this->user->profile->gender == "f" ){ + $this->user->profile->gender = "female"; + } + + if( $this->user->profile->gender == "m" ){ + $this->user->profile->gender = "male"; + } + + // set user as logged in + $this->setUserConnected(); + + // with openid providers we get the user profile only once, so store it + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.user", $this->user ); + } + + // -------------------------------------------------------------------- + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + // try to get the user profile from stored data + $this->user = Hybrid_Auth::storage()->get( "hauth_session.{$this->providerId}.user" ) ; + + // if not found + if ( ! is_object( $this->user ) ){ + throw new Exception( "User profile request failed! User is not connected to {$this->providerId} or his session has expired.", 6 ); + } + + return $this->user->profile; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/AOL.php b/e107_handlers/hybridauth/Hybrid/Providers/AOL.php new file mode 100644 index 000000000..996d1ca04 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/AOL.php @@ -0,0 +1,16 @@ +config["keys"]["id"] || ! $this->config["keys"]["secret"] ){ + throw new Exception( "Your application id and secret are required in order to connect to {$this->providerId}.", 4 ); + } + + // override requested scope + if( isset( $this->config["scope"] ) && ! empty( $this->config["scope"] ) ){ + $this->scope = $this->config["scope"]; + } + + // override requested display + if( isset( $this->config["display"] ) && ! empty( $this->config["display"] ) ){ + $this->display = $this->config["display"]; + } + + if ( ! class_exists('FacebookApiException') ) { + require_once Hybrid_Auth::$config["path_libraries"] . "Facebook/base_facebook.php"; + require_once Hybrid_Auth::$config["path_libraries"] . "Facebook/facebook.php"; + } + + $this->api = new Facebook( ARRAY( 'appId' => $this->config["keys"]["id"], 'secret' => $this->config["keys"]["secret"] ) ); + + $this->api->getUser(); + } + + /** + * begin login step + * + * simply call Facebook::require_login(). + */ + function loginBegin() + { + // get the login url + $url = $this->api->getLoginUrl( array( 'scope' => $this->scope, 'display' => $this->display, 'redirect_uri' => $this->endpoint ) ); + + // redirect to facebook + Hybrid_Auth::redirect( $url ); + } + + /** + * finish login step + */ + function loginFinish() + { + // in case we get error_reason=user_denied&error=access_denied + if ( isset( $_REQUEST['error'] ) && $_REQUEST['error'] == "access_denied" ){ + throw new Exception( "Authentification failed! The user denied your request.", 5 ); + } + + // try to get the UID of the connected user from fb, should be > 0 + if ( ! $this->api->getUser() ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalide user id.", 5 ); + } + + // set user as logged in + $this->setUserConnected(); + + // try to detect the access token for facebook + if( isset( $_SESSION["fb_" . $this->api->getAppId() . "_access_token" ] ) ){ + $this->token( "access_token", $_SESSION["fb_" . $this->api->getAppId() . "_access_token" ] ); + } + } + + /** + * logout + */ + function logout() + { + $this->api->destroySession(); + + parent::logout(); + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + // request user profile from fb api + try{ + $data = $this->api->api('/me'); + } + catch( FacebookApiException $e ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 ); + } + + // if the provider identifier is not recived, we assume the auth has failed + if ( ! isset( $data["id"] ) ){ + throw new Exception( "User profile request failed! {$this->providerId} api returned an invalid response.", 6 ); + } + + # store the user profile. + $this->user->profile->identifier = (array_key_exists('id',$data))?$data['id']:""; + $this->user->profile->displayName = (array_key_exists('name',$data))?$data['name']:""; + $this->user->profile->firstName = (array_key_exists('first_name',$data))?$data['first_name']:""; + $this->user->profile->lastName = (array_key_exists('last_name',$data))?$data['last_name']:""; + $this->user->profile->photoURL = "https://graph.facebook.com/" . $this->user->profile->identifier . "/picture?type=square"; + $this->user->profile->profileURL = (array_key_exists('link',$data))?$data['link']:""; + $this->user->profile->webSiteURL = (array_key_exists('website',$data))?$data['website']:""; + $this->user->profile->gender = (array_key_exists('gender',$data))?$data['gender']:""; + $this->user->profile->description = (array_key_exists('bio',$data))?$data['bio']:""; + $this->user->profile->email = (array_key_exists('email',$data))?$data['email']:""; + $this->user->profile->emailVerified = (array_key_exists('email',$data))?$data['email']:""; + $this->user->profile->region = (array_key_exists("hometown",$data)&&array_key_exists("name",$data['hometown']))?$data['hometown']["name"]:""; + + if( array_key_exists('birthday',$data) ) { + list($birthday_month, $birthday_day, $birthday_year) = explode( "/", $data['birthday'] ); + + $this->user->profile->birthDay = (int) $birthday_day; + $this->user->profile->birthMonth = (int) $birthday_month; + $this->user->profile->birthYear = (int) $birthday_year; + } + + return $this->user->profile; + } + + /** + * load the user contacts + */ + function getUserContacts() + { + try{ + $response = $this->api->api('/me/friends'); + } + catch( FacebookApiException $e ){ + throw new Exception( "User contacts request failed! {$this->providerId} returned an error: $e" ); + } + + if( ! $response || ! count( $response["data"] ) ){ + return ARRAY(); + } + + $contacts = ARRAY(); + + foreach( $response["data"] as $item ){ + $uc = new Hybrid_User_Contact(); + + $uc->identifier = (array_key_exists("id",$item))?$item["id"]:""; + $uc->displayName = (array_key_exists("name",$item))?$item["name"]:""; + $uc->profileURL = "https://www.facebook.com/profile.php?id=" . $uc->identifier; + $uc->photoURL = "https://graph.facebook.com/" . $uc->identifier . "/picture?type=square"; + + $contacts[] = $uc; + } + + return $contacts; + } + + /** + * update user status + */ + function setUserStatus( $status ) + { + $parameters = array(); + + if( is_array( $status ) ){ + $parameters = $status; + } + else{ + $parameters["message"] = $status; + } + + try{ + $response = $this->api->api( "/me/feed", "post", $parameters ); + } + catch( FacebookApiException $e ){ + throw new Exception( "Update user status failed! {$this->providerId} returned an error: $e" ); + } + } + + /** + * load the user latest activity + * - timeline : all the stream + * - me : the user activity only + */ + function getUserActivity( $stream ) + { + try{ + if( $stream == "me" ){ + $response = $this->api->api( '/me/feed' ); + } + else{ + $response = $this->api->api('/me/home'); + } + } + catch( FacebookApiException $e ){ + throw new Exception( "User activity stream request failed! {$this->providerId} returned an error: $e" ); + } + + if( ! $response || ! count( $response['data'] ) ){ + return ARRAY(); + } + + $activities = ARRAY(); + + foreach( $response['data'] as $item ){ + if( $stream == "me" && $item["from"]["id"] != $this->api->getUser() ){ + continue; + } + + $ua = new Hybrid_User_Activity(); + + $ua->id = (array_key_exists("id",$item))?$item["id"]:""; + $ua->date = (array_key_exists("created_time",$item))?strtotime($item["created_time"]):""; + + if( $item["type"] == "video" ){ + $ua->text = (array_key_exists("link",$item))?$item["link"]:""; + } + + if( $item["type"] == "link" ){ + $ua->text = (array_key_exists("link",$item))?$item["link"]:""; + } + + if( empty( $ua->text ) && isset( $item["story"] ) ){ + $ua->text = (array_key_exists("link",$item))?$item["link"]:""; + } + + if( empty( $ua->text ) && isset( $item["message"] ) ){ + $ua->text = (array_key_exists("message",$item))?$item["message"]:""; + } + + if( ! empty( $ua->text ) ){ + $ua->user->identifier = (array_key_exists("id",$item["from"]))?$item["from"]["id"]:""; + $ua->user->displayName = (array_key_exists("name",$item["from"]))?$item["from"]["name"]:""; + $ua->user->profileURL = "https://www.facebook.com/profile.php?id=" . $ua->user->identifier; + $ua->user->photoURL = "https://graph.facebook.com/" . $ua->user->identifier . "/picture?type=square"; + + $activities[] = $ua; + } + } + + return $activities; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/Foursquare.php b/e107_handlers/hybridauth/Hybrid/Providers/Foursquare.php new file mode 100644 index 000000000..8df26142f --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/Foursquare.php @@ -0,0 +1,56 @@ +api->api_base_url = "https://api.foursquare.com/v2/"; + $this->api->authorize_url = "https://foursquare.com/oauth2/authenticate"; + $this->api->token_url = "https://foursquare.com/oauth2/access_token"; + + $this->api->sign_token_name = "oauth_token"; + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + $data = $this->api->api( "users/self" ); + + if ( ! isset( $data->response->user->id ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide response.", 6 ); + } + + $data = $data->response->user; + + $this->user->profile->identifier = $data->id; + $this->user->profile->firstName = $data->firstName; + $this->user->profile->lastName = $data->lastName; + $this->user->profile->displayName = trim( $this->user->profile->firstName . " " . $this->user->profile->lastName ); + $this->user->profile->photoURL = $data->photo; + $this->user->profile->profileURL = $data->canonicalUrl; + $this->user->profile->gender = $data->gender; + $this->user->profile->city = $data->homeCity; + $this->user->profile->email = $data->contact->email; + $this->user->profile->emailVerified = $data->contact->email; + + return $this->user->profile; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/Google.php b/e107_handlers/hybridauth/Hybrid/Providers/Google.php new file mode 100644 index 000000000..424f8f7c4 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/Google.php @@ -0,0 +1,105 @@ +api->authorize_url = "https://accounts.google.com/o/oauth2/auth"; + $this->api->token_url = "https://accounts.google.com/o/oauth2/token"; + $this->api->token_info_url = "https://www.googleapis.com/oauth2/v1/tokeninfo"; + } + + /** + * begin login step + */ + function loginBegin() + { + Hybrid_Auth::redirect( $this->api->authorizeUrl( array( "scope" => $this->scope, "access_type" => "offline" ) ) ); + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + // refresh tokens if needed + $this->refreshToken(); + + // ask google api for user infos + $response = $this->api->api( "https://www.googleapis.com/oauth2/v1/userinfo" ); + + if ( ! isset( $response->id ) || isset( $response->error ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide response.", 6 ); + } + + $this->user->profile->identifier = (property_exists($response,'id'))?$response->id:""; + $this->user->profile->firstName = (property_exists($response,'given_name'))?$response->given_name:""; + $this->user->profile->lastName = (property_exists($response,'family_name'))?$response->family_name:""; + $this->user->profile->displayName = (property_exists($response,'name'))?$response->name:""; + $this->user->profile->photoURL = (property_exists($response,'picture'))?$response->picture:""; + $this->user->profile->profileURL = "https://profiles.google.com/" . $this->user->profile->identifier; + $this->user->profile->gender = (property_exists($response,'gender'))?$response->gender:""; + $this->user->profile->email = (property_exists($response,'email'))?$response->email:""; + $this->user->profile->emailVerified = (property_exists($response,'email'))?$response->email:""; + $this->user->profile->language = (property_exists($response,'locale'))?$response->locale:""; + + if( property_exists($response,'birthday') ){ + list($birthday_year, $birthday_month, $birthday_day) = explode( '-', $response->birthday ); + + $this->user->profile->birthDay = (int) $birthday_day; + $this->user->profile->birthMonth = (int) $birthday_month; + $this->user->profile->birthYear = (int) $birthday_year; + } + + return $this->user->profile; + } + + /** + * load the user (Gmail) contacts + * ..toComplete + */ + function getUserContacts() + { + // refresh tokens if needed + $this->refreshToken(); + + $response = $this->api->api( "https://www.google.com/m8/feeds/contacts/default/full?alt=json" ); + + if( ! $response ){ + return ARRAY(); + } + + $contacts = ARRAY(); + + foreach( $response->feed->entry as $idx => $entry ){ + $uc = new Hybrid_User_Contact(); + + $uc->email = isset($entry->{'gd$email'}[0]->address) ? (string) $entry->{'gd$email'}[0]->address : ''; + $uc->displayName = isset($entry->title->{'$t'}) ? (string) $entry->title->{'$t'} : ''; + $uc->identifier = $uc->email; + + $contacts[] = $uc; + } + + return $contacts; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/GoogleOpenID.php b/e107_handlers/hybridauth/Hybrid/Providers/GoogleOpenID.php new file mode 100644 index 000000000..f0b3e5627 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/GoogleOpenID.php @@ -0,0 +1,40 @@ + array ( + * "enabled" => true, + * "wrapper" => array( "path" => "Providers/GoogleOpenID.php", "class" => "Hybrid_Providers_Google" ) + * ) + */ +class Hybrid_Providers_Google extends Hybrid_Provider_Model_OpenID +{ + var $openidIdentifier = "https://www.google.com/accounts/o8/id"; + + /** + * finish login step + */ + function loginFinish() + { + parent::loginFinish(); + + $this->user->profile->emailVerified = $this->user->profile->email; + + // restore the user profile + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.user", $this->user ); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/LinkedIn.php b/e107_handlers/hybridauth/Hybrid/Providers/LinkedIn.php new file mode 100644 index 000000000..982770ff2 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/LinkedIn.php @@ -0,0 +1,244 @@ +config["keys"]["key"] || ! $this->config["keys"]["secret"] ){ + throw new Exception( "Your application key and secret are required in order to connect to {$this->providerId}.", 4 ); + } + + require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth.php"; + require_once Hybrid_Auth::$config["path_libraries"] . "LinkedIn/LinkedIn.php"; + + $this->api = new LinkedIn( array( 'appKey' => $this->config["keys"]["key"], 'appSecret' => $this->config["keys"]["secret"], 'callbackUrl' => $this->endpoint ) ); + + if( $this->token( "access_token_linkedin" ) ){ + $this->api->setTokenAccess( $this->token( "access_token_linkedin" ) ); + } + } + + /** + * begin login step + */ + function loginBegin() + { + // send a request for a LinkedIn access token + $response = $this->api->retrieveTokenRequest(); + + if( isset( $response['success'] ) && $response['success'] === TRUE ){ + $this->token( "oauth_token", $response['linkedin']['oauth_token'] ); + $this->token( "oauth_token_secret", $response['linkedin']['oauth_token_secret'] ); + + # redirect user to LinkedIn authorisation web page + Hybrid_Auth::redirect( LINKEDIN::_URL_AUTH . $response['linkedin']['oauth_token'] ); + } + else{ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid Token.", 5 ); + } + } + + /** + * finish login step + */ + function loginFinish() + { + $oauth_token = $_REQUEST['oauth_token']; + $oauth_verifier = $_REQUEST['oauth_verifier']; + + if ( ! $oauth_verifier ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid Token.", 5 ); + } + + $response = $this->api->retrieveTokenAccess( $oauth_token, $this->token( "oauth_token_secret" ), $oauth_verifier ); + + if( isset( $response['success'] ) && $response['success'] === TRUE ){ + $this->deleteToken( "oauth_token" ); + $this->deleteToken( "oauth_token_secret" ); + + $this->token( "access_token_linkedin", $response['linkedin'] ); + $this->token( "access_token" , $response['linkedin']['oauth_token'] ); + $this->token( "access_token_secret" , $response['linkedin']['oauth_token_secret'] ); + + // set user as logged in + $this->setUserConnected(); + } + else{ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid Token.", 5 ); + } + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + try{ + // http://developer.linkedin.com/docs/DOC-1061 + $response = $this->api->profile('~:(id,first-name,last-name,public-profile-url,picture-url,date-of-birth,phone-numbers,summary)'); + } + catch( LinkedInException $e ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 ); + } + + if( isset( $response['success'] ) && $response['success'] === TRUE ){ + $data = @ new SimpleXMLElement( $response['linkedin'] ); + + if ( ! is_object( $data ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide xml data.", 6 ); + } + + $this->user->profile->identifier = (string) $data->{'id'}; + $this->user->profile->firstName = (string) $data->{'first-name'}; + $this->user->profile->lastName = (string) $data->{'last-name'}; + $this->user->profile->displayName = trim( $this->user->profile->firstName . " " . $this->user->profile->lastName ); + + $this->user->profile->photoURL = (string) $data->{'picture-url'}; + $this->user->profile->profileURL = (string) $data->{'public-profile-url'}; + $this->user->profile->description = (string) $data->{'summary'}; + + $this->user->profile->phone = (string) $data->{'phone-numbers'}->{'phone-number'}->{'phone-number'}; + + if( $data->{'date-of-birth'} ) { + $this->user->profile->birthDay = (string) $data->{'date-of-birth'}->day; + $this->user->profile->birthMonth = (string) $data->{'date-of-birth'}->month; + $this->user->profile->birthYear = (string) $data->{'date-of-birth'}->year; + } + + return $this->user->profile; + } + else{ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalid response.", 6 ); + } + } + + /** + * load the user contacts + */ + function getUserContacts() + { + try{ + $response = $this->api->profile('~/connections:(id,first-name,last-name,picture-url,public-profile-url,summary)'); + } + catch( LinkedInException $e ){ + throw new Exception( "User contacts request failed! {$this->providerId} returned an error: $e" ); + } + + if( ! $response || ! $response['success'] ){ + return ARRAY(); + } + + $connections = new SimpleXMLElement( $response['linkedin'] ); + + $contacts = ARRAY(); + + foreach( $connections->person as $connection ) { + $uc = new Hybrid_User_Contact(); + + $uc->identifier = (string) $connection->id; + $uc->displayName = (string) $connection->{'last-name'} . " " . $connection->{'first-name'}; + $uc->profileURL = (string) $connection->{'public-profile-url'}; + $uc->photoURL = (string) $connection->{'picture-url'}; + $uc->description = (string) $connection->{'summary'}; + + $contacts[] = $uc; + } + + return $contacts; + } + + /** + * update user status + */ + function setUserStatus( $status ) + { + $parameters = array(); + $private = true; // share with your connections only + + if( is_array( $status ) ){ + if( isset( $status[0] ) && ! empty( $status[0] ) ) $parameters["title"] = $status[0]; // post title + if( isset( $status[1] ) && ! empty( $status[1] ) ) $parameters["comment"] = $status[1]; // post comment + if( isset( $status[2] ) && ! empty( $status[2] ) ) $parameters["submitted-url"] = $status[2]; // post url + if( isset( $status[3] ) && ! empty( $status[3] ) ) $parameters["submitted-image-url"] = $status[3]; // post picture url + if( isset( $status[4] ) && ! empty( $status[4] ) ) $private = $status[4]; // true or false + } + else{ + $parameters["comment"] = $status; + } + + try{ + $response = $this->api->share( 'new', $parameters, $private ); + } + catch( LinkedInException $e ){ + throw new Exception( "Update user status update failed! {$this->providerId} returned an error: $e" ); + } + + if ( ! $response || ! $response['success'] ) + { + throw new Exception( "Update user status update failed! {$this->providerId} returned an error." ); + } + } + + /** + * load the user latest activity + * - timeline : all the stream + * - me : the user activity only + */ + function getUserActivity( $stream ) + { + try{ + if( $stream == "me" ){ + $response = $this->api->updates( '?type=SHAR&scope=self&count=25' ); + } + else{ + $response = $this->api->updates( '?type=SHAR&count=25' ); + } + } + catch( LinkedInException $e ){ + throw new Exception( "User activity stream request failed! {$this->providerId} returned an error: $e" ); + } + + if( ! $response || ! $response['success'] ){ + return ARRAY(); + } + + $updates = new SimpleXMLElement( $response['linkedin'] ); + + $activities = ARRAY(); + + foreach( $updates->update as $update ) { + $person = $update->{'update-content'}->person; + $share = $update->{'update-content'}->person->{'current-share'}; + + $ua = new Hybrid_User_Activity(); + + $ua->id = (string) $update->id; + $ua->date = (string) $update->timestamp; + $ua->text = (string) $share->{'comment'}; + + $ua->user->identifier = (string) $person->id; + $ua->user->displayName = (string) $person->{'first-name'} . ' ' . $person->{'last-name'}; + $ua->user->profileURL = (string) $person->{'site-standard-profile-request'}->url; + $ua->user->photoURL = NULL; + + $activities[] = $ua; + } + + return $activities; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/Live.php b/e107_handlers/hybridauth/Hybrid/Providers/Live.php new file mode 100644 index 000000000..ea1fd1391 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/Live.php @@ -0,0 +1,117 @@ +config["keys"]["id"] || ! $this->config["keys"]["secret"] ){ + throw new Exception( "Your application id and secret are required in order to connect to {$this->providerId}.", 4 ); + } + + // Application Specific Globals + define( 'WRAP_CLIENT_ID' , $this->config["keys"]["id"] ); + define( 'WRAP_CLIENT_SECRET', $this->config["keys"]["secret"] ); + define( 'WRAP_CALLBACK' , $this->endpoint ); + define( 'WRAP_CHANNEL_URL' , Hybrid_Auth::$config["base_url"] . "?get=windows_live_channel" ); + + // Live URLs required for making requests. + define('WRAP_CONSENT_URL' , 'https://consent.live.com/Connect.aspx'); + define('WRAP_ACCESS_URL' , 'https://consent.live.com/AccessToken.aspx'); + define('WRAP_REFRESH_URL' , 'https://consent.live.com/RefreshToken.aspx'); + + require_once Hybrid_Auth::$config["path_libraries"] . "WindowsLive/OAuthWrapHandler.php"; + + $this->api = new OAuthWrapHandler(); + } + + /** + * begin login step + */ + function loginBegin() + { + $this->api->ExpireCookies(); + + Hybrid_Auth::redirect( WRAP_CONSENT_URL . "?wrap_client_id=" . WRAP_CLIENT_ID . "&wrap_callback=" . urlencode( WRAP_CALLBACK ) . "&wrap_scope=WL_Profiles.View" ); + } + + /** + * finish login step + */ + function loginFinish() + { + $response = $this->api->ProcessRequest(); + + if ( ! isset( $response['c_uid'] ) || ! isset( $response['c_accessToken'] ) ){ + throw new Exception( "Authentification failed! {$this->providerId} returned an invalid Token.", 5 ); + } + + // set user as logged in + $this->setUserConnected(); + + # store access token + $this->token( "access_token", $response['c_accessToken'] ); + + # store the user id. + $this->token( "user_id", $response['c_uid'] ); + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + try{ + $access_token = $this->token( "access_token" ); + + $user_id = $this->token( "user_id" ); + + $info_url = 'http://apis.live.net/V4.1/cid-'. $user_id .'/Profiles/1-' . $user_id; + + $response = $this->api->GET( $info_url, false, $access_token ); + + $response = json_decode( $response ); + } + catch( Exception $e ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an error while requesting the user profile.", 6 ); + } + + if ( ! is_object( $response ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalid user data.", 6 ); + } + + $this->user->profile->identifier = $user_id; + $this->user->profile->firstName = (string) $response->FirstName; + $this->user->profile->lastName = (string) $response->LastName; + $this->user->profile->profileURL = (string) $response->UxLink; + $this->user->profile->gender = (string) $response->Gender; + $this->user->profile->email = (string) $response->Emails[0]->Address; + $this->user->profile->displayName = trim( $this->user->profile->firstName . " " . $this->user->profile->lastName ); + + if( $this->user->profile->gender == 1 ){ + $this->user->profile->gender = "female"; + } + elseif( $this->user->profile->gender == 2 ){ + $this->user->profile->gender = "male"; + } + else{ + $this->user->profile->gender = ""; + } + + return $this->user->profile; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/MySpace.php b/e107_handlers/hybridauth/Hybrid/Providers/MySpace.php new file mode 100644 index 000000000..2d5806874 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/MySpace.php @@ -0,0 +1,164 @@ +api->api_endpoint_url = "http://api.myspace.com/v1/"; + $this->api->authorize_url = "http://api.myspace.com/authorize"; + $this->api->request_token_url = "http://api.myspace.com/request_token"; + $this->api->access_token_url = "http://api.myspace.com/access_token"; + } + + /** + * get the connected uid from myspace api + */ + public function getCurrentUserId() + { + $response = $this->api->get( 'http://api.myspace.com/v1/user.json' ); + + if ( ! isset( $response->userId ) ){ + throw new Exception( "User id request failed! {$this->providerId} returned an invalide response." ); + } + + return $response->userId; + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + $userId = $this->getCurrentUserId(); + + $data = $this->api->get( 'http://api.myspace.com/v1/users/' . $userId . '/profile.json' ); + + if ( ! is_object( $data ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide response.", 6 ); + } + + $this->user->profile->identifier = $userId; + $this->user->profile->displayName = $data->basicprofile->name; + $this->user->profile->description = $data->aboutme; + $this->user->profile->gender = $data->basicprofile->gender; + $this->user->profile->photoURL = $data->basicprofile->image; + $this->user->profile->profileURL = $data->basicprofile->webUri; + $this->user->profile->age = $data->age; + $this->user->profile->country = $data->country; + $this->user->profile->region = $data->region; + $this->user->profile->city = $data->city; + $this->user->profile->zip = $data->postalcode; + + return $this->user->profile; + } + + /** + * load the user contacts + */ + function getUserContacts() + { + $userId = $this->getCurrentUserId(); + + $response = $this->api->get( "http://api.myspace.com/v1/users/" . $userId . "/friends.json" ); + + if ( ! is_object( $response ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide response.", 6 ); + } + + $contacts = ARRAY(); + + foreach( $response->Friends as $item ){ + $uc = new Hybrid_User_Contact(); + + $uc->identifier = $item->userId; + $uc->displayName = $item->name; + $uc->profileURL = $item->webUri; + $uc->photoURL = $item->image; + $uc->description = $item->status; + + $contacts[] = $uc; + } + + return $contacts; + } + + /** + * update user status + */ + function setUserStatus( $status ) + { + // crappy myspace... gonna see this asaic + $userId = $this->getCurrentUserId(); + + $parameters = array( 'status' => $status ); + + $response = $this->api->api( "http://api.myspace.com/v1/users/" . $userId . "/status", 'PUT', $parameters ); + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ) + { + throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) ); + } + } + + /** + * load the user latest activity + * - timeline : all the stream + * - me : the user activity only + */ + function getUserActivity( $stream ) + { + $userId = $this->getCurrentUserId(); + + if( $stream == "me" ){ + $response = $this->api->get( "http://api.myspace.com/v1/users/" . $userId . "/status.json" ); + } + else{ + $response = $this->api->get( "http://api.myspace.com/v1/users/" . $userId . "/friends/status.json" ); + } + + if ( ! is_object( $response ) ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an invalide response.", 6 ); + } + + $activities = ARRAY(); + + if( $stream == "me" ){ + // todo + } + else{ + foreach( $response->FriendsStatus as $item ){ + $ua = new Hybrid_User_Activity(); + + $ua->id = $item->statusId; + $ua->date = NULL; // to find out!! + $ua->text = $item->status; + + $ua->user->identifier = $item->user->userId; + $ua->user->displayName = $item->user->name; + $ua->user->profileURL = $item->user->uri; + $ua->user->photoURL = $item->user->image; + + $activities[] = $ua; + } + } + + return $activities; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/OpenID.php b/e107_handlers/hybridauth/Hybrid/Providers/OpenID.php new file mode 100644 index 000000000..42f62b749 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/OpenID.php @@ -0,0 +1,15 @@ +api->api_base_url = "https://api.twitter.com/1/"; + $this->api->authorize_url = "https://api.twitter.com/oauth/authenticate"; + $this->api->request_token_url = "https://api.twitter.com/oauth/request_token"; + $this->api->access_token_url = "https://api.twitter.com/oauth/access_token"; + + $this->api->curl_auth_header = false; + } + + /** + * load the user profile from the IDp api client + */ + function getUserProfile() + { + $response = $this->api->get( 'account/verify_credentials.json' ); + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "User profile request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ), 6 ); + } + + if ( ! is_object( $response ) || ! isset( $response->id ) ){ + throw new Exception( "User profile request failed! {$this->providerId} api returned an invalid response.", 6 ); + } + + # store the user profile. + $this->user->profile->identifier = (property_exists($response,'id'))?$response->id:""; + $this->user->profile->displayName = (property_exists($response,'screen_name'))?$response->screen_name:""; + $this->user->profile->description = (property_exists($response,'description'))?$response->description:""; + $this->user->profile->firstName = (property_exists($response,'name'))?$response->name:""; + $this->user->profile->photoURL = (property_exists($response,'profile_image_url'))?$response->profile_image_url:""; + $this->user->profile->profileURL = (property_exists($response,'screen_name'))?("http://twitter.com/".$response->screen_name):""; + $this->user->profile->webSiteURL = (property_exists($response,'url'))?$response->url:""; + $this->user->profile->region = (property_exists($response,'location'))?$response->location:""; + + return $this->user->profile; + } + + /** + * load the user contacts + */ + function getUserContacts() + { + $parameters = array( 'cursor' => '-1' ); + $response = $this->api->get( 'friends/ids.json', $parameters ); + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "User contacts request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) ); + } + + if( ! $response || ! count( $response->ids ) ){ + return ARRAY(); + } + + // 75 id per time should be okey + $contactsids = array_chunk ( $response->ids, 75 ); + + $contacts = ARRAY(); + + foreach( $contactsids as $chunk ){ + $parameters = array( 'user_id' => implode( ",", $chunk ) ); + $response = $this->api->get( 'users/lookup.json', $parameters ); + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "User contacts request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) ); + } + + if( $response && count( $response ) ){ + foreach( $response as $item ){ + $uc = new Hybrid_User_Contact(); + + $uc->identifier = (property_exists($item,'id'))?$item->id:""; + $uc->displayName = (property_exists($item,'name'))?$item->name:""; + $uc->profileURL = (property_exists($item,'screen_name'))?("http://twitter.com/".$item->screen_name):""; + $uc->photoURL = (property_exists($item,'profile_image_url'))?$item->profile_image_url:""; + $uc->description = (property_exists($item,'description'))?$item->description:""; + + $contacts[] = $uc; + } + } + } + + return $contacts; + } + + /** + * update user status + */ + function setUserStatus( $status ) + { + $parameters = array( 'status' => $status ); + $response = $this->api->post( 'statuses/update.json', $parameters ); + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) ); + } + } + + /** + * load the user latest activity + * - timeline : all the stream + * - me : the user activity only + * + * by default return the timeline + */ + function getUserActivity( $stream ) + { + if( $stream == "me" ){ + $response = $this->api->get( 'statuses/user_timeline.json' ); + } + else{ + $response = $this->api->get( 'statuses/home_timeline.json' ); + } + + // check the last HTTP status code returned + if ( $this->api->http_code != 200 ){ + throw new Exception( "User activity stream request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) ); + } + + if( ! $response ){ + return ARRAY(); + } + + $activities = ARRAY(); + + foreach( $response as $item ){ + $ua = new Hybrid_User_Activity(); + + $ua->id = (property_exists($item,'id'))?$item->id:""; + $ua->date = (property_exists($item,'created_at'))?strtotime($item->created_at):""; + $ua->text = (property_exists($item,'text'))?$item->text:""; + + $ua->user->identifier = (property_exists($item->user,'id'))?$item->user->id:""; + $ua->user->displayName = (property_exists($item->user,'name'))?$item->user->name:""; + $ua->user->profileURL = (property_exists($item->user,'screen_name'))?("http://twitter.com/".$item->user->screen_name):""; + $ua->user->photoURL = (property_exists($item->user,'profile_image_url'))?$item->user->profile_image_url:""; + + $activities[] = $ua; + } + + return $activities; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/Providers/Yahoo.php b/e107_handlers/hybridauth/Hybrid/Providers/Yahoo.php new file mode 100644 index 000000000..d0b0b8d4a --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Providers/Yahoo.php @@ -0,0 +1,32 @@ +user->profile->emailVerified = $this->user->profile->email; + + // restore the user profile + Hybrid_Auth::storage()->set( "hauth_session.{$this->providerId}.user", $this->user ); + } +} + diff --git a/e107_handlers/hybridauth/Hybrid/Storage.php b/e107_handlers/hybridauth/Hybrid/Storage.php new file mode 100644 index 000000000..cb25a57e9 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/Storage.php @@ -0,0 +1,74 @@ + $v ){ + if( strstr( $k, $key ) ){ + unset( $_SESSION["HA::STORE"][ $k ] ); + } + } + } + } + + function getSessionData() + { + return serialize( $_SESSION["HA::STORE"] ); + } + + function restoreSessionData( $sessiondata = NULL ) + { + $_SESSION["HA::STORE"] = unserialize( $sessiondata ); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/User.php b/e107_handlers/hybridauth/Hybrid/User.php new file mode 100644 index 000000000..6187d6d4e --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/User.php @@ -0,0 +1,31 @@ +timestamp = time(); + + $this->profile = new Hybrid_User_Profile(); + } +} diff --git a/e107_handlers/hybridauth/Hybrid/User_Activity.php b/e107_handlers/hybridauth/Hybrid/User_Activity.php new file mode 100644 index 000000000..fb163ee2c --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/User_Activity.php @@ -0,0 +1,39 @@ +user = new stdClass(); + + // typically, we should have a few information about the user who created the event from social apis + $this->user->identifier = NULL; + $this->user->displayName = NULL; + $this->user->profileURL = NULL; + $this->user->photoURL = NULL; + } +} diff --git a/e107_handlers/hybridauth/Hybrid/User_Contact.php b/e107_handlers/hybridauth/Hybrid/User_Contact.php new file mode 100644 index 000000000..0f9d2ed92 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/User_Contact.php @@ -0,0 +1,37 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/e107_handlers/hybridauth/Hybrid/resources/config.php.tpl b/e107_handlers/hybridauth/Hybrid/resources/config.php.tpl new file mode 100644 index 000000000..1905af216 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/config.php.tpl @@ -0,0 +1,78 @@ + "#GLOBAL_HYBRID_AUTH_URL_BASE#", + + "providers" => array ( + // openid providers + "OpenID" => array ( + "enabled" => #OPENID_ADAPTER_STATUS# + ), + + "Yahoo" => array ( + "enabled" => #YAHOO_ADAPTER_STATUS# + ), + + "AOL" => array ( + "enabled" => #AOL_ADAPTER_STATUS# + ), + + "Google" => array ( + "enabled" => #GOOGLE_ADAPTER_STATUS#, + "keys" => array ( "id" => "#GOOGLE_APPLICATION_APP_ID#", "secret" => "#GOOGLE_APPLICATION_SECRET#" ), + "scope" => "" + ), + + "Facebook" => array ( + "enabled" => #FACEBOOK_ADAPTER_STATUS#, + "keys" => array ( "id" => "#FACEBOOK_APPLICATION_APP_ID#", "secret" => "#FACEBOOK_APPLICATION_SECRET#" ), + + // 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" => "", + + // 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" => "" + ), + + "Twitter" => array ( + "enabled" => #TWITTER_ADAPTER_STATUS#, + "keys" => array ( "key" => "#TWITTER_APPLICATION_KEY#", "secret" => "#TWITTER_APPLICATION_SECRET#" ) + ), + + // windows live + "Live" => array ( + "enabled" => #LIVE_ADAPTER_STATUS#, + "keys" => array ( "id" => "#LIVE_APPLICATION_APP_ID#", "secret" => "#LIVE_APPLICATION_SECRET#" ) + ), + + "MySpace" => array ( + "enabled" => #MYSPACE_ADAPTER_STATUS#, + "keys" => array ( "key" => "#MYSPACE_APPLICATION_KEY#", "secret" => "#MYSPACE_APPLICATION_SECRET#" ) + ), + + "LinkedIn" => array ( + "enabled" => #LINKEDIN_ADAPTER_STATUS#, + "keys" => array ( "key" => "#LINKEDIN_APPLICATION_KEY#", "secret" => "#LINKEDIN_APPLICATION_SECRET#" ) + ), + + "Foursquare" => array ( + "enabled" => #FOURSQUARE_ADAPTER_STATUS#, + "keys" => array ( "id" => "#FOURSQUARE_APPLICATION_APP_ID#", "secret" => "#FOURSQUARE_APPLICATION_SECRET#" ) + ), + ), + + // if you want to enable logging, set 'debug_mode' to true then provide a writable file by the web server on "debug_file" + "debug_mode" => false, + + "debug_file" => "", + ); diff --git a/e107_handlers/hybridauth/Hybrid/resources/index.html b/e107_handlers/hybridauth/Hybrid/resources/index.html new file mode 100644 index 000000000..065d2da5e --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/e107_handlers/hybridauth/Hybrid/resources/openid_policy.html b/e107_handlers/hybridauth/Hybrid/resources/openid_policy.html new file mode 100644 index 000000000..bf5c52c35 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/openid_policy.html @@ -0,0 +1,10 @@ + + + OpenID Policy + + + + + \ No newline at end of file diff --git a/e107_handlers/hybridauth/Hybrid/resources/openid_realm.html b/e107_handlers/hybridauth/Hybrid/resources/openid_realm.html new file mode 100644 index 000000000..e26a5a17d --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/openid_realm.html @@ -0,0 +1,13 @@ + + + HybridAuth Endpoint + + + + +

HybridAuth

+ Open Source Social Sign On PHP Library. +
+ hybridauth.sourceforge.net/ + + diff --git a/e107_handlers/hybridauth/Hybrid/resources/openid_xrds.xml b/e107_handlers/hybridauth/Hybrid/resources/openid_xrds.xml new file mode 100644 index 000000000..9d50170fa --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/openid_xrds.xml @@ -0,0 +1,12 @@ + + + + + http://specs.openid.net/auth/2.0/return_to + {RETURN_TO_URL} + + + \ No newline at end of file diff --git a/e107_handlers/hybridauth/Hybrid/resources/windows_live_channel.html b/e107_handlers/hybridauth/Hybrid/resources/windows_live_channel.html new file mode 100644 index 000000000..dadcf1911 --- /dev/null +++ b/e107_handlers/hybridauth/Hybrid/resources/windows_live_channel.html @@ -0,0 +1,37 @@ + + + +