بررسی دسترسی هر کاربر و ارزیابی عملکرد آن با استفاده از روش توسعه TDD در لاراول

تعریف دسترسی‌ها و اطمینان از حصول دسترسی هر کاربر به اطلاعاتی که دارد جزو بخش‌های حیاتی هر سرویس است. شما بعنوان توسعه‌دهنده سمت بک باید بتوانید دسترسی هر کاربر رو بدرستی تعریف کنید و از نشر اطلاعات آن جلوگیری کنید. در این بین ارزیابی عملکرد اپلیکیشنی که توسعه می‌دهید جزو ضروریات کار هست. در این آموزش قصد داریم با توجه به پروژه‌ای که در این دوره توسعه دادیم، دسترسی کاربران برای ایجاد وظایف رو با کمک روش توسعه مبتنی بر تست TDD بررسی کنیم و از صحت دسترسی هر کاربر به وظایفی که برای خود تعریف کرده اطمینان پیدا کنیم.


این آموزش در ادامه مقاله قبلی که امکان احراز هویت کاربر با پکیج بسیار سبک Sanctum از لاراول بر روی یک بستر API رو فراهم می‌کرد، منتشر شده و پیشنهاد می‌کنم چنانچه با این مفهوم آشنایی ندارید حتما آن مقاله رو مطالعه کنید!

............................

خب بیایید شروع کنیم.

در ابتدا سراغ migration هامون میریم و به جدول وظایف یک ستون برای ارتباط هر وظیفه با کاربر در نظر می‌گیریم:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->string('description')->nullable();
    $table->unsignedBigInteger('user_id');
    $table->timestamps();
});

قطعا اگر الان تست مربوط به افزودن task جدید رو اجرا کنیم با خطای مقداردهی برای user_id مواجه خواهیم شد. برای این منظور به شیوه‌ مقداردهی با کمک توابع ارتباط همانطور که در آموزش زیر به آن اشاره شد عمل می‌کنیم:

اما ابتدا باید توابع ارتباط کاربران با وظایف رو تعریف کنیم:

// User model

public function tasks()
{
    return $this->hasMany(Task::class);
}


// Task model

public function user()
{
    return $this->belongsTo(User::class);
}

و در نهایت تابع store در کنترلر TaskController رو مشابه زیر ویرایش می‌کنیم:

public function store(TaskRequest $request)
{
    Auth::user()->tasks()->create($request->validated());
}

خب حالا یکبار دیگه تست افزودن وظیفه رو اجرا می‌کنیم:

Image for post
Image for post

دلیل این خطا لاگین نبودن کاربره! البته بهتره یک middleware برای حفاظت از مسیرهامون و ایجاد دسترسی تنها برای کاربران احراز هویت شده تعریف کنیم که sanctum براحتی این امکان در اختیار ما قرار داده. برای این منظور به شیوه زیر عمل کنید:

Route::resource('tasks', TaskController::class)->middleware('auth:sanctum');

خب با اجرای تست دوباره خطای معتبر نبودن کاربر رو دریافت می‌کنیم:

Image for post
Image for post

البته در ابتدا اگر شما هم مانند من نامی برای مسیر login تعریف نکرده باشید خطای تعریف نکردن نام مسیر رو خواهید داشت.  اما سوالی که مطرح هست اینه که چطور باید لاگین کرد و چطور باید درخواست احراز هویت شده در محیط تست ایجاد کنیم. برای این منظور هم دوتا روش وجود داره!

یکی از روش‌ها اینه که ابتدا پیش از درخواست ایجاد وظیفه، یک کاربر جدید رو لاگین کنیم و بعد توکنی که در پاسخ می‌گیریم رو در header درخواست جدیدمون قرار بدیم. اینکار به سادگی و با متد withHeader مانند زیر انجام میشه:

Image for post
Image for post

و اما روش دوم که بسیار ساده‌تره استفاده از خود کلاس Sanctum هست. روش کار هم سادست‌ تنها کافیه یک کاربر ایجاد کنید و با استفاده از متد actingAs درخواست‌های بعدی احراز هویت شده خواهند بود:

Image for post
Image for post

من این خط کد رو بر روی تمامی توابع اعمال کردم و نهایتا با اجرای تست کلی همانطور که در تصویر زیر هم مشاهده می‌کنید، و خروجی ارزیابی نشون میده سیستم تا اینجا مشکلی نداره و تست‌ها پاس شدند!

Image for post
Image for post

بیایید برای تکمیل پروژه دو متد show و index رو هم به کنترلر اضافه کنیم. ابتدا با متد show شروع می‌کنیم:

public function show(Task $task)
{
    return $this->success($task);
}

حالا به تست برمی‌گردم و برای ارزیابی عملکرد این تابع از تست زیر استفاده می‌کنم:

Image for post
Image for post

خروجی تست موفق هست ولی بیاید ببینم آیا در این کد دسترسی کاربر‌ رعایت شده، چرا که ما نمی‌خواهیم کاربرها به وظایف همدیگر دسترسی داشته باشند. برای این منظور در تست دیگری تلاش می‌کنم با کاربر جدیدی به task که توسط کاربر دیگری تعریف شده دسترسی پیدا کنم:

Image for post
Image for post

خب تست بالا زمانی که task پیدا نشه پاس میشه اما در اینجا ما خطا دریافت می‌کنیم چرا که کاربر جدید به وظیفه کاربر دیگر دسترسی داره. برای حل کردن این مشکل کافیه به کنترلر برگردیم و اینبار بدون قابلیت تزریق مدل و با استفاده از کلاس Auth اینکار رو همانند زیر انجام دهیم:

public function show($task_id)
{
    return $this->success(
    	Auth::user()->tasks()
    		->where('id', $task_id)
    		->firstOrFail()
    );
}

من در اینجا برای اطمینان از اینکه رکوردی که به کاربر پاس میدیم حتما برای خودش باشه و نتونه اطلاعات باقی کاربرا دسترسی داشته باشه از کلاس Auth استفاده کردم (البته برای این کلاس تابع کمکی ‍()auth هم وجود داره و می‌تونید از اون هم استفاده کنید). حالا اگه مجددا تست رو اجرا کنیم، خواهیم دید که بدون هیچ مشکلی ارزیابی ما موفق بوده و دسترسی هم رعایت شده. 

در انتها هم کد کامل مربوط به کنترلر  ‍TaskController و کلاس TaskTest رو می‌تونید در زیر مشاهده کنید:

و کد کلاس تست که ارزیابی مربوط به متد index هم به اون اضافه شده:


در این آموزش شما توانستید با استفاده از بسته نرم ‌افزاری Sanctum از لاراول دسترسی کاربران برای اطلاعات خودشان رو محدود و با استفاده از روش توسعه TDD از صحت عملکرد فرآیند‌ها اطمینان پیدا کنید.

امیدوارم از این آموزش لذت برده باشید!

همچنین می‌توانید لینک مربوط به دوره رو از طریق آدرس زیر در گیت‌هاب هم مشاهده کنید: