Beispiel #1
0
 /**
  * Retrieves the currently logged-in user from the session.
  * If the user was not originally retrieved from the database,
  * inserts a new one.
  * Thus, this can also be used to turn visitors into registered
  * users.
  * @method authenticate
  * @static
  * @param {string} $provider Currently only supports the value "facebook".
  * @param {integer} [$appId=null] The id of the app within the specified provider.
  *  Used for storing app-specific session information.
  * @param {&boolean} [$authenticated=null] If authentication fails, puts false here.
  *  Otherwise, puts one of the following:
  *  'registered' if user just registered,
  *  'adopted' if a futureUser was just adopted,
  *  'connected' if a logged-in user just connected the provider account for the first time,
  *  'authorized' if a logged-in user was connected to provider but just authorized this app for the first time
  *  or true otherwise.
  * @param {boolean} [$import_emailAddress=false] If true, and the user's email address is not set yet,
  *  imports the email address from the provider if it is available,
  *  and sets it as the user's email address without requiring verification.
  * @return {Users_User}
  */
 static function authenticate($provider, $appId = null, &$authenticated = null, $import_emailAddress = false)
 {
     if (!isset($appId)) {
         $app = Q_Config::expect('Q', 'app');
         $appId = Q_Config::expect('Users', 'facebookApps', $app, 'appId');
     }
     $authenticated = null;
     $during = 'authenticate';
     $return = null;
     /**
      * @event Users/authenticate {before}
      * @param {string} provider
      * @param {string} appId
      * @return {Users_User}
      */
     $return = Q::event('Users/authenticate', compact('provider', 'appId'), 'before');
     if (isset($return)) {
         return $return;
     }
     if (!isset($provider) or $provider != 'facebook') {
         throw new Q_Exception_WrongType(array('field' => 'provider', 'type' => '"facebook"'));
     }
     if (!isset($appId)) {
         throw new Q_Exception_WrongType(array('field' => 'appId', 'type' => 'a valid facebook app id'));
     }
     Q_Session::start();
     // First, see if we've already logged in somehow
     if ($user = self::loggedInUser()) {
         // Get logged in user from session
         $user_was_logged_in = true;
         $retrieved = true;
     } else {
         // Get an existing user or create a new one
         $user_was_logged_in = false;
         $retrieved = false;
         $user = new Users_User();
     }
     $authenticated = false;
     $emailAddress = null;
     // Try authenticating the user with the specified provider
     switch ($provider) {
         case 'facebook':
             $facebook = Users::facebook($appId);
             if (!$facebook or !$facebook->getUser()) {
                 // no facebook authentication is happening
                 return $user_was_logged_in ? $user : false;
             }
             if (empty($user->emailAddress)) {
                 $queries = array(array('method' => 'GET', 'relative_url' => '/me/permissions'), array('method' => 'GET', 'relative_url' => '/me'));
                 $batchResponse = $facebook->api('?batch=' . Q::json_encode($queries), 'POST');
                 $permissions = json_decode($batchResponse[0]['body'], true);
                 if (Q::ifset($permissions, 'data', 0, 'email', false)) {
                     $userData = json_decode($batchResponse[1]['body'], true);
                     if (!empty($userData['email'])) {
                         $emailAddress = $userData['email'];
                     }
                 }
             }
             $authenticated = true;
             $fb_uid = $facebook->getUser();
             $re_save_user = false;
             if ($retrieved) {
                 if (empty($user->fb_uid)) {
                     // this is a logged-in user who was never authenticated with facebook.
                     // First, let's find any other user who has authenticated with this facebook uid,
                     // and set their fb_uid to NULL.
                     $authenticated = 'connected';
                     $ui = Users::identify('facebook', $fb_uid);
                     if ($ui) {
                         Users_User::update()->set(array('fb_uid' => 0))->where(array('id' => $ui->userId))->execute();
                         $ui->remove();
                     }
                     // Now, let's associate their account with this facebook uid.
                     $user->fb_uid = $fb_uid;
                     $user->save();
                     // Save the identifier in the quick lookup table
                     $ui = new Users_Identify();
                     $ui->identifier = "facebook:{$fb_uid}";
                     $ui->state = 'verified';
                     $ui->userId = $user->id;
                     $ui->save(true);
                 } else {
                     if ($user->fb_uid !== $fb_uid) {
                         // The logged-in user was authenticated with facebook already,
                         // and associated with a different facebook id.
                         // Most likely, a completely different person has logged into facebook
                         // at this computer. So rather than changing the associated facebook uid
                         // for the logged-in user, simply log out and essentially run this function
                         // from the beginning again.
                         Users::logout();
                         $user_was_logged_in = false;
                         $user = new Users_User();
                         $retrieved = false;
                     }
                 }
             }
             if (!$retrieved) {
                 $ui = Users::identify('facebook', $fb_uid, null);
                 if ($ui) {
                     $user = new Users_User();
                     $user->id = $ui->userId;
                     $exists = $user->retrieve();
                     if (!$exists) {
                         throw new Q_Exception("Users_Identify for fb_uid {$fb_uid} exists but not user with id {$ui->userId}");
                     }
                     $retrieved = true;
                     if ($ui->state === 'future') {
                         $authenticated = 'adopted';
                         $user->fb_uid = $fb_uid;
                         $user->signedUpWith = 'facebook';
                         // should have been "none" before this
                         /**
                          * @event Users/adoptFutureUser {before}
                          * @param {Users_User} user
                          * @param {string} during
                          * @return {Users_User}
                          */
                         $ret = Q::event('Users/adoptFutureUser', compact('user', 'during'), 'before');
                         if ($ret) {
                             $user = $ret;
                         }
                         $user->save();
                         $ui->state = 'verified';
                         $ui->save();
                         /**
                          * @event Users/adoptFutureUser {after}
                          * @param {Users_User} user
                          * @param {array} links
                          * @param {string} during
                          * @return {Users_User}
                          */
                         Q::event('Users/adoptFutureUser', compact('user', 'links', 'during'), 'after');
                     } else {
                         // If we are here, that simply means that we already verified the
                         // $fb_uid => $userId mapping for some existing user who signed up
                         // and has been using the system. So there is nothing more to do besides
                         // setting this user as the logged-in user below.
                     }
                 } else {
                     // user is logged out and no user corresponding to $fb_uid yet
                     $authenticated = 'registered';
                     // If we can import email address from facebook,
                     // try retrieving it quietly
                     if ($import_emailAddress) {
                         // DELAY: The following call may take some time,
                         // but once the user is saved, it will not happen again
                         // for this facebook user, because it would be identified by fb_uid
                         $userData = $facebook->api('/me');
                         if (!empty($userData['email'])) {
                             $emailAddress = $userData['email'];
                         }
                         Users::$cache['facebookUserData'] = $userData;
                     }
                     if (!empty($emailAddress)) {
                         $ui = Users::identify('email', $emailAddress, 'verified');
                         if ($ui) {
                             // existing user  identified from verified email address
                             // load it into $user
                             $user = new Users_User();
                             $user->id = $ui->userId;
                             $user->retrieve(null, null, true)->caching()->resume();
                         }
                     }
                     $user->fb_uid = $fb_uid;
                     /**
                      * @event Users/insertUser {before}
                      * @param {Users_User} user
                      * @param {string} during
                      * @return {Users_User}
                      */
                     $ret = Q::event('Users/insertUser', compact('user', 'during'), 'before');
                     if (isset($ret)) {
                         $user = $ret;
                     }
                     if (!$user->wasRetrieved()) {
                         // Register a new user basically and give them an empty username for now
                         $user->username = "";
                         $user->icon = 'default';
                         $user->signedUpWith = 'facebook';
                         $user->save();
                         // Save the identifier in the quick lookup table
                         $ui = new Users_Identify();
                         $ui->identifier = "facebook:{$fb_uid}";
                         $ui->state = 'verified';
                         $ui->userId = $user->id;
                         $ui->save(true);
                         // Download and save facebook icon for the user
                         $sizes = Q_Config::expect('Users', 'icon', 'sizes');
                         sort($sizes);
                         $icon = array();
                         foreach ($sizes as $size) {
                             $parts = explode('x', $size);
                             $width = Q::ifset($parts, 0, '');
                             $height = Q::ifset($parts, 1, '');
                             $width = $width ? $width : $height;
                             $height = $height ? $height : $width;
                             $icon["{$size}.png"] = "https://graph.facebook.com/{$fb_uid}/picture?width={$width}&height={$height}";
                         }
                         if (!Q_Config::get('Users', 'register', 'icon', 'leaveDefault', false)) {
                             self::importIcon($user, $icon);
                             $user->save();
                         }
                     }
                 }
             }
             Users::$cache['user'] = $user;
             Users::$cache['authenticated'] = $authenticated;
             // Checking if user email is not set, and we have facebook "email" permission,
             // try retrieving it from facebook and verifying the email for the user
             if (!empty($emailAddress)) {
                 $emailSubject = Q_Config::get('Users', 'transactional', 'authenticated', 'subject', false);
                 $emailView = Q_Config::get('Users', 'transactional', 'authenticated', 'body', false);
                 if ($emailSubject !== false and $emailView) {
                     $user->addEmail($emailAddress, $emailSubject, $emailView);
                 }
                 // After this, we automatically verify their email,
                 // even if they never clicked the confirmation link,
                 // because we trust the authentication provider.
                 $user->setEmailAddress($emailAddress, true);
             }
             break;
         default:
             // not sure how to log this user in
             return $user_was_logged_in ? $user : false;
     }
     if (!$user_was_logged_in) {
         self::setLoggedInUser($user);
     }
     if ($retrieved) {
         /**
          * @event Users/updateUser {after}
          * @param {Users_User} user
          * @param {string} during
          */
         Q::event('Users/updateUser', compact('user', 'during'), 'after');
     } else {
         /**
          * @event Users/insertUser {after}
          * @param {string} during
          * @param {Users_User} 'user'
          */
         Q::event('Users/insertUser', compact('user', 'during'), 'after');
     }
     // Now make sure our master session contains the
     // session info for the provider app.
     if ($provider == 'facebook') {
         $access_token = $facebook->getAccessToken();
         if (isset($_SESSION['Users']['appUsers']['facebook_' . $appId])) {
             // Facebook app user exists. Do we need to update it? (Probably not!)
             $pk = $_SESSION['Users']['appUsers']['facebook_' . $appId];
             $app_user = Users_AppUser::select('*')->where($pk)->fetchDbRow();
             if (empty($app_user)) {
                 // somehow this app_user disappeared from the database
                 throw new Q_Exception_MissingRow(array('table' => 'AppUser', 'criteria' => http_build_query($pk, null, ' & ')));
             }
             if (empty($app_user->state) or $app_user->state !== 'added') {
                 $app_user->state = 'added';
             }
             if (!isset($app_user->access_token) or $access_token != $app_user->access_token) {
                 /**
                  * @event Users/authenticate/updateAppUser {before}
                  * @param {Users_User} user
                  */
                 Q::event('Users/authenticate/updateAppUser', compact('user', 'app_user'), 'before');
                 $app_user->access_token = $access_token;
                 $app_user->save();
                 // update access_token in app_user
                 /**
                  * @event Users/authenticate/updateAppUser {after}
                  * @param {Users_User} user
                  */
                 Q::event('Users/authenticate/updateAppUser', compact('user', 'app_user'), 'after');
             }
         } else {
             // We have to put the session info in
             $app_user = new Users_AppUser();
             $app_user->userId = $user->id;
             $app_user->provider = 'facebook';
             $app_user->appId = $appId;
             if ($app_user->retrieve()) {
                 // App user exists in database. Do we need to update it?
                 if (!isset($app_user->access_token) or $app_user->access_token != $access_token) {
                     /**
                      * @event Users/authenticate/updateAppUser {before}
                      * @param {Users_User} user
                      */
                     Q::event('Users/authenticate/updateAppUser', compact('user', 'app_user'), 'before');
                     $app_user->access_token = $access_token;
                     $app_user->save();
                     // update access_token in app_user
                     /**
                      * @event Users/authenticate/updateAppUser {after}
                      * @param {Users_User} user
                      */
                     Q::event('Users/authenticate/updateAppUser', compact('user', 'app_user'), 'after');
                 }
             } else {
                 if (empty($app_user->state) or $app_user->state !== 'added') {
                     $app_user->state = 'added';
                 }
                 $app_user->access_token = $access_token;
                 $app_user->provider_uid = $user->fb_uid;
                 /**
                  * @event Users/insertAppUser {before}
                  * @param {Users_User} user
                  * @param {string} 'during'
                  */
                 Q::event('Users/insertAppUser', compact('user', 'during'), 'before');
                 // The following may update an existing app_user row
                 // in the rare event that someone tries to tie the same
                 // provider account to two different accounts.
                 // A provider account can only reference one account, so the
                 // old connection will be dropped, and the new connection saved.
                 $app_user->save(true);
                 /**
                  * @event Users/authenticate/insertAppUser {after}
                  * @param {Users_User} user
                  */
                 Q::event('Users/authenticate/insertAppUser', compact('user'), 'after');
                 $authenticated = 'authorized';
             }
         }
         $_SESSION['Users']['appUsers']['facebook_' . $appId] = $app_user->getPkValue();
     }
     Users::$cache['authenticated'] = $authenticated;
     /**
      * @event Users/authenticate {after}
      * @param {string} provider
      * @param {string} appId
      */
     Q::event('Users/authenticate', compact('provider', 'appId'), 'after');
     // At this point, $user is set.
     return $user;
 }