1
|
<?php
|
2
|
/**
|
3
|
* @file
|
4
|
* Alters default Drupal password recovery process by overriding default submit.
|
5
|
*/
|
6
|
|
7
|
/**
|
8
|
* Implements hook_help().
|
9
|
*/
|
10
|
function recovery_pass_help($path, $arg) {
|
11
|
$output = '';
|
12
|
switch ($path) {
|
13
|
case 'admin/help#recovery_pass':
|
14
|
$output = file_get_contents(drupal_get_path('module', 'recovery_pass') . '/README.txt');
|
15
|
}
|
16
|
return $output;
|
17
|
}
|
18
|
|
19
|
/**
|
20
|
* Implements hook_ctools_plugin_api().
|
21
|
*/
|
22
|
function recovery_pass_ctools_plugin_api($owner, $api) {
|
23
|
if ($owner == 'services' && $api == 'services') {
|
24
|
return array(
|
25
|
'version' => 3,
|
26
|
'file' => 'recovery_pass.services.inc'
|
27
|
);
|
28
|
}
|
29
|
}
|
30
|
|
31
|
/**
|
32
|
* Implements hook_menu().
|
33
|
*/
|
34
|
function recovery_pass_menu() {
|
35
|
$items = array();
|
36
|
|
37
|
// Menu item for module configurations.
|
38
|
$items['admin/config/people/recovery-pass'] = array(
|
39
|
'title' => 'Recovery Password Configuration',
|
40
|
'description' => 'Configure email message to be sent to user for password recovery.',
|
41
|
'page callback' => 'drupal_get_form',
|
42
|
'page arguments' => array('recovery_pass_config_form'),
|
43
|
'access arguments' => array('administer users'),
|
44
|
'file' => 'recovery_pass.admin.inc',
|
45
|
);
|
46
|
return $items;
|
47
|
}
|
48
|
|
49
|
/**
|
50
|
* Implements hook_form_FORM_ID_alter().
|
51
|
*/
|
52
|
function recovery_pass_form_user_pass_alter(&$form, &$form_state, $form_id) {
|
53
|
// Overrides default submit handler for user_pass form.
|
54
|
$form['#submit'] = array_diff($form['#submit'], array('user_pass_submit'));
|
55
|
array_unshift($form['#submit'], 'recovery_pass_forgot_password_submit');
|
56
|
}
|
57
|
|
58
|
/**
|
59
|
* Custom submit handler to send password in recovery mail.
|
60
|
*/
|
61
|
function recovery_pass_forgot_password_submit($form, &$form_state) {
|
62
|
global $language;
|
63
|
$user = $form_state['values']['account'];
|
64
|
|
65
|
// Generate random password.
|
66
|
$random_password = user_password();
|
67
|
// Store Old Hash Password Temporarily.
|
68
|
if (!_recovery_pass_store_old_pass($user)) {
|
69
|
watchdog('recovery_pass', 'Error saving old password for user @id', array('@id' => $user->uid), WATCHDOG_NOTICE, 'link');
|
70
|
}
|
71
|
// Save new password.
|
72
|
user_save($user, array('pass' => $random_password), 'account');
|
73
|
|
74
|
// Retrive email body and subject.
|
75
|
$message = _recovery_pass_mail_text('email_text', $language, TRUE, $user);
|
76
|
if ($message) {
|
77
|
// Replace [user_new_password] placeholder with new password.
|
78
|
$message = str_replace("[user_new_password]", $random_password, $message);
|
79
|
}
|
80
|
$subject = _recovery_pass_mail_text('email_subject', $language, TRUE, $user);
|
81
|
if (module_exists("htmlmail")) {
|
82
|
// For html mail convert new lines to br.
|
83
|
$message = nl2br($message);
|
84
|
}
|
85
|
$params = array(
|
86
|
'body' => $message,
|
87
|
'subject' => $subject,
|
88
|
);
|
89
|
$to = $user->mail;
|
90
|
$from = variable_get('site_mail');
|
91
|
if (drupal_mail('recovery_pass', 'recovery_pass', $to, language_default(), $params, $from, TRUE)) {
|
92
|
drupal_set_message(t("Further instructions have been sent to your registered Email-id."), 'status', FALSE);
|
93
|
}
|
94
|
else {
|
95
|
drupal_set_message(t("Error Sending Recovery Mail. Please contact site administrator."), 'error', FALSE);
|
96
|
}
|
97
|
$form_state['redirect'] = variable_get('recovery_pass_fpass_redirect', 'user');
|
98
|
}
|
99
|
|
100
|
/**
|
101
|
* Temporarily stores old password in custom table for error message in future.
|
102
|
*/
|
103
|
function _recovery_pass_store_old_pass($user) {
|
104
|
// Update or Insert using db_merge() the old password.
|
105
|
$result = db_merge('recovery_pass')
|
106
|
->key(array('uid' => $user->uid))
|
107
|
->fields(array(
|
108
|
'uid' => (int) $user->uid,
|
109
|
'old_pass' => $user->pass,
|
110
|
'changed' => time(),
|
111
|
))
|
112
|
->execute();
|
113
|
if ($result) {
|
114
|
// Successfully saved old password.
|
115
|
return TRUE;
|
116
|
}
|
117
|
return FALSE;
|
118
|
}
|
119
|
|
120
|
/**
|
121
|
* Implements hook_mail().
|
122
|
*/
|
123
|
function recovery_pass_mail($key, &$message, $params) {
|
124
|
switch ($key) {
|
125
|
case 'recovery_pass':
|
126
|
// Mail parameters used for recovery mail.
|
127
|
$message['subject'] = $params['subject'];
|
128
|
$message['body'][] = $params['body'];
|
129
|
if (module_exists("htmlmail")) {
|
130
|
// For html mail.
|
131
|
$message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
|
132
|
}
|
133
|
break;
|
134
|
}
|
135
|
}
|
136
|
|
137
|
/**
|
138
|
* Returns a mail string for a variable name.
|
139
|
*
|
140
|
* Used by recovery_pass_mail() and the settings forms to retrieve strings.
|
141
|
*/
|
142
|
function _recovery_pass_mail_text($key, $language = NULL, $replace = TRUE, $user = array()) {
|
143
|
$langcode = !empty($language) ? $language->language : NULL;
|
144
|
$text = '';
|
145
|
|
146
|
if ($admin_setting = variable_get('recovery_pass_' . $key, FALSE)) {
|
147
|
// An admin setting overrides the default string.
|
148
|
$text = $admin_setting;
|
149
|
}
|
150
|
else {
|
151
|
// No override, return default string.
|
152
|
switch ($key) {
|
153
|
case 'email_subject':
|
154
|
$text = t('Replacement login information for [user:name] at [site:name]', array(), array('langcode' => $langcode));
|
155
|
break;
|
156
|
|
157
|
case 'email_text':
|
158
|
$text = t("[user:name],
|
159
|
|
160
|
A request to reset the password for your account has been made at [site:name].
|
161
|
|
162
|
Your new password is [user_new_password].
|
163
|
|
164
|
|
165
|
-- [site:name] team", array(), array('langcode' => $langcode));
|
166
|
break;
|
167
|
|
168
|
case 'old_pass_warning':
|
169
|
$text = t('You are using <strong>old password</strong>, your password was reset recently. New Password was sent to your registered email id.');
|
170
|
break;
|
171
|
}
|
172
|
}
|
173
|
|
174
|
if ($replace) {
|
175
|
// Token Replace the text.
|
176
|
return token_replace($text, array('user' => $user));
|
177
|
}
|
178
|
|
179
|
return $text;
|
180
|
}
|
181
|
|
182
|
/**
|
183
|
* Implements hook_form_alter().
|
184
|
*/
|
185
|
function recovery_pass_form_alter(&$form, &$form_state, $form_id) {
|
186
|
if (variable_get('recovery_pass_old_pass_show', 1)) {
|
187
|
switch ($form_id) {
|
188
|
case 'user_login_block':
|
189
|
case 'user_login':
|
190
|
// Extending default drupal login validators.
|
191
|
$insert = 'recovery_pass_user_login_validate';
|
192
|
$form['#validate'] = _recovery_pass_insert_array($form['#validate'], 1, $insert);
|
193
|
break;
|
194
|
}
|
195
|
}
|
196
|
}
|
197
|
|
198
|
/**
|
199
|
* To insert our validator at index 1 between the default validators.
|
200
|
*/
|
201
|
function _recovery_pass_insert_array($array, $index, $val) {
|
202
|
// Because this will be used one more time.
|
203
|
$size = count($array);
|
204
|
if (!is_int($index) || $index < 0 || $index > $size) {
|
205
|
return -1;
|
206
|
}
|
207
|
else {
|
208
|
$temp = array_slice($array, 0, $index);
|
209
|
$temp[] = $val;
|
210
|
return array_merge($temp, array_slice($array, $index, $size));
|
211
|
}
|
212
|
}
|
213
|
|
214
|
/**
|
215
|
* Custom Submit handler for user login form.
|
216
|
*
|
217
|
* Incase user tries to login using old pass then error msg is shown that pass
|
218
|
* has been reset, till user tries any other pass.
|
219
|
*/
|
220
|
function recovery_pass_user_login_validate($form, &$form_state) {
|
221
|
$input_password = trim($form_state['values']['pass']);
|
222
|
if (!empty($form_state['values']['name']) && !empty($input_password)) {
|
223
|
$account = db_query("SELECT uid,pass FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
|
224
|
// Check uid exists in recovery_pass table.
|
225
|
$result = db_select('recovery_pass', 'r')
|
226
|
->fields('r', array('uid', 'old_pass'))
|
227
|
->condition('uid', (int) $account->uid)
|
228
|
->execute()
|
229
|
->fetchAssoc();
|
230
|
if ($result) {
|
231
|
// If uid exists in table.
|
232
|
$old_password = $result['old_pass'];
|
233
|
// Match input password with password stored in recovery_pass table.
|
234
|
if (_recovery_pass_match_old_password($input_password, $old_password)) {
|
235
|
drupal_set_message(_recovery_pass_mail_text('old_pass_warning'), 'warning', FALSE);
|
236
|
}
|
237
|
else {
|
238
|
// Irrespective of the input password delete the entry.
|
239
|
$entry_deleted = db_delete('recovery_pass')
|
240
|
->condition('uid', $result['uid'])
|
241
|
->execute();
|
242
|
if (!$entry_deleted) {
|
243
|
watchdog('recovery_pass', 'Error deleting entry from recovery_table for user @id', array('@id' => $result['uid']), WATCHDOG_NOTICE, 'link');
|
244
|
}
|
245
|
}
|
246
|
}
|
247
|
}
|
248
|
}
|
249
|
|
250
|
/**
|
251
|
* Matches old password stored in recovery_pass table with user input password.
|
252
|
*/
|
253
|
function _recovery_pass_match_old_password($password, $old_password) {
|
254
|
// Allow alternate password hashing schemes.
|
255
|
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
|
256
|
if (substr($old_password, 0, 2) == 'U$') {
|
257
|
// This may be an updated password from user_update_7000(). Such hashes
|
258
|
// have 'U' added as the first character and need an extra md5().
|
259
|
$stored_hash = substr($old_password, 1);
|
260
|
$password = md5($password);
|
261
|
}
|
262
|
else {
|
263
|
$stored_hash = $old_password;
|
264
|
}
|
265
|
|
266
|
$type = substr($stored_hash, 0, 3);
|
267
|
switch ($type) {
|
268
|
case '$S$':
|
269
|
// A normal Drupal 7 password using sha512.
|
270
|
$hash = _password_crypt('sha512', $password, $stored_hash);
|
271
|
break;
|
272
|
|
273
|
case '$H$':
|
274
|
// phpBB3 uses "$H$" for the same thing as "$P$".
|
275
|
case '$P$':
|
276
|
// A phpass password generated using md5. This is an
|
277
|
// imported password or from an earlier Drupal version.
|
278
|
$hash = _password_crypt('md5', $password, $stored_hash);
|
279
|
break;
|
280
|
|
281
|
default:
|
282
|
return FALSE;
|
283
|
}
|
284
|
return ($hash && $stored_hash == $hash);
|
285
|
}
|
286
|
|
287
|
/**
|
288
|
* Implements hook_cron().
|
289
|
*/
|
290
|
function recovery_pass_cron() {
|
291
|
$expiry_period = strtotime("-" . variable_get('recovery_pass_expiry_period', '1') . " week");
|
292
|
// Delete all records more created than one week ago.
|
293
|
$entry_deleted = db_delete('recovery_pass')
|
294
|
->condition('changed', $expiry_period, '<')
|
295
|
->execute();
|
296
|
|
297
|
if ($entry_deleted) {
|
298
|
watchdog('recovery_pass', 'Error deleting entry from recovery_table at cron time.', array(), WATCHDOG_NOTICE, 'link');
|
299
|
}
|
300
|
}
|