আজকের লেকচারে স্বাগতম!

কোথায় আছি আমরা? আপনি বর্তমানে Section 25: Identity-তে আছেন। গত লেকচারে আমরা ReturnUrl নিয়ে কাজ করেছিলাম, যার মাধ্যমে লগইন করার পর ইউজারকে তার কাঙ্ক্ষিত পেজে রিডাইরেক্ট করা হয়। আজকে আমরা একটি অত্যন্ত গুরুত্বপূর্ণ টপিক শিখবো—User Roles। একটি অ্যাপ্লিকেশনে কীভাবে সাধারণ User এবং Administrator আলাদা করতে হয় এবং তাদের ডেটা কীভাবে ডাটাবেসে সেভ হয়, তা আজ আমরা দেখবো। আগামী লেকচারে আমরা “Areas” নিয়ে কাজ করবো যেখানে Admin-দের জন্য আলাদা পেজ তৈরি করা হবে। চলুন শুরু করি!


📝 Quick Summary for Revision

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে লিস্ট করা হলো:

  • User Roles Concept: অ্যাপ্লিকেশনে ভিন্ন ভিন্ন ইউজারের (যেমন: Admin, Customer, Employee) অ্যাক্সেস কন্ট্রোল আলাদা করার জন্য Role ব্যবহার করা হয়।
  • Enum Creation: User, Admin ইত্যাদি টাইপগুলো নির্দিষ্ট করার জন্য UserTypeOptions নামে একটি Enum তৈরি করা।
  • Register UI Update: রেজিস্ট্রেশন ফর্মে Radio buttons যুক্ত করে ইউজারকে তার Role সিলেক্ট করার অপশন দেওয়া।
  • RoleManager: Identity-তে Role তৈরি এবং ম্যানেজ করার জন্য RoleManager<ApplicationRole> ইনজেক্ট করা।
  • Role Tables: ডাটাবেসে দুটি প্রধান টেবিল কাজ করে— AspNetRoles (যেখানে সব Role-এর লিস্ট থাকে) এবং AspNetUserRoles (যেখানে ইউজার এবং রোলের ম্যাপিং থাকে)।
  • Assigning Role: _roleManager.RoleExistsAsync() (বা FindByNameAsync) দিয়ে Role আছে কিনা চেক করা, না থাকলে তৈরি করা এবং সবশেষে _userManager.AddToRoleAsync() দিয়ে ইউজারকে সেই রোলে যুক্ত করা।

🧠 Comprehensive Breakdown

এখানে লেকচারের প্রতিটি বিষয় বিস্তারিতভাবে এবং ধাপে ধাপে ব্যাখ্যা করা হলো:

১. Concept of User Roles (Priority: 9/10)

যেকোনো বড় Web Application-এ সব ইউজারের পাওয়ার বা অ্যাক্সেস সমান থাকে না।

  • Why do we need this? ধরুন একটি ই-কমার্স সাইটে সাধারণ Customer শুধু প্রোডাক্ট কিনতে পারে, কিন্তু Administrator প্রোডাক্ট অ্যাড করতে বা ডিলিট করতে পারে। এই পারমিশনগুলো আলাদা করার জন্যই User Roles (যেমন: Admin, Manager, User) তৈরি করতে হয়।

২. Creating Enum and Updating DTO (Priority: 8/10)

ইউজার কোন টাইপের হবে, সেটি হার্ডকোড না করে একটি Enum তৈরি করা সবচেয়ে ভালো উপায়।

💻 Code Implementation: প্রথমে Core প্রজেক্টের Enums ফোল্ডারে UserTypeOptions.cs তৈরি করুন:

public enum UserTypeOptions
{
    User,
    Admin
}
 

এরপর RegisterDTO-তে এই Enum-এর একটি প্রপার্টি যুক্ত করুন এবং ডিফল্ট ভ্যালু হিসেবে User সেট করে দিন:

public UserTypeOptions UserType { get; set; } = UserTypeOptions.User;
 

৩. Updating the Register UI (Priority: 8/10)

View-তে ইউজারকে টাইপ সিলেক্ট করার সুযোগ দিতে হবে। এর জন্য Register.cshtml ফাইলে Submit বাটনের ঠিক আগে Radio buttons যুক্ত করতে হবে।

💻 Code Implementation:

<div class="form-group">
    <label>User Type</label>
    <div>
        <input type="radio" asp-for="UserType" value="User" id="User" checked />
        <label for="User">User</label>
        
        <input type="radio" asp-for="UserType" value="Admin" id="Admin" />
        <label for="Admin">Admin</label>
    </div>
</div>
 

৪. Understanding Role Tables in Database (Priority: 10/10)

Identity ডাটাবেসে Role ম্যানেজ করার জন্য মূলত দুটি টেবিল কাজ করে:

  1. AspNetRoles: এই টেবিলে শুধু Role-এর নামগুলো থাকে। যেমন: একটি Row-তে “Admin”, আরেকটিতে “User”। (Columns: Id, Name, NormalizedName ইত্যাদি)।
  2. AspNetUserRoles: এটি একটি Many-to-Many ম্যাপিং টেবিল। এখানে কোনো নতুন কলাম থাকে না, শুধু UserId এবং RoleId থাকে। অর্থাৎ, “X নামের ইউজারটি Y নামের রোলের অধীনে আছে” — এই সম্পর্কটি এখানে স্টোর হয়। একজন ইউজার একাধিক রোলে থাকতে পারে, আবার একটি রোলে অনেক ইউজার থাকতে পারে।

৫. Injecting RoleManager (Priority: 10/10)

