Skip to content

অধ্যায় ৯: নিরাপত্তা - মিডলওয়্যার এবং পলিসি

এখন পর্যন্ত আমাদের প্যাকেজটি কার্যকরী, কিন্তু সুরক্ষিত নয়। যে কোনো লগইন করা ব্যবহারকারী যে কোনো ইনভয়েস দেখতে বা পরিবর্তন করতে পারছে, যা একটি বড় নিরাপত্তা ঝুঁকি। এই অধ্যায়ে আমরা আমাদের প্যাকেজকে সুরক্ষিত করার জন্য লারাভেলের দুটি শক্তিশালী ফিচার—মিডলওয়্যার (Middleware) এবং পলিসি (Policies)—ব্যবহার করব।

৯.১ লক্ষ্য: প্যাকেজের কার্যকারিতা সুরক্ষিত করা

আমাদের লক্ষ্য হলো:

  1. মিডলওয়্যার ব্যবহার করে নির্দিষ্ট রাউট গ্রুপকে সুরক্ষিত করা (যেমন, শুধুমাত্র অ্যাডমিনরা নির্দিষ্ট পেজ দেখতে পারবে)।
  2. পলিসি ব্যবহার করে ডেটার ওপর সূক্ষ্ম নিয়ন্ত্রণ প্রতিষ্ঠা করা (যেমন, একজন ব্যবহারকারী শুধুমাত্র তার নিজের তৈরি ইনভয়েস দেখতে বা এডিট করতে পারবে)।

৯.২ Step 1: মিডলওয়্যার দিয়ে রাউট সুরক্ষিত করা

মিডলওয়্যার হলো একটি ফিল্টার যা HTTP রিকোয়েস্ট আপনার অ্যাপ্লিকেশন পর্যন্ত পৌঁছানোর আগে বা পরে কাজ করে। আমরা একটি মিডলওয়্যার তৈরি করব যা চেক করবে ব্যবহারকারী অ্যাডমিন কিনা।

১. মিডলওয়্যার ক্লাস তৈরি করুন:src/Http/Middleware ডিরেক্টরি এবং তার ভেতরে EnsureIsAdmin.php ফাইল তৈরি করুন।

bash
mkdir -p src/Http/Middleware
touch src/Http/Middleware/EnsureIsAdmin.php

EnsureIsAdmin.php ফাইলে নিচের কোডটি লিখুন:

php
<?php

namespace DevMaster\InvoiceBuilder\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureIsAdmin
{
    public function handle(Request $request, Closure $next)
    {
        // আমরা সহজভাবে চেক করছি ব্যবহারকারীর ইমেইল অ্যাডমিন ইমেইল কিনা।
        // এই ইমেইলটি কনফিগ ফাইল থেকে আসবে।
        $adminEmail = config('invoice-builder.admin_email');

        if (! $request->user() || $request->user()->email !== $adminEmail) {
            abort(403, 'Unauthorized action.');
        }

        return $next($request);
    }
}

২. কনফিগারেশন আপডেট করুন:config/invoice-builder.php ফাইলে অ্যাডমিন ইমেইলের জন্য একটি এন্ট্রি যোগ করুন:

php
<?php
// config/invoice-builder.php
return [
    // ... other config
    'admin_email' => 'admin@example.com',
];

৩. মিডলওয়্যার রেজিস্টার করুন: প্যাকেজের মিডলওয়্যার লারাভেলকে চেনানোর জন্য আমাদের সার্ভিস প্রোভাইডারে একটি "এলিয়াস" বা ডাকনাম রেজিস্টার করতে হবে।

InvoiceBuilderServiceProvider-এর boot() মেথডে নিচের কোড যোগ করুন:

php
// InvoiceBuilderServiceProvider.php -> boot() method
use Illuminate\Routing\Router;

// ...

$router = $this->app->make(Router::class);
$router->aliasMiddleware('invoice.admin', \DevMaster\InvoiceBuilder\Http\Middleware\EnsureIsAdmin::class);

এখন আমরা invoice.admin নামটি ব্যবহার করে আমাদের মিডলওয়্যারটি রাউটে প্রয়োগ করতে পারব।

৪. রাউটে মিডলওয়্যার প্রয়োগ করুন:routes/web.php ফাইলে একটি নতুন অ্যাডমিন রাউট গ্রুপ তৈরি করুন এবং সেখানে মিডলওয়্যারটি ব্যবহার করুন:

php
// routes/web.php
Route::group(['prefix' => 'admin', 'middleware' => ['web', 'auth', 'invoice.admin']], function () {
    Route::get('/invoices', function () {
        return 'Welcome Admin! This is the list of all invoices.';
    });
});

যাচাইকরণ:

  • আপনার config/invoice-builder.php ফাইলে admin_email-এর মান পরিবর্তন করে এমন একটি ইমেইল দিন যা আপনার লগইন করা ব্যবহারকারীর ইমেইল নয়।
  • এবার /admin/invoices URL-এ যাওয়ার চেষ্টা করুন। আপনি একটি 403 Unauthorized পেজ দেখতে পাবেন।
  • এখন কনফিগ ফাইলে আপনার সঠিক ইমেইলটি দিন এবং আবার চেষ্টা করুন। আপনি সফলভাবে পেজটি দেখতে পাবেন।

৯.৩ Step 2: পলিসি দিয়ে ডেটা সুরক্ষিত করা

