diff options
author | Dries Buytaert <dries@buytaert.net> | 2005-04-24 11:43:08 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2005-04-24 11:43:08 +0000 |
commit | e2b256dcf63e01de58a47f7b8358ac918eaf3b91 (patch) | |
tree | 47a95f58a9a1de49c94fc9bf9589452861048cfb | |
parent | 45c5344f15ad1dd812eaa0fda677a72ab077d755 (diff) | |
download | brdo-e2b256dcf63e01de58a47f7b8358ac918eaf3b91.tar.gz brdo-e2b256dcf63e01de58a47f7b8358ac918eaf3b91.tar.bz2 |
- Patch #18719 by Jose: reworked the 'request new password' functionality.
-rw-r--r-- | CHANGELOG.txt | 2 | ||||
-rw-r--r-- | database/database.mysql | 1 | ||||
-rw-r--r-- | database/database.pgsql | 1 | ||||
-rw-r--r-- | database/updates.inc | 14 | ||||
-rw-r--r-- | modules/contact.module | 8 | ||||
-rw-r--r-- | modules/contact/contact.module | 8 | ||||
-rw-r--r-- | modules/user.module | 91 | ||||
-rw-r--r-- | modules/user/user.module | 91 |
8 files changed, 158 insertions, 58 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fcc39bccd..a0e7575a3 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,8 @@ Drupal x.x.x, xxxx-xx-xx (Development version) ------------------------ - added free tagging support (folksonomies). +- added a site-wide contact form. +- reworked the 'request new password' functionality. - profiles: * added a block to display author information along with posts. * added support for private profile fields. diff --git a/database/database.mysql b/database/database.mysql index 2e2e8ad55..d0279f4c5 100644 --- a/database/database.mysql +++ b/database/database.mysql @@ -705,6 +705,7 @@ CREATE TABLE users ( signature varchar(255) NOT NULL default '', created int(11) NOT NULL default '0', changed int(11) NOT NULL default '0', + login int(11) NOT NULL default '0', status tinyint(4) NOT NULL default '0', timezone varchar(8) default NULL, language varchar(12) NOT NULL default '', diff --git a/database/database.pgsql b/database/database.pgsql index 1fa3d1634..b635751b4 100644 --- a/database/database.pgsql +++ b/database/database.pgsql @@ -702,6 +702,7 @@ CREATE TABLE users ( signature varchar(255) NOT NULL default '', created integer NOT NULL default '0', changed integer NOT NULL default '0', + login integer NOT NULL default '0', status smallint NOT NULL default '0', timezone varchar(8) default NULL, language varchar(12) NOT NULL default '', diff --git a/database/updates.inc b/database/updates.inc index aef73c8c2..6874bd72c 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -109,7 +109,7 @@ $sql_updates = array( "2005-04-10" => "update_130", "2005-04-11" => "update_131", "2005-04-14" => "update_132", - "2005-04-20" => "update_133" + "2005-04-24" => "update_133" ); function update_32() { @@ -2407,14 +2407,24 @@ function update_132() { } function update_133() { - $ret[] = update_sql("CREATE TABLE contact ( + $ret[] = update_sql("CREATE TABLE {contact} ( subject varchar(255) NOT NULL default '', recipients longtext NOT NULL default '', reply longtext NOT NULL default '' )"); + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql("ALTER TABLE {users} ADD login int(11) NOT NULL default '0'"); + } + elseif ($GLOBALS['db_type'] == 'pgsql') { + $ret[] = update_sql("ALTER TABLE {users} ADD login integer"); + $ret[] = update_sql("ALTER TABLE {users} ALTER COLUMN login SET NOT NULL"); + $ret[] = update_sql("ALTER TABLE {users} ALTER COLUMN login SET DEFAULT '0'"); + } + return $ret; } + function update_sql($sql) { $edit = $_POST["edit"]; $result = db_query($sql); diff --git a/modules/contact.module b/modules/contact.module index 71d88cdb8..7f9646bb4 100644 --- a/modules/contact.module +++ b/modules/contact.module @@ -224,7 +224,7 @@ function contact_mail_page() { if (isset($_POST['edit'])) { $edit = $_POST['edit']; } - + if ($edit) { // Validate the fields: if (!$edit['name']) { @@ -236,11 +236,11 @@ function contact_mail_page() { if (!$edit['message']) { form_set_error('message', t('You must enter a message.')); } - + if (!form_get_errors()) { // Prepare the sender: $from = $edit['mail']; - + // Compose the body: $message[] = t("%name sent a message using the contact form at %form:", array('%name' => $edit['name'], '%form' => url($_GET['q'], NULL, NULL, TRUE))); $message[] = $edit['message']; @@ -282,7 +282,7 @@ function contact_mail_page() { $edit['name'] = $user->name; $edit['mail'] = $user->mail; } - + $result = db_query('SELECT subject FROM contact ORDER BY subject'); while ($subject = db_fetch_object($result)) { $subjects[$subject->subject] = $subject->subject; diff --git a/modules/contact/contact.module b/modules/contact/contact.module index 71d88cdb8..7f9646bb4 100644 --- a/modules/contact/contact.module +++ b/modules/contact/contact.module @@ -224,7 +224,7 @@ function contact_mail_page() { if (isset($_POST['edit'])) { $edit = $_POST['edit']; } - + if ($edit) { // Validate the fields: if (!$edit['name']) { @@ -236,11 +236,11 @@ function contact_mail_page() { if (!$edit['message']) { form_set_error('message', t('You must enter a message.')); } - + if (!form_get_errors()) { // Prepare the sender: $from = $edit['mail']; - + // Compose the body: $message[] = t("%name sent a message using the contact form at %form:", array('%name' => $edit['name'], '%form' => url($_GET['q'], NULL, NULL, TRUE))); $message[] = $edit['message']; @@ -282,7 +282,7 @@ function contact_mail_page() { $edit['name'] = $user->name; $edit['mail'] = $user->mail; } - + $result = db_query('SELECT subject FROM contact ORDER BY subject'); while ($subject = db_fetch_object($result)) { $subjects[$subject->subject] = $subject->subject; diff --git a/modules/user.module b/modules/user.module index a2d994816..329715990 100644 --- a/modules/user.module +++ b/modules/user.module @@ -392,7 +392,7 @@ function user_fields() { } else { // Make sure we return the default fields at least - $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'status', 'timezone', 'language', 'init', 'data'); + $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'login', 'status', 'timezone', 'language', 'init', 'data'); } } @@ -644,6 +644,8 @@ function user_menu($may_cache) { 'callback' => 'user_page', 'access' => $user->uid == 0 && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK); $items[] = array('path' => 'user/password', 'title' => t('request new password'), 'callback' => 'user_pass', 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK); + $items[] = array('path' => 'user/reset', 'title' => t('reset password'), + 'callback' => 'user_pass_reset', 'access' => $user->uid == 0, 'type' => MENU_CALLBACK); $items[] = array('path' => 'user/help', 'title' => t('help'), 'callback' => 'user_help_page', 'type' => MENU_CALLBACK); @@ -809,7 +811,7 @@ function user_login($edit = array(), $msg = '') { watchdog('user', t('Session opened for %name.', array('%name' => theme('placeholder', $user->name)))); // Update the user table timestamp noting user has logged in. - db_query("UPDATE {users} SET changed = '%d' WHERE uid = '%s'", time(), $user->uid); + db_query("UPDATE {users} SET login = '%d' WHERE uid = '%s'", time(), $user->uid); user_module_invoke('login', $edit, $user); @@ -922,31 +924,27 @@ function user_pass() { } if ($account) { $from = variable_get('site_mail', ini_get('sendmail_from')); - $pass = user_password(); - // Save new password: - user_save($account, array('pass' => $pass)); - - // Mail new password: - $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); + // Mail one time login URL and instructions. + $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%login_url' => user_pass_reset_url($account), '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); $subject = _user_mail_text('pass_subject', $variables); $body = _user_mail_text('pass_body', $variables); $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from"; $mail_success = user_mail($account->mail, $subject, $body, $headers); if ($mail_success) { - watchdog('user', t('Password mailed to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail)))); - drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.')); + watchdog('user', t('Password reset instructions mailed to %name at %email.', array('%name' => '<em>'. $account->name .'</em>', '%email' => '<em>'. $account->mail .'</em>'))); + drupal_set_message(t('Further instructions have been sent to your e-mail address.')); } else { - watchdog('user', t('Error mailing password to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR); + watchdog('user', t('Error mailing password reset instructions to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR); drupal_set_message(t('Unable to send mail. Please contact the site admin.')); } drupal_goto('user'); } else { if ($edit) { - drupal_set_message(t('You must provider either a username or e-mail address.'), 'error'); + drupal_set_message(t('You must provide either a username or e-mail address.'), 'error'); } // Display form: $output = '<p>'. t('Enter your username <strong><em>or</em></strong> your e-mail address.') .'</p>'; @@ -957,6 +955,50 @@ function user_pass() { } } +/** + * Menu callback; process one time login URL, and redirects to the user page on success. + */ +function user_pass_reset($uid, $timestamp, $hashed_pass) { + global $user; + // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds. + $timeout = 86400; + $current = time(); + // Some redundant checks for extra security ? + if ($timestamp < $current && is_numeric($uid) && $account = user_load(array('uid' => $uid, 'status' => 1)) ) { + // No time out for first time login. + if ($account->login && $current - $timestamp > $timeout) { + drupal_set_message(t('You have tried to use a one time login URL which has expired. Please request a new one using the form below.')); + drupal_goto('user/password'); + } + if ($account->uid && !$user->uid && !empty($account) && $timestamp > $account->login && $timestamp < $current && + $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) { + watchdog('user', t('One time login URL used for %name with timestamp %timestamp.', array('%name' => "<em>$account->name</em>", '%timestamp' => $timestamp))); + // Update the user table noting user has logged in. + // And this also makes this hashed password a one-time-only login. + db_query("UPDATE {users} SET login = '%d' WHERE uid = %d", time(), $account->uid); + // Now we can set the new user. + $user = $account; + // And proceed with normal login, going to user page. + user_module_invoke('login', $edit, $user); + drupal_set_message(t("You have used a one-time login, which won't be valid anymore.")); + drupal_set_message(t('Please change your password.')); + drupal_goto('user/'. $user->uid .'/edit'); + } + } + // Deny access, no more clues. + // Everything will be in the watchdog's URL for the administrator to check. + drupal_access_denied(); +} + +function user_pass_reset_url($account){ + $timestamp = time(); + return url("user/reset/$account->uid/$timestamp/".user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE); +} + +function user_pass_rehash($password, $timestamp, $login){ + return md5($timestamp . $password . $login); +} + function user_register($edit = array()) { global $user, $base_url; @@ -977,7 +1019,7 @@ function user_register($edit = array()) { $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $edit)); watchdog('user', t('New user: %name %email.', array('%name' => theme('placeholder', $edit['name']), '%email' => theme('placeholder', '<'. $edit['mail'] .'>'))), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit')); - $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); + $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE), '%login_url' => user_pass_reset_url($account)); // The first user may login immediately, and receives a customized welcome e-mail. if ($account->uid == 1) { @@ -1256,15 +1298,15 @@ function _user_mail_text($messageid, $variables = array()) { case 'welcome_subject': return t('Account details for %username at %site', $variables); case 'welcome_body': - return t("%username,\n\nThank you for registering at %site. You may now log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); + return t("%username,\n\nThank you for registering at %site. You may now log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to %edit_uri so you can change your password.\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drop.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); case 'approval_subject': return t('Account details for %username at %site (pending admin approval)', $variables); case 'approval_body': - return t("%username,\n\nThank you for registering at %site. Your application for an account is currently pending approval. Once it has been granted, you may log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); + return t("%username,\n\nThank you for registering at %site. Your application for an account is currently pending approval. Once it has been granted, you may log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drop.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); case 'pass_subject': return t('Replacement login information for %username at %site', $variables); case 'pass_body': - return t("%username,\n\nHere is your new password for %site. You may now login to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri", $variables); + return t("%username,\n\nA request to reset the password for your account has been made at %site.\n\nYou may now log in to %uri_brief clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to %edit_uri so you can change your password.", $variables); } } } @@ -1276,12 +1318,12 @@ function user_configure_settings() { $output = form_group(t('User registration settings'), $group); // User e-mail settings. - $group = form_textfield(t('Subject of welcome e-mail'), 'user_mail_welcome_subject', _user_mail_text('welcome_subject'), 70, 180, t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of welcome e-mail'), 'user_mail_welcome_body', _user_mail_text('welcome_body'), 70, 10, t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); - $group .= form_textfield(t('Subject of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_subject', _user_mail_text('approval_subject'), 70, 180, t('Customize the subject of your awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_body', _user_mail_text('approval_body'), 70, 10, t('Customize the body of the awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); - $group .= form_textfield(t('Subject of password recovery e-mail'), 'user_mail_pass_subject', _user_mail_text('pass_subject'), 70, 180, t('Customize the Subject of your forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of password recovery e-mail'), 'user_mail_pass_body', _user_mail_text('pass_body'), 70, 10, t('Customize the body of the forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); + $group = form_textfield(t('Subject of welcome e-mail'), 'user_mail_welcome_subject', _user_mail_text('welcome_subject'), 70, 180, t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %login_url.'); + $group .= form_textarea(t('Body of welcome e-mail'), 'user_mail_welcome_body', _user_mail_text('welcome_body'), 70, 10, t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri, %login_url.'); + $group .= form_textfield(t('Subject of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_subject', _user_mail_text('approval_subject'), 70, 180, t('Customize the subject of your awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %login_url.'); + $group .= form_textarea(t('Body of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_body', _user_mail_text('approval_body'), 70, 10, t('Customize the body of the awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri, %login_url.'); + $group .= form_textfield(t('Subject of password recovery e-mail'), 'user_mail_pass_subject', _user_mail_text('pass_subject'), 70, 180, t('Customize the Subject of your forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %login_url, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); + $group .= form_textarea(t('Body of password recovery e-mail'), 'user_mail_pass_body', _user_mail_text('pass_body'), 70, 10, t('Customize the body of the forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %login_url, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); $output .= form_group(t('User email settings'), $group); // Picture settings. @@ -1626,10 +1668,10 @@ function user_admin_account() { array('data' => t('Username'), 'field' => 'u.name'), array('data' => t('Status'), 'field' => 'u.status'), array('data' => t('Roles')), - array('data' => t('Last access'), 'field' => 'u.changed', 'sort' => 'desc'), + array('data' => t('Last access'), 'field' => 'u.login', 'sort' => 'desc'), t('Operations') ); - $sql = 'SELECT u.uid, u.name, u.status, u.changed FROM {users} u WHERE uid != 0'; + $sql = 'SELECT u.uid, u.name, u.status, u.login, u.changed FROM {users} u WHERE uid != 0'; $sql .= tablesort_sql($header); $result = pager_query($sql, 50); @@ -1644,6 +1686,7 @@ function user_admin_account() { } $rows[] = array($account->uid, format_name($account), $status[$account->status], implode(', ', $roles), format_date($account->changed, 'small'), l(t('edit'), "user/$account->uid/edit", array(), $destination)); + } $pager = theme('pager', NULL, 50, 0, tablesort_pager()); diff --git a/modules/user/user.module b/modules/user/user.module index a2d994816..329715990 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -392,7 +392,7 @@ function user_fields() { } else { // Make sure we return the default fields at least - $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'status', 'timezone', 'language', 'init', 'data'); + $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'login', 'status', 'timezone', 'language', 'init', 'data'); } } @@ -644,6 +644,8 @@ function user_menu($may_cache) { 'callback' => 'user_page', 'access' => $user->uid == 0 && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK); $items[] = array('path' => 'user/password', 'title' => t('request new password'), 'callback' => 'user_pass', 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK); + $items[] = array('path' => 'user/reset', 'title' => t('reset password'), + 'callback' => 'user_pass_reset', 'access' => $user->uid == 0, 'type' => MENU_CALLBACK); $items[] = array('path' => 'user/help', 'title' => t('help'), 'callback' => 'user_help_page', 'type' => MENU_CALLBACK); @@ -809,7 +811,7 @@ function user_login($edit = array(), $msg = '') { watchdog('user', t('Session opened for %name.', array('%name' => theme('placeholder', $user->name)))); // Update the user table timestamp noting user has logged in. - db_query("UPDATE {users} SET changed = '%d' WHERE uid = '%s'", time(), $user->uid); + db_query("UPDATE {users} SET login = '%d' WHERE uid = '%s'", time(), $user->uid); user_module_invoke('login', $edit, $user); @@ -922,31 +924,27 @@ function user_pass() { } if ($account) { $from = variable_get('site_mail', ini_get('sendmail_from')); - $pass = user_password(); - // Save new password: - user_save($account, array('pass' => $pass)); - - // Mail new password: - $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); + // Mail one time login URL and instructions. + $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%login_url' => user_pass_reset_url($account), '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); $subject = _user_mail_text('pass_subject', $variables); $body = _user_mail_text('pass_body', $variables); $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from"; $mail_success = user_mail($account->mail, $subject, $body, $headers); if ($mail_success) { - watchdog('user', t('Password mailed to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail)))); - drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.')); + watchdog('user', t('Password reset instructions mailed to %name at %email.', array('%name' => '<em>'. $account->name .'</em>', '%email' => '<em>'. $account->mail .'</em>'))); + drupal_set_message(t('Further instructions have been sent to your e-mail address.')); } else { - watchdog('user', t('Error mailing password to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR); + watchdog('user', t('Error mailing password reset instructions to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR); drupal_set_message(t('Unable to send mail. Please contact the site admin.')); } drupal_goto('user'); } else { if ($edit) { - drupal_set_message(t('You must provider either a username or e-mail address.'), 'error'); + drupal_set_message(t('You must provide either a username or e-mail address.'), 'error'); } // Display form: $output = '<p>'. t('Enter your username <strong><em>or</em></strong> your e-mail address.') .'</p>'; @@ -957,6 +955,50 @@ function user_pass() { } } +/** + * Menu callback; process one time login URL, and redirects to the user page on success. + */ +function user_pass_reset($uid, $timestamp, $hashed_pass) { + global $user; + // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds. + $timeout = 86400; + $current = time(); + // Some redundant checks for extra security ? + if ($timestamp < $current && is_numeric($uid) && $account = user_load(array('uid' => $uid, 'status' => 1)) ) { + // No time out for first time login. + if ($account->login && $current - $timestamp > $timeout) { + drupal_set_message(t('You have tried to use a one time login URL which has expired. Please request a new one using the form below.')); + drupal_goto('user/password'); + } + if ($account->uid && !$user->uid && !empty($account) && $timestamp > $account->login && $timestamp < $current && + $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) { + watchdog('user', t('One time login URL used for %name with timestamp %timestamp.', array('%name' => "<em>$account->name</em>", '%timestamp' => $timestamp))); + // Update the user table noting user has logged in. + // And this also makes this hashed password a one-time-only login. + db_query("UPDATE {users} SET login = '%d' WHERE uid = %d", time(), $account->uid); + // Now we can set the new user. + $user = $account; + // And proceed with normal login, going to user page. + user_module_invoke('login', $edit, $user); + drupal_set_message(t("You have used a one-time login, which won't be valid anymore.")); + drupal_set_message(t('Please change your password.')); + drupal_goto('user/'. $user->uid .'/edit'); + } + } + // Deny access, no more clues. + // Everything will be in the watchdog's URL for the administrator to check. + drupal_access_denied(); +} + +function user_pass_reset_url($account){ + $timestamp = time(); + return url("user/reset/$account->uid/$timestamp/".user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE); +} + +function user_pass_rehash($password, $timestamp, $login){ + return md5($timestamp . $password . $login); +} + function user_register($edit = array()) { global $user, $base_url; @@ -977,7 +1019,7 @@ function user_register($edit = array()) { $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $edit)); watchdog('user', t('New user: %name %email.', array('%name' => theme('placeholder', $edit['name']), '%email' => theme('placeholder', '<'. $edit['mail'] .'>'))), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit')); - $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE)); + $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE), '%login_url' => user_pass_reset_url($account)); // The first user may login immediately, and receives a customized welcome e-mail. if ($account->uid == 1) { @@ -1256,15 +1298,15 @@ function _user_mail_text($messageid, $variables = array()) { case 'welcome_subject': return t('Account details for %username at %site', $variables); case 'welcome_body': - return t("%username,\n\nThank you for registering at %site. You may now log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); + return t("%username,\n\nThank you for registering at %site. You may now log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to %edit_uri so you can change your password.\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drop.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); case 'approval_subject': return t('Account details for %username at %site (pending admin approval)', $variables); case 'approval_body': - return t("%username,\n\nThank you for registering at %site. Your application for an account is currently pending approval. Once it has been granted, you may log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); + return t("%username,\n\nThank you for registering at %site. Your application for an account is currently pending approval. Once it has been granted, you may log in to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at %edit_uri\n\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drop.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n-- %site team", $variables); case 'pass_subject': return t('Replacement login information for %username at %site', $variables); case 'pass_body': - return t("%username,\n\nHere is your new password for %site. You may now login to %login_uri using the following username and password:\n\nusername: %username\npassword: %password\n\nAfter logging in, you may wish to change your password at %edit_uri", $variables); + return t("%username,\n\nA request to reset the password for your account has been made at %site.\n\nYou may now log in to %uri_brief clicking on this link or copying and pasting it in your browser:\n\n%login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to %edit_uri so you can change your password.", $variables); } } } @@ -1276,12 +1318,12 @@ function user_configure_settings() { $output = form_group(t('User registration settings'), $group); // User e-mail settings. - $group = form_textfield(t('Subject of welcome e-mail'), 'user_mail_welcome_subject', _user_mail_text('welcome_subject'), 70, 180, t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of welcome e-mail'), 'user_mail_welcome_body', _user_mail_text('welcome_body'), 70, 10, t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); - $group .= form_textfield(t('Subject of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_subject', _user_mail_text('approval_subject'), 70, 180, t('Customize the subject of your awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_body', _user_mail_text('approval_body'), 70, 10, t('Customize the body of the awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); - $group .= form_textfield(t('Subject of password recovery e-mail'), 'user_mail_pass_subject', _user_mail_text('pass_subject'), 70, 180, t('Customize the Subject of your forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); - $group .= form_textarea(t('Body of password recovery e-mail'), 'user_mail_pass_body', _user_mail_text('pass_body'), 70, 10, t('Customize the body of the forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); + $group = form_textfield(t('Subject of welcome e-mail'), 'user_mail_welcome_subject', _user_mail_text('welcome_subject'), 70, 180, t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %login_url.'); + $group .= form_textarea(t('Body of welcome e-mail'), 'user_mail_welcome_body', _user_mail_text('welcome_body'), 70, 10, t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri, %login_url.'); + $group .= form_textfield(t('Subject of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_subject', _user_mail_text('approval_subject'), 70, 180, t('Customize the subject of your awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %login_url.'); + $group .= form_textarea(t('Body of welcome e-mail (awaiting admin approval)'), 'user_mail_approval_body', _user_mail_text('approval_body'), 70, 10, t('Customize the body of the awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri, %login_url.'); + $group .= form_textfield(t('Subject of password recovery e-mail'), 'user_mail_pass_subject', _user_mail_text('pass_subject'), 70, 180, t('Customize the Subject of your forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %login_url, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri.'); + $group .= form_textarea(t('Body of password recovery e-mail'), 'user_mail_pass_body', _user_mail_text('pass_body'), 70, 10, t('Customize the body of the forgotten password e-mail.') .' '. t('Available variables are:') .' %username, %site, %login_url, %uri, %uri_brief, %mailto, %login_uri, %edit_uri.'); $output .= form_group(t('User email settings'), $group); // Picture settings. @@ -1626,10 +1668,10 @@ function user_admin_account() { array('data' => t('Username'), 'field' => 'u.name'), array('data' => t('Status'), 'field' => 'u.status'), array('data' => t('Roles')), - array('data' => t('Last access'), 'field' => 'u.changed', 'sort' => 'desc'), + array('data' => t('Last access'), 'field' => 'u.login', 'sort' => 'desc'), t('Operations') ); - $sql = 'SELECT u.uid, u.name, u.status, u.changed FROM {users} u WHERE uid != 0'; + $sql = 'SELECT u.uid, u.name, u.status, u.login, u.changed FROM {users} u WHERE uid != 0'; $sql .= tablesort_sql($header); $result = pager_query($sql, 50); @@ -1644,6 +1686,7 @@ function user_admin_account() { } $rows[] = array($account->uid, format_name($account), $status[$account->status], implode(', ', $roles), format_date($account->changed, 'small'), l(t('edit'), "user/$account->uid/edit", array(), $destination)); + } $pager = theme('pager', NULL, 50, 0, tablesort_pager()); |