]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
implement rememberme functionality
authorEvan Prodromou <evan@controlezvous.ca>
Tue, 24 Jun 2008 02:52:34 +0000 (22:52 -0400)
committerEvan Prodromou <evan@controlezvous.ca>
Tue, 24 Jun 2008 02:52:34 +0000 (22:52 -0400)
Added a checkbox on login or register to remember the current user. If
the login is successful, this sets a cookie with a random code (saved
in the DB). If they come back, and they aren't logged in "normally",
we check to see if they have a rememberme cookie. If so, we log them
in.

However, they can't change settings -- cookie theft is too prevalent.
So we mark a session as having a "real" (password or OpenID) login, or
not. In settings pages, we check to see if the login is "real", and if
not, we redirect to the login page.

darcs-hash:20080624025234-34904-ad20001bf35bf41fcb63a0c357fd929aacc55fdb.gz

actions/finishopenidlogin.php
actions/login.php
actions/logout.php
actions/register.php
lib/common.php
lib/openid.php
lib/settingsaction.php
lib/util.php

index 02d8fff9814a88fd0006fec53bdf844ad666d439..09102e8442806b7c18b1ac1d443cc5d48e9c53d8 100644 (file)
@@ -130,6 +130,7 @@ class FinishopenidloginAction extends Action {
                                oid_set_last($display);
                                oid_update_user($user, $sreg);
                                common_set_user($user->nickname);
+                               common_real_login(true);
                                $this->go_home($user->nickname);
                        } else {
                                $this->save_values($display, $canonical, $sreg);
@@ -253,6 +254,7 @@ class FinishopenidloginAction extends Action {
                
                oid_set_last($display);
                common_set_user($user->nickname);
+               common_real_login(true);
                common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)));
        }
        
@@ -287,6 +289,7 @@ class FinishopenidloginAction extends Action {
                oid_update_user($user, $sreg);
                oid_set_last($display);
                common_set_user($user->nickname);
+               common_real_login(true);
                $this->go_home($user->nickname);
        }
        
index 5acb157f4f0315ebdb16c8d7276b111e7e524243..fe2ae5c1c459a3b2b72108c3f9812b55698cb54d 100644 (file)
@@ -43,6 +43,10 @@ class LoginAction extends Action {
                                common_server_error(_t('Error setting user.'));
                                return;
                        }
+                       common_real_login(true);
+                       if ($this->boolean('rememberme')) {
+                               common_rememberme();
+                       }
                        # success!
                        $url = common_get_returnto();
                        if ($url) {
@@ -66,6 +70,9 @@ class LoginAction extends Action {
                                                                                   'action' => common_local_url('login')));
                common_input('nickname', _t('Nickname'));
                common_password('password', _t('Password'));
+               common_checkbox('rememberme', _t('Remember me'),
+                               _t('Automatically login in the future; ' . 
+                                  'not for shared computers!'));
                common_submit('submit', _t('Login'));
                common_element_end('form');
                common_show_footer();
index c4d0bd4e7aac92587e5b90aa6c0376b61a3aafcd..5f3a607a78499adb1a39f913907364d9610bb5bb 100644 (file)
@@ -27,8 +27,9 @@ class LogoutAction extends Action {
                if (!common_logged_in()) {
                        common_user_error(_t('Not logged in.'));
                } else {
-                       oid_clear_last();
                        common_set_user(NULL);
+                       common_real_login(false); # not logged in
+                       common_forget_me(); # don't log back in!
                        common_redirect(common_local_url('public'));
                }
        }
index 95e684c1b57260ce33957af9210ae4da8c74931e..f346e4b9c805ba9d89c7b9751ee240caac988cd2 100644 (file)
@@ -69,6 +69,10 @@ class RegisterAction extends Action {
                                common_server_error(_t('Error setting user.'));
                                return;
                        }
+                       common_real_login(true);
+                       if ($this->boolean('rememberme')) {
+                               common_rememberme();
+                       }
                        common_redirect(common_local_url('profilesettings'));
                } else {
                        $this->show_form(_t('Invalid username or password.'));
@@ -170,6 +174,9 @@ class RegisterAction extends Action {
                                                _t('Same as password above'));
                common_input('email', _t('Email'), NULL,
                                         _t('Used only for updates, announcements, and password recovery'));
+               common_checkbox('rememberme', _t('Remember me'),
+                               _t('Automatically login in the future; ' . 
+                                  'not for shared computers!'));
                common_element_start('p');
                common_element('input', array('type' => 'checkbox',
                                                                          'id' => 'license',
index d2e7e2ed50784401f38f9bdb61ed7351fb890930..18e4ed54afd066c1bd4f8b24c229cd488d9e3da1 100644 (file)
@@ -90,3 +90,4 @@ require_once(INSTALLDIR.'/classes/Remote_profile.php');
 require_once(INSTALLDIR.'/classes/Subscription.php');
 require_once(INSTALLDIR.'/classes/User.php');
 require_once(INSTALLDIR.'/classes/Confirm_address.php');
+require_once(INSTALLDIR.'/classes/Remember_me.php');
index 41102457a32857576e4c9279aae825d02b72fff1..11934138fd463d49735897cff3c3ac31c8c6318a 100644 (file)
@@ -30,7 +30,7 @@ require_once('Auth/OpenID/MySQLStore.php');
 
 define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60));
 define('OPENID_COOKIE_KEY', 'lastusedopenid');
-          
+
 function oid_store() {
     static $store = NULL;
        if (!$store) {
@@ -53,21 +53,9 @@ function oid_clear_last() {
 }
 
 function oid_set_last($openid_url) {
-       
-       $path = common_config('site', 'path');
-       $server = common_config('site', 'server');
-
-       if ($path && ($path != '/')) {
-               $cookiepath = '/' . $path . '/';
-       } else {
-               $cookiepath = '/';
-       }
-       
-       setcookie(OPENID_COOKIE_KEY,
-                         $openid_url,
-                         time() + OPENID_COOKIE_EXPIRY,
-                         $cookiepath,
-                         $server);
+       common_set_cookie(OPENID_COOKIE_KEY,
+                                $openid_url,
+                                time() + OPENID_COOKIE_EXPIRY);
 }
 
 function oid_get_last() {
@@ -80,7 +68,7 @@ function oid_get_last() {
 }
 
 function oid_link_user($id, $canonical, $display) {
-       
+
        $oid = new User_openid();
        $oid->user_id = $id;
        $oid->canonical = $canonical;
@@ -92,7 +80,7 @@ function oid_link_user($id, $canonical, $display) {
                common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__);
                return false;
        }
-       
+
        return true;
 }
 
@@ -113,13 +101,13 @@ function oid_check_immediate($openid_url, $backto=NULL) {
                $backto = common_local_url($action, $args);
        }
        common_debug('going back to "' . $backto . '"', __FILE__);
-       
+
        common_ensure_session();
-       
+
        $_SESSION['openid_immediate_backto'] = $backto;
        common_debug('passed-in variable is "' . $backto . '"', __FILE__);
        common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__);
-       
+
        oid_authenticate($openid_url,
                                         'finishimmediate',
                                         true);
@@ -128,23 +116,23 @@ function oid_check_immediate($openid_url, $backto=NULL) {
 function oid_authenticate($openid_url, $returnto, $immediate=false) {
 
        $consumer = oid_consumer();
-       
+
        if (!$consumer) {
                common_server_error(_t('Cannot instantiate OpenID consumer object.'));
                return false;
        }
-       
+
        common_ensure_session();
-       
+
        $auth_request = $consumer->begin($openid_url);
-       
+
        // Handle failure status return values.
        if (!$auth_request) {
                return _t('Not a valid OpenID.');
        } else if (Auth_OpenID::isFailure($auth_request)) {
                return _t('OpenID failure: ') . $auth_request->message;
        }
-       
+
        $sreg_request = Auth_OpenID_SRegRequest::build(// Required
                                                                                                   array(),
                                                                                                   // Optional
@@ -155,14 +143,14 @@ function oid_authenticate($openid_url, $returnto, $immediate=false) {
                                                                                                                 'timezone',
                                                                                                                 'postcode',
                                                                                                                 'country'));
-       
+
        if ($sreg_request) {
                $auth_request->addExtension($sreg_request);
        }
-       
+
        $trust_root = common_local_url('public');
        $process_url = common_local_url($returnto);
-       
+
        if ($auth_request->shouldSendRedirect()) {
                $redirect_url = $auth_request->redirectURL($trust_root,
                                                                                                   $process_url,
@@ -178,12 +166,12 @@ function oid_authenticate($openid_url, $returnto, $immediate=false) {
                $form_id = 'openid_message';
                $form_html = $auth_request->formMarkup($trust_root, $process_url,
                                                                                           $immediate, array('id' => $form_id));
-               
+
                # XXX: This is cheap, but things choke if we don't escape ampersands
                # in the HTML attributes
-               
+
                $form_html = preg_replace('/&/', '&amp;', $form_html);
-               
+
                // Display an error if the form markup couldn't be generated;
                // otherwise, render the HTML.
                if (Auth_OpenID::isFailure($form_html)) {
@@ -212,15 +200,15 @@ function _oid_print_instructions() {
 # update a user from sreg parameters
 
 function oid_update_user(&$user, &$sreg) {
-               
+
        $profile = $user->getProfile();
-       
+
        $orig_profile = clone($profile);
-       
+
        if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) {
                $profile->fullname = $sreg['fullname'];
        }
-       
+
        if ($sreg['country']) {
                if ($sreg['postcode']) {
                        # XXX: use postcode to get city and region
@@ -230,25 +218,25 @@ function oid_update_user(&$user, &$sreg) {
                        $profile->location = $sreg['country'];
                }
        }
-       
+
        # XXX save language if it's passed
        # XXX save timezone if it's passed
-       
+
        if (!$profile->update($orig_profile)) {
                common_server_error(_t('Error saving the profile.'));
                return false;
        }
-       
+
        $orig_user = clone($user);
-       
+
        if ($sreg['email'] && Validate::email($sreg['email'], true)) {
                $user->email = $sreg['email'];
        }
-       
+
        if (!$user->update($orig_user)) {
                common_server_error(_t('Error saving the user.'));
                return false;
        }
-       
+
        return true;
 }
index fad6abaf1293a53d780b49de5149cad459500d82..2a80c0e31b3c9669c366fa095e24df60f2c30422 100644 (file)
@@ -26,6 +26,12 @@ class SettingsAction extends Action {
         if (!common_logged_in()) {
             common_user_error(_t('Not logged in.'));
             return;
+        } else if (!common_is_real_login()) {
+               # Cookie theft means that automatic logins can't
+               # change important settings or see private info, and
+               # _all_ our settings are important
+            common_set_returnto($this->self_url());
+            common_redirect(common_local_url('login'));
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
             $this->handle_post();
         } else {
@@ -52,8 +58,8 @@ class SettingsAction extends Action {
     function settings_menu() {
         # action => array('prompt', 'title')
         static $menu =
-        array('profilesettings' => 
-              array('Profile', 
+        array('profilesettings' =>
+              array('Profile',
                        'Change your profile settings'),
             'avatar' =>
             array('Avatar',
@@ -62,12 +68,12 @@ class SettingsAction extends Action {
             array('Password',
                   'Change your password'),
             'openidsettings' =>
-            array('OpenID', 
+            array('OpenID',
                   'Add or remove OpenIDs'),
             'imsettings' =>
             array('IM',
                   'Updates by instant messenger (IM)'));
-                       
+
         $action = $this->trimmed('action');
         common_element_start('ul', array('id' => 'nav_views'));
         foreach ($menu as $menuaction => $menudesc) {
index 01457734573d778d769fe86e0e6ff3f865de56a4..2fa8e9f95504073390086d9195b8040b4070efb3 100644 (file)
@@ -325,7 +325,7 @@ function common_checkbox($id, $label, $value='true',
        common_element_start('p');
        $attrs = array('name' => $id,
                                   'type' => 'checkbox',
-                                  'id' => $id, 
+                                  'id' => $id,
                                   'value' => $value);
        if ($value) {
                $attrs['value'] = htmlspecialchars($value);
@@ -432,16 +432,106 @@ function common_set_user($nickname) {
        return false;
 }
 
+function commmon_set_cookie($key, $value, $expiration=0) {
+       $path = common_config('site', 'path');
+       $server = common_config('site', 'server');
+
+       if ($path && ($path != '/')) {
+               $cookiepath = '/' . $path . '/';
+       } else {
+               $cookiepath = '/';
+       }
+       return setcookie($key,
+                        $value,
+                                $expiration,
+                                        $cookiepath,
+                                    $server);
+}
+
+define('REMEMBERME', 'rememberme');
+define('REMEMBERME_EXPIRY', round(30 * 24 * 60 * 60));
+
+function common_rememberme() {
+       $user = common_current_user();
+       if (!$user) {
+               return false;
+       }
+       $rm = new Remember_me();
+       $rm->code = common_good_rand(16);
+       $rm->user = $user->id();
+       if (!$rm->insert) {
+               common_log_db_error($rm, 'INSERT', __FILE__);
+               return false;
+       }
+       common_set_cookie(REMEMBERME,
+                                         $rm->user . ':' . $rm->code,
+                                         time() + REMEMBERME_EXPIRY);
+}
+
+function common_remembered_user() {
+       $user = NULL;
+       # Try to remember
+       $packed = $_COOKIE[REMEMBERME];
+       if ($packed) {
+               list($id, $code) = explode(':', $packed);
+               if ($id && $code) {
+                       $rm = Remember_me::staticGet($code);
+                       if ($rm && $rm->id == $id) {
+                               $user = User::staticGet($rm->id);
+                               if ($user) {
+                                       # successful!
+                                       $result = $rm->delete();
+                                       if (!$result) {
+                                               common_log_db_error($rm, 'DELETE', __FILE__);
+                                               $user = NULL;
+                                       } else {
+                                               common_set_user($user);
+                                               common_real_login(false);
+                                               common_rememberme();
+                                       }
+                               }
+                       }
+               }
+       }
+       return $user;
+}
+
+# must be called with a valid user!
+
+function common_forgetme() {
+       common_set_cookie(REMEMBERME, '', 0);
+}
+
 # who is the current user?
 function common_current_user() {
-       common_ensure_session();
-       $id = $_SESSION['userid'];
-       if ($id) {
-               $user = User::staticGet($id);
+
+       if (common_have_session()) {
+               $id = $_SESSION['userid'];
+               if ($id) {
+                       # note: this should cache
+                       $user = User::staticGet($id);
+                       return $user;
+               }
        }
+
+       # that didn't work; try to remember
+       $user = common_remembered_user();
        return $user;
 }
 
+# Logins that are 'remembered' aren't 'real' -- they're subject to
+# cookie-stealing. So, we don't let them do certain things. New reg,
+# OpenID, and password logins _are_ real.
+
+function common_real_login($real=true) {
+       common_ensure_session();
+       $_SESSION['real_login'] = $real;
+}
+
+function common_is_real_login() {
+       return common_logged_in() && $_SESSION['real_login'];
+}
+
 # get canonical version of nickname for comparison
 function common_canonical_nickname($nickname) {
        # XXX: UTF-8 canonicalization (like combining chars)
@@ -944,7 +1034,7 @@ function common_user_uri(&$user) {
 }
 
 function common_notice_uri(&$notice) {
-       return common_local_url('shownotice', 
+       return common_local_url('shownotice',
                array('notice' => $notice->id));
 }