[D7] Custom forgot password process by reset user password - Viết forgot password với chức năng reset password

Post Reply
tthlan
Quản trị viên
Posts: 76
Joined: Tue Aug 23, 2016 8:13 am

[D7] Custom forgot password process by reset user password - Viết forgot password với chức năng reset password

Post by tthlan »

Trước khi custom thì ta phải làm rõ Drupal đang chạy như thế nào.

Hai giao diện chính của 'Quên mật khẩu'

Image
Giao diện chưa login

Image
Giao diện đã login


Khi submit Email hoặc UserName hợp lệ và tồn tại



Check mail đã đăng ký sẽ nhận được link thông báo có dạng
.../user/reset/5/1463456492/MeSxXL-kyIIhc8BWP60g7a_dtuYsmSzOOA4qtKt1Sgo

Image


Khi mở link đó ra sẽ thấy một trang thông báo kèm một nút login một lần trực tiếp (không phải nhập thông tin đăng nhập như thường lệ)
Image


Khi đã vào bên trong hệ thống để chỉnh sửa lại password (không cần cung cấp lại password cũ). Sau khi thay đổi password thì quá trinh lấy lại password coi như hoàn tất.
Image


Chú ý khi đã nhấn vào nút login trực tiếp này thì link vừa cung cấp qua email sẽ ko còn hiệu dụng. Bạn sẽ nhận được thông về chuyện này.

Ở trên là Cơ chể Forgot Password Drupal cho phép người dùng với active link dùng 1 lần để vào hệ thống và đổi password.

Nếu bạn muốn Dùng lại Forgot Password của Drupal cho phép người dùng với active link dùng 1 lần sẽ reset password gởi qua email thì hãy theo dõi tiếp nhé ...

Chúc vui !
tthlan
Quản trị viên
Posts: 76
Joined: Tue Aug 23, 2016 8:13 am

Re: [D7] Custom forgot password process by reset user password - Viết forgot password với chức năng reset password

Post by tthlan »

Tiếp tục ta sẽ dựng lại 'Forgot password' với xử lý cho auto reset password như sau.

- Đầu tiên ta sẽ build form, các input controlers nó sẽ y hệt function user_pass() trong .\modules\user\user.pages.inc

- Tiếp theo là validate khi submit form, cũng sẽ y hệt function user_pass_validate() trong .\modules\user\user.pages.inc

- Sẽ có thay đổi trong submit form được dựng lại
p/s: code nguyên thủy trong function user_pass_submit() trong .\modules\user\user.pages.inc

node 1: Code mới sẽ được custom bằng function reset_password_content là function để tạo nội dung mail kích hoạt 'Forgot password' để mail có link kích hoạt khác đi, sau đó và dùng function drupal_mail để gởi mail. Để dùng được drupal_mail, ta phải tạo hook_mail và truyền các thông số cần thiết đúng quy định thì cơ chế mail mới gởi đi được nếu không việc gởi thất bại!

note 2: code nguyên thủy Drupal sẽ gọi _user_mail_notify() và trong đó bao gồm cả tạo nội dung mail và gởi mail kích hoạt có
link /user/reset/uid/time_stamp/user_pass_hash

Code: Select all

function form_forgot_form_submit($form, &$form_state) {
	
	$account = $form_state ['values'] ['account'];
	$to = $form_state['values']['account']->mail;
	
  $email_content = reset_password_content($form_state);
  $params = array('body' => $email_content);
  $key = 'active_password';
  
  $from = variable_get('site_mail', ini_get('sendmail_from')); //$form_state['values']['from_email'];
	
	//var_dump('ss_reset_password', $key, $to, language_default(), $params, $from);die;
  $mail = drupal_mail('ss_password', $key, $to, language_default(), $params, $from);
  	
	if (! empty ( $mail )) {
		watchdog ( 'user', 'Password reset request instructions mailed to %name at %email.', array (
				'%name' => $account->name,
				'%email' => $account->mail 
		) );
		drupal_set_message ( t ( 'Further instructions have been sent to your e-mail address.' ) );
	}
		
	$form_state ['redirect'] = 'member_login';
	
	return;
}
function reset_password_content

link kích hoạt sẽ dùng trong mail sẽ là /activepass/reset/uid/time_stamp/user_pass_hash
node : user_pass_rehash function của Drupal sẽ tạo ra một giá trị hash để sử dụng trong URL mỗi người sử dụng phụ thuộc thời gian

Code: Select all

function reset_password_content($form_state)
{
	$timestamp = REQUEST_TIME;
	$account = $form_state['values']['account'];
	$email_to = $form_state['values']['account']->mail;
	$user_name = $form_state['values']['account']->name;
  	
	$one_time_login_url = url("activepass/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE));
	$body = t("$user_name,

A request to reset the password for your account has been made at tthlan.info.

You may now log in by clicking this link or copying and pasting it to your browser:

$one_time_login_url

This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.

--  tthlan.info team");

return $body;
}

tham khảo thêm function user_pass_rehash () trong ./modules/user.module

Code: Select all

/**
 * Creates a unique hash value for use in time-dependent per-user URLs.
 *
 * This hash is normally used to build a unique and secure URL that is sent to
 * the user by email for purposes such as resetting the user's password. In
 * order to validate the URL, the same hash can be generated again, from the
 * same information, and compared to the hash value from the URL. The URL
 * normally contains both the time stamp and the numeric user ID. The login
 * timestamp and hashed password are retrieved from the database as necessary.
 * For a usage example, see user_cancel_url() and user_cancel_confirm().
 *
 * @param string $password
 *   The hashed user account password value.
 * @param int $timestamp
 *   A UNIX timestamp, typically REQUEST_TIME.
 * @param int $login
 *   The UNIX timestamp of the user's last login.
 * @param int $uid
 *   The user ID of the user account.
 *
 * @return
 *   A string that is safe for use in URLs and SQL statements.
 */
function user_pass_rehash($password, $timestamp, $login, $uid)
function ss_password_mail

hook_mail ss_password, để thiết lập subject và body.
Để dùng chung cho gởi kích hoạc và gởi mail auto reset mới cho client.
Nên ở đây switch trên key để thiết lập riêng biệt cho từng trường hợp.

Code: Select all

/**
 * Implements hook_mail()
 * @param 
 *   type $key to decide which email body to sent on basis of key parameter incense of multiple email content
 * @param 
 *   type $message the email content to be sent.Message array contains 'subject and body ' for the email.
 * @param 
 *   type $params using to get the cusotm email content from a function.This can be used in my other ways aslo as per need.
 */
function ss_password_mail($key, &$message, $params) {
  $language = $message['language'];
	
	//var_dump($key, $message, $params) ;die;
	
  switch ($key) {
		//switching on $key lets you create variations of the email based on the $key parameter
    case 'active_password':
      $message['subject'] = t('Active Reset Password Request');
			//the email body is here, inside the $message array
      $message['body'][] = $params['body'];
      break;
		case 'reset_password':
      $message['subject'] = t('New Password Login');
			//the email body is here, inside the $message array
      $message['body'][] = $params['body'];
      break;
  }
}

- Để link click trên mail , ta phải tạo hook_menu để drupal hiểu activepass link

node : hook_menu sẽ gọi function ss_user_pass_reset

Code: Select all

$items['activepass/reset/%/%/%'] = array(
    'title' => 'Reset password',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('ss_user_pass_reset', 2, 3,4),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );

function ss_user_pass_reset

Hàm gần giống với function nguyên thủy user_pass_reset trong .\modules\user\user.pages.inc
Build lại function ss_user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL)
khi client click nhấp link active forgot pass ở phần trước, system sẽ redirect client tới page để client click vào nút submit một lần duy nhất để
sau đó system sẽ gởi trực tiếp new auto generate new password có độ dài 8 ký tự tới email của user và auto login cho client sau khi submit.

note 1: trả về một cấu trúc form khi vào activepass link

note 2: vẫn dùng ss_password hook_mail với key 'reset_password' để gởi email với password mới

note 3: sau khi đối chiếu user_pass_hash và timestamp hợp lệ, user_login_finalize là hàm tự động login và cập nhật timestamp trên database để việc xác nhận trên form active forgot password link chỉ dùng 1 lần.

node 4: trường hợp clien vào page của active link lần đầu mà ko nhấn nút submit xác nhận thì link đó vẫn dùng được trong 24h

node 5:
tạo ngẫu nhiên password
$edit['pass'] = user_password(8);
cập nhật password cho account user
$account = user_save($account, $edit);

Code: Select all

/**
 * Menu callback; process one time login link and redirects to the user page on success.
 */
function ss_user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
	global $user;
	
	// When processing the one-time login link, we have to make sure that a user
	// isn't already logged in.
	if ($user->uid) {		
		// The existing user is already logged in.
		if ($user->uid == $uid) {
			//....
		} 		// A different user is already logged in on the computer.
		else {
			//....
		}
		drupal_goto ( 'user_settings' );
	} else {
		
		// Time out, in seconds, until login URL expires. Defaults to 24 hours =
		// 86400 seconds.
		$timeout = variable_get ( 'user_password_reset_timeout', 86400 );
		$current = REQUEST_TIME;
		// Some redundant checks for extra security ?
		$users = user_load_multiple ( array (
				$uid 
		), array (
				'status' => '1' 
		) );
		if ($timestamp <= $current && $account = reset ( $users )) {
			// 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 link that has expired. Please request a new one using the form below.' ), 'error' );
				drupal_goto ( 'user_settings' );
			} elseif ($account->uid && $timestamp >= $account->login && $timestamp <= $current && $hashed_pass == user_pass_rehash ( $account->pass, $timestamp, $account->login, $account->uid )) {
				// First stage is a confirmation form, then login								
				if ($action == 'active') { // active reset password
					// Set the new user.
					$user = $account;
					// user_login_finalize() also updates the login timestamp of the
					// user, which invalidates further use of the one-time login link.
					user_login_finalize ();
					watchdog ( 'user', 'User %name used one-time login link at time %timestamp.', array (
							'%name' => $account->name,
							'%timestamp' => $timestamp 
					) );

					// Generate auto password for active password request									
					$edit['pass'] = user_password(8);
					$account = user_save($account, $edit);
					
					// Email new generate password
					$to = $user->mail ;					
					$email_content = new_password_content($account->name, $edit['pass']);
					$params = array('body' => $email_content);
					$key = 'reset_password';
					
					$from = variable_get('site_mail', ini_get('sendmail_from')); //$form_state['values']['from_email'];
					
					//var_dump('ss_reset_password', $key, $to, language_default(), $params, $from);die;
					$mail = drupal_mail('ss_password', $key, $to, language_default(), $params, $from);
						
					if (! empty ( $mail )) {
						watchdog ( 'user', 'A new Password mailed to %name at %email.', array (
								'%name' => $user->name,
								'%email' => $user->mail 
						) );
						drupal_set_message (  t ( 'You are logged in as ' . $account->name . '. A new password have been sent to your e-mail address.' ) );
					}
					
					drupal_goto ( 'user_settings' );
				} else {
					$form ['message'] = array (
							'#markup' => t ( '<p>This is a one-time login for %user_name and will expire on %expiration_date.</p>'.
									'<p>Click on this button to active your password request once time.</p>', array (
									'%user_name' => $account->name,
									'%expiration_date' => format_date ( $timestamp + $timeout ) 
							) ) 
					);
					$form ['help'] = array (
							'#markup' => '<p>' . t ( 'This active link can not be used after click this button.' ) . '</p>' 
					);
					$form ['actions'] = array (
							'#type' => 'actions' 
					);
					$form ['actions'] ['submit'] = array (
							'#type' => 'submit',
							'#value' => t ( 'Submit' ) 
					);
					$form ['#action'] = url ( "activepass/reset/$uid/$timestamp/$hashed_pass/active" );
					$form['#attributes']['class'] = 'tac'; // update action attribute
					return $form;
				}				
			} else {				
				//...
			}
		} else {
			// Deny access, no more clues.
			// Everything will be in the watchdog's URL for the administrator to check.
			drupal_access_denied ();
			drupal_exit ();
		}
	}
}
tthlan
Quản trị viên
Posts: 76
Joined: Tue Aug 23, 2016 8:13 am

Re: [D7] Custom forgot password process by reset user password - Viết forgot password với chức năng reset password

Post by tthlan »

Kết Quả là đây
Image


Image
Post Reply