যেমনভাবে ইউজারদের ম্যানেজ করার জন্য UserManager লাগে, ঠিক তেমনি Role চেক করা বা তৈরি করার জন্য RoleManager ইনজেক্ট করতে হয়।

💻 Code Implementation (AccountController.cs):

private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly RoleManager<ApplicationRole> _roleManager; // নতুন ফিল্ড
 
public AccountController(
    UserManager<ApplicationUser> userManager, 
    SignInManager<ApplicationUser> signInManager,
    RoleManager<ApplicationRole> roleManager) // কনস্ট্রাক্টরে ইনজেকশন
{
    _userManager = userManager;
    _signInManager = signInManager;
    _roleManager = roleManager;
}
 

৬. Writing the Role Assignment Logic (Priority: 10/10)

এখন POST Register মেথডে যখন ইউজার ডাটাবেসে সফলভাবে সেভ হবে (_userManager.CreateAsync এর পর), তখন আমাদের চেক করতে হবে সে কোন Role সিলেক্ট করেছে।

💻 Code Implementation (Inside Register POST Method):

if (result.Succeeded)
{
    // ১. চেক করা হচ্ছে ইউজার কোন Role সিলেক্ট করেছে (Admin নাকি User)
    if (registerDTO.UserType == Core.Enums.UserTypeOptions.Admin)
    {
        // ২. চেক করা হচ্ছে "Admin" Role টি ডাটাবেসে (AspNetRoles) আগে থেকেই আছে কিনা
        if (await _roleManager.FindByNameAsync(Core.Enums.UserTypeOptions.Admin.ToString()) == null)
        {
            // ৩. না থাকলে নতুন একটি Role তৈরি করা হচ্ছে
            ApplicationRole applicationRole = new ApplicationRole() 
            { 
                Name = Core.Enums.UserTypeOptions.Admin.ToString() 
            };
            await _roleManager.CreateAsync(applicationRole);
        }
 
        // ৪. ইউজারকে "Admin" Role-এ অ্যাসাইন করা হচ্ছে (AspNetUserRoles টেবিলে ডেটা যাবে)
        await _userManager.AddToRoleAsync(user, Core.Enums.UserTypeOptions.Admin.ToString());
    }
    else
    {
        // যদি সে সাধারণ User হয়, তবে তাকে "User" Role-এ দেওয়া হচ্ছে
        if (await _roleManager.FindByNameAsync(Core.Enums.UserTypeOptions.User.ToString()) == null)
        {
            ApplicationRole applicationRole = new ApplicationRole() 
            { 
                Name = Core.Enums.UserTypeOptions.User.ToString() 
            };
            await _roleManager.CreateAsync(applicationRole);
        }
        await _userManager.AddToRoleAsync(user, Core.Enums.UserTypeOptions.User.ToString());
    }
 
    // এরপর সাইন-ইন করানো...
    await _signInManager.SignInAsync(user, isPersistent: false);
    return RedirectToAction(nameof(Index), "Persons");
}
 

🌟 Best Practices & Smarter Updates (.NET 10 Context)

  • CRITICAL Design Flaw in the Lecture (Checking Roles on Registration): লেকচারার প্রতিটি ইউজার রেজিস্ট্রেশনের সময় চেক করছেন যে Role ডাটাবেসে আছে কিনা (FindByNameAsync) এবং না থাকলে বানাচ্ছেন। এটি ডাটাবেসের ওপর অযথা প্রেশার ফেলে। Best Practice হলো “Role Seeding”। অর্থাৎ, অ্যাপ্লিকেশন যখন রান হবে (Startup/Program.cs-এ), তখনই চেক করে সবগুলো প্রয়োজনীয় Role (Admin, User, Manager) ডাটাবেসে ইনসার্ট করে রাখা উচিত। তাহলে রেজিস্ট্রেশনের সময় শুধু _userManager.AddToRoleAsync() কল করলেই হবে, Role আছে কিনা তা চেক করার দরকার পড়বে না।
  • Use Static Strings instead of Enums for Roles: লেকচারার Enum ব্যবহার করে .ToString() কল করেছেন। Identity-তে Role-এর নাম সাধারণত স্ট্রিং হিসেবেই বেশি ব্যবহৃত হয়। মডার্ন আর্কিটেকচারে Enum এর বদলে static const string সংবলিত একটি Roles ক্লাস তৈরি করা হয় (যেমন: public const string Admin = "Admin";)। এতে কোড আরও ফাস্ট এবং রিডেবল হয়।
  • Security Concern: সাধারণ রেজিস্ট্রেশন ফর্মে “Admin” হওয়ার অপশন সবার জন্য উন্মুক্ত রাখাটা খুবই রিস্কি। রিয়েল-ওয়ার্ল্ড প্রজেক্টে প্রথম ইউজার (সুপার অ্যাডমিন) ম্যানুয়ালি তৈরি করা হয়, এবং পরবর্তীতে শুধুমাত্র একজন অ্যাডমিনই নতুন অ্যাডমিন তৈরি করার অ্যাক্সেস পান। সাধারণ রেজিস্ট্রেশন ফর্ম দিয়ে সবসময় ডিফল্টভাবে User রোলই দেওয়া উচিত।

অসাধারণ! আপনি এখন জানেন ডাটাবেসে কীভাবে Role কাজ করে এবং ইউজারদের আলাদা আলাদা পারমিশন গ্রুপে কীভাবে ভাগ করতে হয়। পরবর্তী লেকচারে আমরা Areas নিয়ে কাজ করবো, যেখানে Admin-দের জন্য সম্পূর্ণ আলাদা একটি ড্যাশবোর্ড বা সেকশন তৈরি করা হবে!