মিডলওয়্যার পুরো রাউটকে সুরক্ষিত করে। কিন্তু যদি আমাদের আরও সূক্ষ্ম নিয়ন্ত্রণের প্রয়োজন হয়? যেমন, একজন ব্যবহারকারী GET /invoices/1 এবং GET /invoices/2 দুটি URL-ই অ্যাক্সেস করতে পারে, কিন্তু আমরা নিশ্চিত করতে চাই যে সে শুধুমাত্র তার নিজের ইনভয়েসটিই দেখতে পাবে। এই কাজটি করা হয় পলিসি দিয়ে।

১. ডাটাবেস এবং মডেল আপডেট করুন: প্রথমে, আমাদের invoices টেবিলের সাথে ব্যবহারকারীর একটি সম্পর্ক তৈরি করতে হবে।

  • database/migrations/create_invoices_table.php.stub ফাইলটি আপডেট করে user_id কলাম যোগ করুন:
php
// ... inside Schema::create
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('number')->unique();
// ...
  • Invoice মডেলে রিলেশনশিপ ডিফাইন করুন:
php
// src/Models/Invoice.php
public function user()
{
    return $this->belongsTo(\App\Models\User::class);
}
  • InvoiceController-এর store মেথডটি আপডেট করে user_id সেভ করুন:
php
// InvoiceController.php -> store()
Invoice::create([
    'user_id' => auth()->id(), // বর্তমান ব্যবহারকারীর আইডি
    'number' => 'INV-'.uniqid(),
    'total' => rand(100, 1000),
    'status' => 'draft',
]);

নোট: ডাটাবেস স্কিমা পরিবর্তন করার পর, আপনাকে php artisan migrate:fresh কমান্ড চালাতে হতে পারে।

২. পলিসি ক্লাস তৈরি করুন:src/Policies ডিরেক্টরি এবং InvoicePolicy.php ফাইল তৈরি করুন।

bash
mkdir -p src/Policies
touch src/Policies/InvoicePolicy.php

InvoicePolicy.php ফাইলে নিচের কোডটি লিখুন:

php
<?php

namespace DevMaster\InvoiceBuilder\Policies;

use App\Models\User; // হোস্ট অ্যাপের User মডেল
use DevMaster\InvoiceBuilder\Models\Invoice;

class InvoicePolicy
{
    /**
     * Determine whether the user can view the model.
     */
    public function view(User $user, Invoice $invoice): bool
    {
        return $user->id === $invoice->user_id;
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(User $user, Invoice $invoice): bool
    {
        return $user->id === $invoice->user_id;
    }
}

এই পলিসি বলছে যে, একজন ব্যবহারকারী একটি ইনভয়েস শুধুমাত্র তখনই দেখতে বা আপডেট করতে পারবে যদি সে ওই ইনভয়েসের মালিক হয় (user_id মিলে যায়)।

৩. পলিসি রেজিস্টার করুন:InvoiceBuilderServiceProvider-এর boot() মেথডে Gate ব্যবহার করে আমাদের পলিসিটি রেজিস্টার করুন:

php
// InvoiceBuilderServiceProvider.php -> boot() method
use Illuminate\Support\Facades\Gate;
use DevMaster\InvoiceBuilder\Models\Invoice;
use DevMaster\InvoiceBuilder\Policies\InvoicePolicy;

// ...

Gate::policy(Invoice::class, InvoicePolicy::class);

এটি লারাভেলকে বলে দেয় যে, Invoice মডেল সম্পর্কিত যেকোনো অথরাইজেশন চেকের জন্য যেন InvoicePolicy ক্লাসটি ব্যবহার করা হয়।

৪. কন্ট্রোলারে পলিসি ব্যবহার করুন: এখন আমরা কন্ট্রোলারে $this->authorize() মেথড ব্যবহার করে পলিসি চেক করব।

InvoiceController-এ একটি show মেথড যোগ করুন:

php
// InvoiceController.php
use DevMaster\InvoiceBuilder\Models\Invoice;

// ... index() and store() methods

public function show(Invoice $invoice)
{
    // পলিসি চেক: ব্যবহারকারীর কি এই ইনভয়েসটি দেখার অনুমতি আছে?
    $this->authorize('view', $invoice);

    return "Viewing Invoice: {$invoice->number} which belongs to user: {$invoice->user_id}";
}

এবং routes/web.php-তে এর জন্য একটি রাউট যোগ করুন:

php
// routes/web.php
Route::get('/{invoice}', [InvoiceController::class, 'show'])->name('invoices.show');

যাচাইকরণ:

  • দুটি ভিন্ন ব্যবহারকারী দিয়ে লগইন করুন।
  • User A দিয়ে একটি ইনভয়েস তৈরি করুন। ডাটাবেসে এর id এবং user_id নোট করুন।
  • এখন User B হিসেবে লগইন করে User A-এর ইনভয়েসের URL-এ (/invoices/{id}) যাওয়ার চেষ্টা করুন। আপনি একটি 403 THIS ACTION IS UNAUTHORIZED পেজ দেখতে পাবেন।
  • User A দিয়ে আবার লগইন করে একই URL-এ যান। আপনি সফলভাবে ইনভয়েসের তথ্য দেখতে পাবেন।

আপনি সফলভাবে আপনার প্যাকেজে মিডলওয়্যার এবং পলিসি ব্যবহার করে একটি শক্তিশালী নিরাপত্তা স্তর যুক্ত করেছেন।

পরবর্তী অধ্যায়ে আমরা আমাদের প্যাকেজের নির্ভরযোগ্যতা নিশ্চিত করার জন্য অটোমেটেড টেস্ট লেখা শিখব।

সৎ ক্রেডিট: লেখক AI, সম্পাদক আবুল হাসান