---
title: "رؤية المشكلة بعيون عملائكم عبر انتحال هوية العميل (Customer Impersonation)"
description: "أي موقع يقدّم دعمًا للعملاء سيواجه في النهاية مشكلة يصعب على العميل شرحها، أو يصعب فهمها دون رؤية ما يراه. ومع pretender gem يمكننا تسجيل الدخول إلى حساب المستخدم ورؤية ما يراه بالضبط."
canonical: https://oozou.com/ar/blog/seeing-through-your-customers-eyes-with-customer-impersonation-54
locale: ar
date: 2020-07-29
author: "OOZOU Team"
---

# رؤية المشكلة بعيون عملائكم عبر انتحال هوية العميل (Customer Impersonation)

> أي موقع يقدّم دعمًا للعملاء سيواجه في النهاية مشكلة يصعب على العميل شرحها، أو يصعب فهمها دون رؤية ما يراه. ومع pretender gem يمكننا تسجيل الدخول إلى حساب المستخدم ورؤية ما يراه بالضبط.

Published: 2020-07-29 · Author: OOZOU Team (Engineering) · 6 min read

طلب الدعم لحل مشكلة تواجهونها كعملاء تجربة مرهقة وبطيئة في الغالب، ويزداد الأمر سوءًا حين يكون البريد الإلكتروني الوسيلة الوحيدة للتواصل مع الدعم. ويمكننا تحسين هذه التجربة بتقليل الجهد الذي يبذله العميل لشرح مشكلته بالكامل. فحين لا يستطيع موظفو الدعم رؤية المشكلة التي يواجهها المستخدم، عليهم جمع كمّ كبير من المعلومات واستيضاح كل تفصيل صغير للتأكد من أنهم فهموا العميل بشكل صحيح. ويمكننا تقليل المعلومات المطلوبة بمنح موظفي الدعم القدرة على تسجيل الدخول إلى حساب العميل.
في هذا المقال سأستخدم [pretender](https://github.com/ankane/pretender) لأتيح لموظفي الدعم تسجيل الدخول باسم مستخدمين آخرين. يعمل Pretender مع أي نظام مصادقة، وسأستخدم هنا [devise](https://github.com/heartcombo/devise)

# الدخول في وضع انتحال الهوية

أولًا لنتأكد من تعريف جميع الـ gems في ملف Gemfile:

```ruby
gem 'devise'
gem 'pundit'
gem 'pretender'
```

ونحتاج أيضًا إلى إضافة مقتطف إلى كل controller نريد أن يتمكن موظفو دعم العملاء من استخدامه أثناء وضع انتحال الهوية. لنضفه إلى ApplicationController حتى نتمكن من انتحال الهوية في كل أجزاء التطبيق:

```ruby
class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  impersonates :user
end
```

نحتاج إلى controller يتيح لموظفي الدعم الدخول في وضع انتحال هوية العميل والخروج منه:

```ruby
class CustomerImpersonationsController < ApplicationController
  before_action :authenticate_customer_support_agent!

  def create
    user = User.find!(params[:id])
    impersonate_user(user)
    redirect_to root_path
  end

  def destroy
    # current_user is the impersonated user before we call
    # stop_impersonating_user so we should build the notice here or store the
    # current_user in a variable so we can fetch values from it later
    notice = "You are logged out as #{current_user.email}"
    stop_impersonating_user
    redirect_to root_path, notice: notice
  end

  private def authenticate_customer_support_agent!
    # If we're already impersonating a user, true_user will be the customer
    # support agent
    #
    # If we're not currently impersonating a user, current_user will be the
    # customer support agent
    return if (true_user || current_user)&.customer_support_agent?

    raise ActionController::RoutingError, 'Not Found'
  end
end
```

لا تنسوا إضافة الـ routes:

```ruby
Rails.application.routes.draw do
  resource :customer_impersonation, only: %i[create destroy]
end
```

نحتاج الآن إلى إضافة نموذج يسجّل منه المسؤولون الدخول، ويمكننا فعل ذلك بطريقتين.
1. نوفّر لموظف دعم العملاء نموذجًا يختار منه المستخدم ويرسله.
2. ننشئ زرًا يستخدم طريقة POST لإرسال معرّف المستخدم.

معظم التطبيقات التي عملت عليها تحتوي على صفحة إدارة تعرض جميع المستخدمين المسجلين في جدول مقسّم إلى صفحات، لذا سنختار الخيار 2. وتنفيذه بسيط جدًا بفضل method الجاهزة link_to في Ruby on Rails، إذ يمكننا أن نطلب منها استخدام طريقة POST ثم نمرر معرّف المستخدم إلى المسار.

```erb
<table>
  <thead>
    <tr>
      <td>Name</td>
      <td>Email</td>
      <td></td>
    </tr>
  </thead>
  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.email %></td>
        <td>
          <%= link_to('Show', user) %>
          <%= link_to(
            'Impersonate',
            customer_impersonation_path(id: user.id),
            method: :post
          ) %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>
```

إذا كانت لديكم عدة أدوار إدارية، فربما تفكرون في إنشاء pundit policy للتأكد من أن المسؤولين أصحاب الأدوار الصحيحة وحدهم يستطيعون انتحال هوية المستخدمين الآخرين.
والآن بعد أن أصبح بإمكاننا انتحال هوية مستخدم، نحتاج أيضًا إلى القدرة على إيقاف ذلك. لدينا بالفعل الـ route والـ action في الـ controller، وبقي أن ننشئ رابطًا يتيح لمنتحلي الهوية تسجيل الخروج فعلًا. وعلينا أيضًا إزالة رابط تسجيل الخروج العادي، وإلا سيُسجَّل خروج المسؤول من حسابه هو. ولواجهة سلسة، يمكننا ببساطة استبدال رابط تسجيل الخروج الحالي برابط إيقاف انتحال الهوية الجديد.

```ruby
<% if user_signed_in?  %>
  <%= link_to(
    'Log out',
    true_user ? customer_impersonation_path : destroy_user_session_path,
    method: :delete
  ) %>
<% end %>
```

# اعتبارات تخص واجهة المستخدم

هناك أمور أنصح بها بشدة لحماية عملائكم من الإجراءات غير المقصودة التي قد يتخذها موظفو الدعم.
على الأرجح يتوقع عملاؤكم الخصوصية، لذا قد ترغبون في تبنّي سياسة تشترط طلب إذنهم قبل السماح للموظفين بالدخول إلى حساباتهم. ويمكنكم فعل ذلك عبر المحادثة أو البريد الإلكتروني، أو إنشاء نظام لطلب الإذن يُسجَّل داخل التطبيق.
وأنصح بشدة أيضًا بتسجيل كل نشاط يجري أثناء انتحال الهوية. فإنشاء سجل تدقيق (audit trail) طريقة جيدة لحماية أنفسكم من الخطأ الذي سيقع حتمًا في المستقبل. ومع سجل التدقيق ستتمكنون من التحقق من الإجراء الصحيح الواجب اتخاذه حين يثير العميل المشكلة. وفي تطبيقاتنا نذهب أبعد من ذلك، فنسجّل كل طلب يرسله موظف دعم العملاء. لنضف ذلك إلى ApplicationController

```bash
bin/rails generate model AuditLog data:jsonb
```
```ruby
class AuditLog < ApplicationModel
  belongs_to :user
end
```
```ruby
class User
  has_many :audit_logs
end
```
```ruby
class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  after_action :log_impersonated_activity, if: :true_user
  impersonates :user

  private def log_impersonated_activity
    return unless true_user

    true_user.audit_logs.create!(
      data: {
        controller: params[:controller],
        action: params[:action],
        method: request.method,
        headers: headers,
        params: params,
        customer_id: current_user.id,
        customer_email: current_user.email,
        admin_id: true_user.id,
        admin_email: true_user.email
      }
    )
  end
end
```

وأنصح أيضًا بتقييد الإجراءات التي يستطيع موظفو دعم العملاء اتخاذها. نستخدم pundit عادة للتفويض (authorization)، لكن للأسف لم يُصمَّم pundit ليتعامل مع كائنَي مستخدم اثنين. ويمكننا إعادة تعريف pundit_user لنخبر pundit أين يجد كائن المستخدم، أي أن نمرر hash يحتوي على current_user وtrue_user، ثم نحدّث ApplicationPolicy ليعكس هذا التغيير.

```ruby
class ApplicationController
  private def pundit_user
    { current_user: current_user, true_user: true_user }
  end
end
```
```ruby
class ApplicationPolicy
  attr_reader :user, :true_user, :record

  def initialize(current_and_true_user, record)
    @user = current_and_true_user[:current_user]
    @true_user = current_and_true_user[:true_user]
    @record = record
  end

  protected def impersonating_customer?
    user.present? && user != true_user
  end
end
```

والآن يمكنكم في الـ policies الأخرى منح صلاحيات مختلفة أثناء وضع انتحال الهوية، كمنع موظفي دعم العملاء من إجراء المدفوعات مثلًا

```ruby
class PaymentPolicy < ApplicationPolicy
  def create?
    !impersonating_customer?
  end
end
```

# 

وآخر ما أراه مهمًا هو أن يكون واضحًا تمامًا أن موظف الدعم في وضع انتحال الهوية، مع إضافة بعض الحماية الإضافية من الإجراءات غير المقصودة.
أولًا يمكننا إضافة شريط تحذير إلى الصفحة حين يكون موظف دعم العملاء في وضع انتحال الهوية

```erb
<% if true_user.present? %>
  <div class='banner danger warning'>
    You are signed in as <%= current_user.name %>.
    <%= link_to 'Stop impersonating', cutomer_impersonations_path(id: current_user.id), method: :delete %>
  </div>
<% end %>
```

ثم يمكننا إضافة رسائل تأكيد إلى كل الإجراءات المهمة:

```erb
<%= f.submit 'Save', data: { confirm: "Are you sure you want to save this item for #{current_user.name}'s account" } %>
```

وأنصح بتعطيل أي إجراءات يجب حمايتها:

```erb
<% if true_user %>
  <span class='disabled_impersonation'>Link account</span>
<% else %>
  <%= link_to 'Link account', link_account_path %>
<% end %>
```

وبهذا نختتم هذا الدليل. لقد تناولنا كل ما نحتاجه لنتيح لموظفي دعم العملاء تسجيل الدخول إلى حسابات عملائنا.

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
