Changeset 11992 for trunk/password.php


Ignore:
Timestamp:
Aug 24, 2011, 10:03:53 PM (13 years ago)
Author:
plg
Message:

feature 2027 implemented: the "lost password" feature was rewritten.

The algorithm is highly inspired from WordPress :

1) in a single field, you give a username or an email
2) Piwigo sends an email with the activation key
3) the user clicks on the link in the email (with the activation key) and is able to set a new password

The "lost password" feature is no longer limited to "classic" users:
administrators and webmasters can use it too (no need to tell webmasters
that they can only change their password in the database)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/password.php

    r10824 r11992  
    3333// | Check Access and exit when user status is not ok                      |
    3434// +-----------------------------------------------------------------------+
     35
    3536check_status(ACCESS_FREE);
    3637
    3738// +-----------------------------------------------------------------------+
    38 // |                          send a new password                          |
     39// | Functions                                                             |
     40// +-----------------------------------------------------------------------+
     41
     42/**
     43 * checks the validity of input parameters, fills $page['errors'] and
     44 * $page['infos'] and send an email with confirmation link
     45 *
     46 * @return bool (true if email was sent, false otherwise)
     47 */
     48function process_password_request()
     49{
     50  global $page, $conf;
     51 
     52  if (empty($_POST['username_or_email']))
     53  {
     54    array_push($page['errors'], l10n('Enter a username or email address'));
     55    return false;
     56  }
     57 
     58  $user_id = get_userid_by_email($_POST['username_or_email']);
     59   
     60  if (!is_numeric($user_id))
     61  {
     62    $user_id = get_userid($_POST['username_or_email']);
     63  }
     64
     65  if (!is_numeric($user_id))
     66  {
     67    array_push($page['errors'], l10n('Invalid username or email'));
     68    return false;
     69  }
     70
     71  $userdata = getuserdata($user_id, false);
     72
     73  // password request is not possible for guest/generic users
     74  $status = $userdata['status'];
     75  if (is_a_guest($status) or is_generic($status))
     76  {
     77    array_push($page['errors'], l10n('Password reset is not allowed for this user'));
     78    return false;
     79  }
     80
     81  if (empty($userdata['email']))
     82  {
     83    array_push(
     84      $page['errors'],
     85      sprintf(
     86        l10n('User "%s" has no email address, password reset is not possible'),
     87        $userdata['username']
     88        )
     89      );
     90    return false;
     91  }
     92
     93  if (empty($userdata['activation_key']))
     94  {
     95    $activation_key = get_user_activation_key();
     96
     97    single_update(
     98      USER_INFOS_TABLE,
     99      array('activation_key' => $activation_key),
     100      array('user_id' => $user_id)
     101      );
     102
     103    $userdata['activation_key'] = $activation_key;
     104  }
     105
     106  set_make_full_url();
     107 
     108  $message = l10n('Someone requested that the password be reset for the following user account:') . "\r\n\r\n";
     109  $message.= sprintf(
     110    l10n('Username "%s" on gallery %s'),
     111    $userdata['username'],
     112    get_gallery_home_url()
     113    );
     114  $message.= "\r\n\r\n";
     115  $message.= l10n('To reset your password, visit the following address:') . "\r\n";
     116  $message.= get_gallery_home_url().'/password.php?key='.$userdata['activation_key']."\r\n\r\n";
     117  $message.= l10n('If this was a mistake, just ignore this email and nothing will happen.')."\r\n";
     118
     119  unset_make_full_url();
     120
     121  $message = trigger_event('render_lost_password_mail_content', $message);
     122
     123  $email_params = array(
     124    'subject' => '['.$conf['gallery_title'].'] '.l10n('Password Reset'),
     125    'content' => $message,
     126    'email_format' => 'text/plain',
     127    );
     128
     129  if (pwg_mail($userdata['email'], $email_params))
     130  {
     131    array_push($page['infos'], l10n('Check your email for the confirmation link'));
     132    return true;
     133  }
     134  else
     135  {
     136    array_push($page['errors'], l10n('Error sending email'));
     137    return false;
     138  }
     139}
     140
     141/**
     142 *  checks the activation key: does it match the expected pattern? is it
     143 *  linked to a user? is this user allowed to reset his password?
     144 *
     145 * @return mixed (user_id if OK, false otherwise)
     146 */
     147function check_password_reset_key($key)
     148{
     149  global $page;
     150 
     151  if (!preg_match('/^[a-z0-9]{20}$/i', $key))
     152  {
     153    array_push($page['errors'], l10n('Invalid key'));
     154    return false;
     155  }
     156
     157  $query = '
     158SELECT
     159    user_id,
     160    status
     161  FROM '.USER_INFOS_TABLE.'
     162  WHERE activation_key = \''.$key.'\'
     163;';
     164  $result = pwg_query($query);
     165
     166  if (pwg_db_num_rows($result) == 0)
     167  {
     168    array_push($page['errors'], l10n('Invalid key'));
     169    return false;
     170  }
     171 
     172  $userdata = pwg_db_fetch_assoc($result);
     173
     174  if (is_a_guest($userdata['status']) or is_generic($userdata['status']))
     175  {
     176    array_push($page['errors'], l10n('Password reset is not allowed for this user'));
     177    return false;
     178  }
     179
     180  return $userdata['user_id'];
     181}
     182
     183/**
     184 * checks the passwords, checks that user is allowed to reset his password,
     185 * update password, fills $page['errors'] and $page['infos'].
     186 *
     187 * @return bool (true if password was reset, false otherwise)
     188 */
     189function reset_password()
     190{
     191  global $page, $user, $conf;
     192
     193  if ($_POST['use_new_pwd'] != $_POST['passwordConf'])
     194  {
     195    array_push($page['errors'], l10n('The passwords do not match'));
     196    return false;
     197  }
     198
     199  if (isset($_GET['key']))
     200  {
     201    $user_id = check_password_reset_key($_GET['key']);
     202    if (!is_numeric($user_id))
     203    {
     204      array_push($page['errors'], l10n('Invalid key'));
     205      return false;
     206    }
     207  }
     208  else
     209  {
     210    // we check the currently logged in user
     211    if (is_a_guest() or is_generic())
     212    {
     213      array_push($page['errors'], l10n('Password reset is not allowed for this user'));
     214      return false;
     215    }
     216
     217    $user_id = $user['id'];
     218  }
     219   
     220  single_update(
     221    USERS_TABLE,
     222    array($conf['user_fields']['password'] => $conf['pass_convert']($_POST['use_new_pwd'])),
     223    array($conf['user_fields']['id'] => $user_id)
     224    );
     225
     226  array_push($page['infos'], l10n('Your password has been reset'));
     227
     228  if (isset($_GET['key']))
     229  {
     230    array_push($page['infos'], '<a href="'.get_root_url().'identification.php">'.l10n('Login').'</a>');
     231  }
     232  else
     233  {
     234    array_push($page['infos'], '<a href="'.get_gallery_home_url().'">'.l10n('Return to home page').'</a>');
     235  }
     236
     237  return true;
     238}
     239
     240// +-----------------------------------------------------------------------+
     241// | Process form                                                          |
    39242// +-----------------------------------------------------------------------+
    40243
     
    44247if (isset($_POST['submit']))
    45248{
    46   $mailto =
    47     '<a href="mailto:'.get_webmaster_mail_address().'">'
    48     .l10n('Contact webmaster')
    49     .'</a>'
    50     ;
    51 
    52   if (isset($_POST['no_mail_address']) and $_POST['no_mail_address'] == 1)
    53   {
    54     array_push($page['infos'], l10n('Email address is missing. Please specify an email address.'));
    55     array_push($page['infos'], $mailto);
    56   }
    57   else if (isset($_POST['mail_address']) and !empty($_POST['mail_address']))
    58   {
    59     $mail_address = pwg_db_real_escape_string($_POST['mail_address']);
    60    
    61     $query = '
    62 SELECT '.$conf['user_fields']['id'].' AS id
    63      , '.$conf['user_fields']['username'].' AS username
    64      , '.$conf['user_fields']['email'].' AS email
    65 FROM '.USERS_TABLE.' as u
    66   INNER JOIN '.USER_INFOS_TABLE.' AS ui
    67       ON u.'.$conf['user_fields']['id'].' = ui.user_id
    68 WHERE '.$conf['user_fields']['email'].' = \''.$mail_address.'\'
    69   AND ui.status = \'normal\'
    70 ;';
    71     $result = pwg_query($query);
    72 
    73     if (pwg_db_num_rows($result) > 0)
    74     {
    75       $error_on_mail = false;
    76       $datas = array();
    77      
    78       while ($row = pwg_db_fetch_assoc($result))
    79       {
    80         $new_password = generate_key(6);
    81 
    82         $infos =
    83           l10n('Username').': '.stripslashes($row['username'])
    84           ."\n".l10n('Password').': '.$new_password
    85           ;
    86 
    87         $infos = trigger_event('render_lost_password_mail_content', $infos);
    88 
    89         if (pwg_mail($row['email'],
    90               array('subject' => l10n('password updated'), 'content' => $infos)))
    91         {
    92           $data =
    93             array(
    94               $conf['user_fields']['id']
    95               => $row['id'],
    96              
    97               $conf['user_fields']['password']
    98               => $conf['pass_convert']($new_password)
    99               );
    100 
    101           array_push($datas, $data);
    102         }
    103         else
    104         {
    105           $error_on_mail = true;
    106         }
    107       }
    108      
    109       if ($error_on_mail)
    110       {
    111         array_push($page['errors'], l10n('Error sending email'));
    112         array_push($page['errors'], $mailto);
    113       }
    114       else
    115       {
    116         include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
    117         mass_updates(
    118           USERS_TABLE,
    119           array(
    120             'primary' => array($conf['user_fields']['id']),
    121             'update' => array($conf['user_fields']['password'])
    122           ),
    123           $datas
    124           );
    125 
    126         array_push($page['infos'], l10n('New password sent by email'));
    127       }
    128     }
    129     else
    130     {
    131       array_push($page['errors'], l10n('No classic user matches this email address'));
    132       array_push($page['errors'], l10n('Administrator, webmaster and special user cannot use this method'));
    133       array_push($page['errors'], $mailto);
    134     }
    135   }
    136 }
    137 
    138 // +-----------------------------------------------------------------------+
    139 // |                        template initialization                        |
    140 // +-----------------------------------------------------------------------+
    141 
    142 $title = l10n('Forgot your password?');
     249  check_pwg_token();
     250 
     251  if ('lost' == $_GET['action'])
     252  {
     253    if (process_password_request())
     254    {
     255      $page['action'] = 'none';
     256    }
     257  }
     258
     259  if ('reset' == $_GET['action'])
     260  {
     261    if (reset_password())
     262    {
     263      $page['action'] = 'none';
     264    }
     265  }
     266}
     267
     268// +-----------------------------------------------------------------------+
     269// | key and action                                                        |
     270// +-----------------------------------------------------------------------+
     271
     272// a connected user can't reset the password from a mail
     273if (isset($_GET['key']) and !is_a_guest())
     274{
     275  unset($_GET['key']);
     276}
     277
     278if (isset($_GET['key']))
     279{
     280  $user_id = check_password_reset_key($_GET['key']);
     281  if (is_numeric($user_id))
     282  {
     283    $userdata = getuserdata($user_id, false);
     284    $page['username'] = $userdata['username'];
     285    $template->assign('key', $_GET['key']);
     286
     287    if (!isset($page['action']))
     288    {
     289      $page['action'] = 'reset';
     290    }
     291  }
     292  else
     293  {
     294    $page['action'] = 'none';
     295  }
     296}
     297
     298if (!isset($page['action']))
     299{
     300  if (!isset($_GET['action']))
     301  {
     302    $page['action'] = 'lost';
     303  }
     304  elseif (in_array($_GET['action'], array('lost', 'reset', 'none')))
     305  {
     306    $page['action'] = $_GET['action'];
     307  }
     308}
     309
     310if ('reset' == $page['action'] and !isset($_GET['key']) and (is_a_guest() or is_generic()))
     311{
     312  redirect(get_gallery_home_url());
     313}
     314
     315if ('lost' == $page['action'] and !is_a_guest())
     316{
     317  redirect(get_gallery_home_url());
     318}
     319
     320// +-----------------------------------------------------------------------+
     321// | template initialization                                               |
     322// +-----------------------------------------------------------------------+
     323
     324$title = l10n('Reset Password');
     325if ('lost' == $page['action'])
     326{
     327  $title = l10n('Forgot your password?');
     328
     329  if (isset($_POST['username_or_email']))
     330  {
     331    $template->assign('username_or_email', stripslashes($_POST['username_or_email']));
     332  }
     333}
     334
    143335$page['body_id'] = 'thePasswordPage';
    144336
    145337$template->set_filenames(array('password'=>'password.tpl'));
    146 $template->assign( array(
    147     'F_ACTION'=> get_root_url().'password.php'
     338$template->assign(
     339  array(
     340    'title' => $title,
     341    'form_action'=> get_root_url().'password.php',
     342    'action' => $page['action'],
     343    'username' => isset($page['username']) ? $page['username'] : $user['username'],
     344    'PWG_TOKEN' => get_pwg_token(),
    148345    )
    149346  );
     347
    150348// +-----------------------------------------------------------------------+
    151349// |                        infos & errors display                         |
    152350// +-----------------------------------------------------------------------+
     351
    153352$template->assign('errors', $page['errors']);
    154353$template->assign('infos', $page['infos']);
     
    164363// |                           html code display                           |
    165364// +-----------------------------------------------------------------------+
     365
    166366include(PHPWG_ROOT_PATH.'include/page_header.php');
    167367$template->pparse('password');
Note: See TracChangeset for help on using the changeset viewer.