/[drupal]/contributions/modules/password_policy/password_policy.module
ViewVC logotype

Contents of /contributions/modules/password_policy/password_policy.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.24 - (show annotations) (download) (as text)
Mon Oct 19 15:31:50 2009 UTC (5 weeks, 2 days ago) by miglius
Branch: MAIN
CVS Tags: HEAD
Changes since 1.23: +2 -2 lines
File MIME type: text/x-php
password_policy: fixed missing closing div tag, #602320 by florent.jousseaume
1 <?php
2 // $Id: password_policy.module,v 1.23 2009/06/04 21:48:39 deekayen Exp $
3 /**
4 * @file
5 * The password policy module allows you to enforce a specific level of
6 * password complexity for the user passwords on the system.
7 */
8
9 //////////////////////////////////////////////////////////////////////////////
10
11 define('PASSWORD_POLICY_ADMIN', variable_get('password_policy_admin', 0));
12 define('PASSWORD_POLICY_BEGIN', variable_get('password_policy_begin', 0));
13 define('PASSWORD_POLICY_BLOCK', variable_get('password_policy_block', 0));
14 define('PASSWORD_POLICY_SHOW_RESTRICTIONS', variable_get('password_policy_show_restrictions', 0));
15
16 define('PASSWORD_POLICY_ENTRIES_PER_PAGE', 20);
17
18 //////////////////////////////////////////////////////////////////////////////
19 // Core API hooks
20
21 /**
22 * Implementation of hook_help().
23 */
24 function password_policy_help($path, $arg) {
25 switch ($path) {
26 case "admin/help#password_policy":
27 return '<p>'. t('The password policy module allows you to enforce a specific level of password complexity for the user passwords on the system.') .'</p>';
28 }
29 }
30
31 /**
32 * Implementation of hook_init().
33 */
34 function password_policy_init() {
35 global $_password_policy;
36
37 // Save all available constrains in a global variable.
38 $dir = drupal_get_path('module', 'password_policy') .'/constraints';
39 $constraints = file_scan_directory($dir, '^constraint.*\.inc$');
40 foreach ($constraints as $file) {
41 if (is_file($file->filename)) {
42 include_once($file->filename);
43 $_password_policy[] = drupal_substr($file->name, 11);
44 }
45 }
46 }
47
48 /**
49 * Implementation of hook_theme().
50 */
51
52 function password_policy_theme() {
53 return array(
54 'password_policy_admin_list' => array(
55 'arguments' => array('form' => NULL),
56 'file' => 'password_policy.theme.inc',
57 ),
58 );
59 }
60
61 /**
62 * Implementation of hook_perm().
63 */
64 function password_policy_perm() {
65 return array('unblock expired accounts');
66 }
67
68 /**
69 * Implementation of hook_menu().
70 */
71 function password_policy_menu() {
72 $items['admin/settings/password_policy'] = array(
73 'title' => 'Password policies',
74 'description' => 'Configures policies for user account passwords.',
75 'page callback' => 'drupal_get_form',
76 'page arguments' => array('password_policy_admin_settings'),
77 'access arguments' => array('administer site configuration'),
78 'file' => 'password_policy.admin.inc',
79 );
80 $items['admin/settings/password_policy/configure'] = array(
81 'title' => 'Settings',
82 'type' => MENU_DEFAULT_LOCAL_TASK,
83 );
84 $items['admin/settings/password_policy/list'] = array(
85 'title' => 'List',
86 'type' => MENU_LOCAL_TASK,
87 'page callback' => 'drupal_get_form',
88 'page arguments' => array('password_policy_admin_list'),
89 'access arguments' => array('administer site configuration'),
90 'weight' => 1,
91 'file' => 'password_policy.admin.inc',
92 );
93 $items['admin/settings/password_policy/add'] = array(
94 'title' => 'Add',
95 'type' => MENU_LOCAL_TASK,
96 'page callback' => 'drupal_get_form',
97 'page arguments' => array('password_policy_admin_form', NULL),
98 'access arguments' => array('administer site configuration'),
99 'weight' => 2,
100 'file' => 'password_policy.admin.inc',
101 );
102 $items['admin/settings/password_policy/%pp_policy'] = array(
103 'title callback' => 'password_policy_format_title',
104 'title arguments' => array(3),
105 'type' => MENU_CALLBACK,
106 'page callback' => 'password_policy_admin_view',
107 'page arguments' => array(3),
108 'access arguments' => array('administer site configuration'),
109 'file' => 'password_policy.admin.inc',
110 );
111 $items['admin/settings/password_policy/%pp_policy/view'] = array(
112 'title' => 'View',
113 'type' => MENU_DEFAULT_LOCAL_TASK,
114 );
115 $items['admin/settings/password_policy/%pp_policy/edit'] = array(
116 'title' => 'Edit',
117 'type' => MENU_LOCAL_TASK,
118 'page callback' => 'drupal_get_form',
119 'page arguments' => array('password_policy_admin_form', 3),
120 'access arguments' => array('administer site configuration'),
121 'weight' => 1,
122 'file' => 'password_policy.admin.inc',
123 );
124 $items['admin/settings/password_policy/delete'] = array(
125 'title' => 'Delete',
126 'type' => MENU_CALLBACK,
127 'page callback' => 'drupal_get_form',
128 'page arguments' => array('password_policy_admin_delete'),
129 'access arguments' => array('administer site configuration'),
130 'file' => 'password_policy.admin.inc',
131 );
132 $items['admin/user/expired'] = array(
133 'title' => 'Expired accounts',
134 'description' => 'Lists all expired accounts.',
135 'page callback' => 'password_policy_expired_list',
136 'page arguments' => array('password_policy_list_expired'),
137 'access arguments' => array('unblock expired accounts'),
138 );
139 $items['admin/user/expired/unblock/%pp_uid'] = array(
140 'title' => 'Unblock',
141 'type' => MENU_CALLBACK,
142 'page callback' => 'password_policy_expired_unblock',
143 'page arguments' => array(4),
144 'access arguments' => array('unblock expired accounts'),
145 );
146 return $items;
147 }
148
149 /**
150 * Load policy array from the database.
151 *
152 * @param $pid
153 * The policy id
154 *
155 * @return
156 * A populated policy array or NULL if not found.
157 */
158 function pp_policy_load($pid) {
159 static $policies = array();
160
161 if (is_numeric($pid)) {
162 if (isset($policies[$pid])) {
163 return $policies[$pid];
164 }
165 else {
166 $policy = _password_policy_load_policy_by_pid($pid);
167 if ($policy) {
168 $policies[$pid] = $policy;
169 return $policy;
170 }
171 }
172 }
173 return FALSE;
174 }
175
176 /**
177 * Load user object from the database.
178 *
179 * @param $id
180 * The user id
181 *
182 * @return
183 * A populated user object or NULL if not found.
184 */
185 function pp_uid_load($id) {
186 if (is_numeric($id)) {
187 $account = user_load(array('uid' => $id));
188 if ($account) {
189 return $account;
190 }
191 }
192 return FALSE;
193 }
194
195 /**
196 * Display a password policy form title.
197 *
198 * @param $policy
199 * Policy array
200 *
201 * @return
202 * A policy's title string
203 */
204 function password_policy_format_title($policy) {
205 return $policy['name'];
206 }
207
208 /**
209 * Implementation of hook_user().
210 */
211 function password_policy_user($op, &$edit, &$account, $category = NULL) {
212 switch ($op) {
213 case "insert":
214 if (!empty($edit['pass'])) {
215 // New users do not yet have an uid during the validation step, but they do have at this insert step.
216 // Store their first password in the system for use with the history constraint (if used).
217 if ($account->uid) {
218 _password_policy_store_password($account->uid, $edit['pass']);
219 }
220 }
221 break;
222 case "login":
223 $policy = _password_policy_load_active_policy();
224 // A value $edit['name'] is NULL for a one time login
225 if ($policy && ($account->uid > 1 || PASSWORD_POLICY_ADMIN) && !empty($edit['name'])) {
226 // Calculate expiration and warning times.
227 $expiration = $policy['expiration'];
228 $warning = max(explode(',', $policy['warning']));
229 $expiration_seconds = $expiration*60*60*24;
230 $warning_seconds = $warning*60*60*24;
231 // The policy was enabled
232 $policy_start = _password_policy_start($expiration_seconds);
233 if (!empty($expiration)) {
234 // Account expiration is active.
235 // Get the last password change time.
236 $result = db_query_range("SELECT * FROM {password_policy_history} WHERE uid = %d ORDER BY created DESC", $account->uid, 0, 1);
237 if ($row = db_fetch_object($result)) {
238 $last_change = $row->created;
239 }
240 else {
241 // A user has not changed his pwd after this module had been enabled.
242 $last_change = $account->created;
243 }
244 $time = time();
245 if ($time > max($policy_start, $last_change) + $expiration_seconds) {
246 // The account has expired.
247 db_query("UPDATE {users} SET status = '0' WHERE uid = %d", $account->uid);
248 $result = db_query("SELECT * FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
249 if ($row = db_fetch_array($result)) {
250 db_query("UPDATE {password_policy_expiration} SET blocked = %d WHERE uid = %d", $time, $account->uid);
251 }
252 else {
253 db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES (%d, %d)", $account->uid, $time);
254 }
255 watchdog('password_policy', 'Password for user %name has expired.', array('%name' => $account->name), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
256 if (PASSWORD_POLICY_BLOCK == 0) {
257 // User is blocked imediately and cannot change his password after expiration.
258 include_once(drupal_get_path('module', 'user') .'/user.pages.inc');
259 user_logout();
260 }
261 else {
262 // User is transfered to a password change page. User is unblocked on password change.
263 // If the user don't change password now, he will not be able to login again, since he is still blocked.
264 drupal_set_message(t('Your password has expired. You have to change it now or you won\'t be able to login again.'), 'error');
265 $destination = drupal_get_destination();
266 unset($_REQUEST['destination']);
267 drupal_goto('user/'. $account->uid .'/'. (module_exists('password_policy_password_tab') ? 'password' : 'edit'), $destination);
268 }
269 }
270 elseif ($time > max($policy_start, $last_change) + $expiration_seconds - $warning_seconds) {
271 // The warning is shown on login and the user is transfered to the password change page.
272 $days_left = ceil((max($policy_start, $last_change) + $expiration_seconds - $time)/(60*60*24));
273 drupal_set_message(format_plural($days_left, 'Your password will expire in less than one day. Please change it.', 'Your password will expire in less than @count days. Please change it.'));
274 $destination = drupal_get_destination();
275 unset($_REQUEST['destination']);
276 drupal_goto('user/'. $account->uid .'/'. (module_exists('password_policy_password_tab') ? 'password' : 'edit'), $destination);
277 }
278 }
279 }
280 break;
281 case "delete":
282 db_query("DELETE FROM {password_policy_history} WHERE uid = %d", $account->uid);
283 db_query("DELETE FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
284 break;
285 case "submit":
286 if ($account->status == 0 && isset($edit['status']) && $edit['status'] == 1 && user_access('unblock expired accounts')) {
287 // Account is beeing unblocked.
288 _password_policy_unblock($account);
289 }
290 break;
291 }
292 }
293
294 /**
295 * Implementation of hook_form_alter().
296 */
297 function password_policy_form_alter(&$form, $form_state, $form_id) {
298 switch ($form_id) {
299 case "user_profile_form":
300 case "user_register":
301 // Password change form.
302 $uid = isset($form['#uid']) ? $form['#uid'] : NULL;
303 //if ($uid == 1 && !PASSWORD_POLICY_ADMIN) { break; }
304 $policy = _password_policy_load_active_policy();
305 $translate = array();
306 if (!empty($policy['policy'])) {
307 // Some policy constraints are active.
308 password_policy_add_policy_js($policy, $uid);
309 foreach ($policy['policy'] as $key => $value) {
310 $translate['constraint_'. $key] = _password_policy_constraint_error($key, $value);
311 }
312 }
313
314 // Printing out the restrictions.
315 if (PASSWORD_POLICY_SHOW_RESTRICTIONS && isset($translate)) {
316 $form['account']['pass']['#prefix'] = '<div id="account-pass-restrictions"><ul><li>'. implode('</li><li>', $translate) .'</li></ul></div>';
317 }
318
319 // Set a custom form validate and submit handlers.
320 $form['#validate'][] = 'password_policy_password_validate';
321 $form['#submit'][] = 'password_policy_password_submit';
322 break;
323 }
324 }
325
326 /**
327 * Implementation of hook_cron().
328 */
329 function password_policy_cron() {
330 $policy = _password_policy_load_active_policy();
331 if ($policy) {
332 $expiration = $policy['expiration'];
333 $warnings = explode(',', $policy['warning']);
334 if (!empty($expiration)) {
335 // Accounts expiration is active.
336 // Get all users' last password change time. We don't touch blocked accounts
337 $result = db_query("SELECT u.uid AS uid, u.created AS created_u, p.created AS created_p, e.pid AS pid, e.warning AS warning, e.unblocked AS unblocked FROM {users} u LEFT JOIN {password_policy_history} p ON u.uid = p.uid LEFT JOIN {password_policy_expiration} e ON u.uid = e.uid WHERE u.uid > 0 AND u.status = '1' ORDER BY p.created ASC");
338 while ($row = db_fetch_object($result)) {
339 if ($row->uid == 1 && !PASSWORD_POLICY_ADMIN)
340 continue;
341
342 // Use account creation timestamp if there is no entry in password history table.
343 $accounts[$row->uid] = empty($row->created_p) ? $row->created_u : $row->created_p;
344 // Last time a warning was mailed out (if was). We need it because we send warnings only once a day, not on all cron runs.
345 $warns[$row->uid] = $row->warning;
346 // The user was last time unblocked (if was). We don't block this account again for some period of time.
347 $unblocks[$row->uid] = $row->unblocked;
348 // The user was last time unblocked (if was). We don't block this account again for some period of time.
349 $pids[$row->uid] = $row->pid;
350 }
351 // Calculate expiration time.
352 $expiration_seconds = $expiration*60*60*24;
353 $policy_start = _password_policy_start($expiration_seconds);
354 rsort($warnings, SORT_NUMERIC);
355 $time = time();
356 if ($accounts) {
357 foreach ($accounts as $uid => $last_change) {
358 // Check expiration and warning days for each account.
359 if (!empty($warnings)) {
360 foreach ($warnings as $warning) {
361 // Loop through all configured warning send out days. If today is the day we send out the warning.
362 $warning_seconds = $warning*60*60*24;
363 // Warning start time.
364 $start_period = max($policy_start, $last_change) + $expiration_seconds - $warning_seconds;
365 // Warning end time. We create a one day window for cron to run.
366 $end_period = $start_period + 60*60*24;
367 if ($warns[$uid] && $warns[$uid] > $start_period && $warns[$uid] < $end_period) {
368 // A warning was already mailed out
369 continue;
370 }
371 if ($time > $start_period && $time < $end_period) {
372 // A warning falls in the one day window, so we send out the warning.
373 $account = user_load(array('uid' => $uid));
374 $message = drupal_mail('password_policy', 'warning', $account->mail, user_preferred_language($account), array('account' => $account, 'days_left' => $warning));
375 if ($message['result']) {
376 // The mail was sent out successfully.
377 watchdog('password_policy', 'Password expiration warning mailed to %username at %email.', array('%username' => $account->name, '%email' => $account->mail));
378 }
379 if ($pids[$uid]) {
380 db_query("UPDATE {password_policy_expiration} SET warning = %d WHERE uid = %d", $time, $uid);
381 }
382 else {
383 db_query("INSERT INTO {password_policy_expiration} (uid, warning) VALUES (%d, %d)", $uid, $time);
384 }
385 }
386 }
387 }
388 if ($time > max($policy_start, $last_change) + $expiration_seconds && $time > $unblocks[$uid] + 60*60*24 && PASSWORD_POLICY_BLOCK == 0) {
389 // Block expired accounts. Unblocked accounts are not blocked for 24h.
390 // One time login lasts for a 24h.
391 db_query("UPDATE {users} SET status = '0' WHERE uid = %d", $uid);
392 if ($pids[$uid]) {
393 db_query("UPDATE {password_policy_expiration} SET blocked = %d WHERE uid = %d", $time, $uid);
394 }
395 else {
396 db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES (%d, %d)", $uid, $time);
397 }
398
399 $account = user_load(array('uid' => $uid));
400 watchdog('password_policy', 'Password for user %name has expired.', array('%name' => $account->name), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
401 }
402 }
403 }
404 }
405 }
406 }
407
408 /**
409 * Implementation of hook_mail().
410 */
411 function password_policy_mail($key, &$message, $params) {
412 $language = $message['language'];
413 $variables = password_policy_mail_tokens($params, $language);
414 $message['subject'] .= _password_policy_mail_text($key .'_subject', $language, $variables);
415 $message['body'][] = _password_policy_mail_text($key .'_body', $language, $variables);
416 }
417
418 /**
419 * Implementation of hook_simpletest().
420 */
421 function password_policy_simpletest() {
422 // Scan through mymodule/tests directory for any .test files to tell SimpleTest module.
423 $tests = file_scan_directory(drupal_get_path('module', 'password_policy') .'/tests', '\.test');
424 return array_keys($tests);
425 }
426
427 //////////////////////////////////////////////////////////////////////////////
428 // FAPI
429
430 /**
431 * Password save validate handler.
432 */
433 function password_policy_password_validate($form, &$form_state) {
434 $values = $form_state['values'];
435 $account = isset($form['_account']['#value']) ? $form['_account']['#value'] : (object)array('uid' => 0);
436
437 if (!empty($values['pass']) && !isset($values['auth_openid'])) {
438 $error = _password_policy_constraint_validate($values['pass'], $account);
439 if ($error) {
440 form_set_error('pass', t('Your password has not met the following requirement(s):') .'<ul><li>'. implode('</li><li>', $error) .'</li></ul>');
441 }
442 }
443 }
444
445 /**
446 * Password save submit handler.
447 */
448 function password_policy_password_submit($form, &$form_state) {
449 global $user;
450
451 $values = $form_state['values'];
452 $account = isset($form['_account']['#value']) ? $form['_account']['#value'] : (object)array('uid' => 0);
453
454 // Track the hashed password values which can then be used in the history constraint.
455 if ($account->uid && !empty($values['pass'])) {
456 _password_policy_store_password($account->uid, $values['pass']);
457
458 // If user successfully changed his password we will unblock the account.
459 if ($user->uid == $account->uid) {
460 db_query("UPDATE {users} SET status = 1 WHERE uid = %d", $account->uid);
461 db_query("DELETE FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
462 }
463 }
464 }
465
466 //////////////////////////////////////////////////////////////////////////////
467 // Expired accounts UI
468
469 /**
470 * Lists all expired accounts.
471 */
472 function password_policy_expired_list() {
473 $header[] = array('data' => t('Username'), 'field' => 'name');
474 $header[] = array('data' => t('Blocked'), 'field' => 'blocked', 'sort' => 'desc');
475 $header[] = array('data' => t('Unblocked'), 'field' => 'unblocked');
476 $header[] = array('data' => t('Action'));
477
478 $result = pager_query("SELECT p.*, u.name FROM {password_policy_expiration} p INNER JOIN {users} u ON p.uid = u.uid WHERE p.blocked > 0". tablesort_sql($header), PASSWORD_POLICY_ENTRIES_PER_PAGE, 0, NULL);
479 while ($row = db_fetch_object($result)) {
480 $entry[$row->uid]['name'] = l($row->name, 'user/'. $row->uid);
481 $entry[$row->uid]['blocked'] = format_date($row->blocked, 'medium');
482 $entry[$row->uid]['unblocked'] = $row->unblocked < $row->blocked ? '' : format_date($row->unblocked, 'medium');
483 $entry[$row->uid]['action'] = $row->unblocked < $row->blocked ? l(t('unblock'), 'admin/user/expired/unblock/'. $row->uid) : '';
484 }
485 if (!isset($entry)) {
486 $colspan = '4';
487 $entry[] = array(array('data' => t('No entries'), 'colspan' => $colspan));
488 }
489 $page = theme('table', $header, $entry);
490 $page .= theme('pager', NULL, PASSWORD_POLICY_ENTRIES_PER_PAGE, 0);
491
492 return $page;
493 }
494
495 /**
496 * Unblocks the expired account.
497 */
498 function password_policy_expired_unblock($account) {
499 // Unblock the user
500 _password_policy_unblock($account);
501 drupal_goto('admin/user/expired');
502 }
503
504 //////////////////////////////////////////////////////////////////////////////
505 // Mail handling
506
507 /**
508 * Returns a mail string for a variable name.
509 *
510 * Used by password_policy_mail() and the settings forms to retrieve strings.
511 */
512 function _password_policy_mail_text($key, $language = NULL, $variables = array()) {
513 $langcode = isset($language) ? $language->language : NULL;
514
515 if ($admin_setting = variable_get('password_policy_'. $key, FALSE)) {
516 // An admin setting overrides the default string.
517 return strtr($admin_setting, $variables);
518 }
519 else {
520 // No override, return with default strings.
521 switch ($key) {
522 case 'warning_subject':
523 return t('Password expiration warning for !username at !site', $variables, $langcode);
524 case 'warning_body':
525 return t("!username,\n\nYour password at !site will expire in less than !days_left day(s).\n\nPlease go to !edit_uri to change your password.", $variables, $langcode);
526 }
527 }
528 }
529
530 /**
531 * Return an array of token to value mappings for user e-mail messages.
532 *
533 * @param $params
534 * Structured array with the parameters.
535 * @param $language
536 * Language object to generate the tokens with.
537 *
538 * @return
539 * Array of mappings from token names to values (for use with strtr()).
540 */
541 function password_policy_mail_tokens($params, $language) {
542 global $base_url;
543 $account = $params['account'];
544 $tokens = array(
545 '!username' => $account->name,
546 '!site' => variable_get('site_name', 'Drupal'),
547 '!uri' => $base_url,
548 '!uri_brief' => drupal_substr($base_url, drupal_strlen('http://')),
549 '!date' => format_date(time(), 'medium', '', NULL, $language->language),
550 '!login_uri' => url('user', array('absolute' => TRUE)),
551 '!edit_uri' => url('user/'. $account->uid .'/'. (module_exists('password_policy_password_tab') ? 'password' : 'edit'), array('absolute' => TRUE)),
552 '!days_left' => isset($params['days_left']) ? $params['days_left'] : NULL,
553 '!login_url' => isset($params['login_url']) ? $params['login_url'] : NULL,
554 );
555 return $tokens;
556 }
557
558 //////////////////////////////////////////////////////////////////////////////
559 // Constraints API
560
561 /**
562 * Validates user password. Returns NULL on success or array with error messages
563 * from the constraints on failure.
564 *
565 * @param $pass
566 * Clear text password.
567 * @param &$account
568 * Populated user object.
569 *
570 * @return
571 * NULL or array with error messages.
572 */
573 function _password_policy_constraint_validate($pass, &$account) {
574 $error = NULL;
575 $policy = _password_policy_load_active_policy();
576 if (!empty($policy['policy'])) {
577 foreach ($policy['policy'] as $key => $value) {
578 if (!call_user_func('password_policy_constraint_'. $key .'_validate', $pass, $value, $account->uid)) {
579 $error[] = call_user_func('password_policy_constraint_'. $key .'_error', $value);
580 }
581 }
582 }
583 return $error;
584 }
585
586 /**
587 * Gets the constraint's name and description.
588 *
589 * @param $name
590 * Name of the constraint.
591 *
592 * @return
593 * Array containing the name and description.
594 */
595 function _password_policy_constraint_description($name) {
596 return call_user_func('password_policy_constraint_'. $name .'_description');
597 }
598
599 /**
600 * Gets the constraint's error message.
601 *
602 * @param $name
603 * Name of the constraint.
604 * @param $constraint
605 * Constraint value.
606 *
607 * @return
608 * Error message.
609 */
610 function _password_policy_constraint_error($name, $constraint) {
611 return call_user_func('password_policy_constraint_'. $name .'_error', $constraint);
612 }
613
614 /**
615 * Gets the javascript code from the constraint to be added to the password validation.
616 *
617 * @param $name
618 * Name of the constraint.
619 * @param $constraint
620 * Constraint value.
621 * @param $uid
622 * User's id.
623 *
624 * @return
625 * Javascript code snippet for the constraint.
626 */
627 function _password_policy_constraint_js($name, $constraint, $uid) {
628 if (function_exists('password_policy_constraint_'. $name .'_js')) {
629 return call_user_func('password_policy_constraint_'. $name .'_js', $constraint, $uid);
630 }
631 }
632
633 //////////////////////////////////////////////////////////////////////////////
634 // Auxiliary functions
635
636 /**
637 * Loads the policy with the specified id.
638 *
639 * @param $pid
640 * The policy id.
641 *
642 * @return
643 * A policy array, or NULL if no policy was found.
644 */
645 function _password_policy_load_policy_by_pid($pid) {
646 $result = db_query('SELECT * FROM {password_policy} WHERE pid = %d', $pid);
647 $row = db_fetch_array($result);
648 if (is_array($row)) {
649 // fetch and unserialize the serialized policy
650 $row['policy'] = unserialize($row['policy']);
651 return $row;
652 }
653 return NULL;
654 }
655
656 /**
657 * Loads the default (enabled and active) policy.
658 *
659 * @return
660 * A policy array, or NULL if no active policy exists.
661 */
662 function _password_policy_load_active_policy() {
663 $result = db_query('SELECT * FROM {password_policy} p WHERE p.enabled = 1');
664 $row = db_fetch_array($result);
665 if (is_array($row)) {
666 // fetch and unserialize the serialized policy
667 $row['policy'] = unserialize($row['policy']);
668 return $row;
669 }
670 return NULL;
671 }
672
673 /**
674 * Returns a timestamp when the policy was enabled.
675 * It might be pushed back by the expiration time for the retroactive behaviour
676 * based on the configuration options.
677 *
678 * @param $expiration
679 * Expiration time in seconds.
680 *
681 * @return
682 * A timestamp when a policy was enabled
683 */
684 function _password_policy_start($expiration = 0) {
685 $result = db_query_range("SELECT created FROM {password_policy} WHERE enabled = 1 ORDER BY enabled DESC", 0, 1);
686 if ($row = db_fetch_object($result)) {
687 $policy_start = $row->created;
688 }
689 if (PASSWORD_POLICY_BEGIN == 1) {
690 // Password older than expiration time expires starting from setting the policy.
691 $policy_start -= $expiration;
692 }
693 return $policy_start;
694 }
695
696 /**
697 * Stores user password hash.
698 *
699 * @param $uid
700 * User id.
701 * @param $pass
702 * Clear text password.
703 */
704 function _password_policy_store_password($uid, $pass) {
705 db_query("INSERT INTO {password_policy_history} (uid, pass, created) VALUES (%d, '%s', %d)", $uid, md5($pass), time());
706 }
707
708 /**
709 * Unblocks the expired account.
710 *
711 * @param $uccount
712 * User pbject.
713 */
714 function _password_policy_unblock($account) {
715 // Unblock the user
716 db_query("UPDATE {users} SET status = '1' WHERE uid = %d", $account->uid);
717 db_query("UPDATE {password_policy_expiration} SET unblocked = %d WHERE uid = %d", time(), $account->uid);
718
719 $message = drupal_mail('user', 'password_reset', $account->mail, user_preferred_language($account), array('account' => $account, 'login_url' => user_pass_reset_url($account)));
720 if ($message['result']) {
721 watchdog('password_policy', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
722 }
723 drupal_set_message(t('The user %name has been unblocked.', array('%name' => $account->name)));
724 }
725
726 /**
727 * Add password policy JS
728 *
729 * @param $policy
730 * A policy array.
731 * @param $uid
732 * A user ID for which the policy is applied.
733 */
734 function password_policy_add_policy_js($policy, $uid) {
735
736 // Print out the javascript which checks the strength of the password.
737 // It overwrites the defaut core javascript function.
738 drupal_add_js(drupal_get_path('module', 'password_policy') .'/password_policy.js', 'module');
739 $s = "/**\n";
740 $s .= " * Evaluate the strength of a user's password.\n";
741 $s .= " *\n";
742 $s .= " * Returns the estimated strength and the relevant output message.\n";
743 $s .= " */\n";
744 $s .= "Drupal.evaluatePasswordStrength = function(value) {\n";
745 $s .= " var strength = \"high\", msg = [], translate = Drupal.settings.password_policy;\n";
746 // Print out each constraint's javascript password strength evaluation.
747 foreach ($policy['policy'] as $key => $value) {
748 $s .= _password_policy_constraint_js($key, $value, $uid);
749 // Constraints' error messages are used in javascript.
750 $translate['constraint_'. $key] = _password_policy_constraint_error($key, $value);
751 }
752 $s .= " msg = msg.length > 0 ? translate.needsMoreVariation +\"<ul><li>\"+ msg.join(\"</li><li>\") +\"</li></ul>\" : \"\";\n";
753 $s .= " return { strength: strength, message: msg };\n";
754 $s .= "};\n";
755 drupal_add_js($s, 'inline');
756
757 drupal_add_js(array(
758 'password_policy' => array_merge(array(
759 'strengthTitle' => t('Password quality:'),
760 'lowStrength' => t('Bad'),
761 'mediumStrength' => t('Medium'),
762 'highStrength' => t('Good'),
763 'needsMoreVariation' => t('The password does not include enough variation to be secure.'),
764 'confirmSuccess' => t('Yes'),
765 'confirmFailure' => t('No'),
766 'confirmTitle' => t('Passwords match:')), $translate)),
767 'setting');
768 }
769

  ViewVC Help
Powered by ViewVC 1.1.2