কম্পিউটার টিউটোরিয়াল

Ruby Authorization মাস্টার করুন: Pundit vs CanCanCan তুলনা

আজকাল অনেক ওয়েব অ্যাপ্লিকেশনে পাবলিকলি অ্যাক্সেসযোগ্য পেজ (হোমপেজ) থাকে এবং আরও সিকিউর পেজ যেখানে লগইন করতে হয়। ইউজার রেজিস্ট্রেশন, লগইন, সেশন স্টেট ট্র্যাকিং — এটাকে authentication বলে।

একই সাথে, লগড-ইন ইউজারদের জন্য অ্যাকশন এবং রিসোর্স আলাদা আলাদা করা লাগে তাদের রোল অনুযায়ী। যেমন "admins"-এর অ্যাক্সেস নরমাল ইউজারের চেয়ে বেশি। এই প্রসেসকে authorization বলে।

এই পোস্টে Ruby-এর দুটি সবচেয়ে জনপ্রিয় অথোরাইজেশন লাইব্রেরি Pundit এবং CanCanCan এক্সপ্লোর করব।

সেটআপ এবং পূর্বশর্ত

এই আর্টিকেলে একটি সিম্পল Rails 7 অ্যাপ ব্যবহার করব যাতে ইউজার এবং পোস্ট আছে। ইউজারকে "editor" বা "writer" রোল অ্যাসাইন করা থাকবে।

Pundit এবং CanCanCan-এ এক্সাম্পল অ্যাপের কোড রিপো থেকে চেক আউট করুন।

অথেনটিকেশন সেটআপ এই পোস্টের স্কোপ বাইরে, Devise gem ডকুমেন্টেশন ফলো করুন (Deviseকে অথোরাইজেশন gems-এর সাথে পেয়ার করব)।

গুরুত্বপূর্ণ: Pundit বা CanCanCan ইনস্টল করলেই ইউজার রোল অটোমেটিক হয় না। ম্যানুয়ালি সেটআপ করতে হয়।

User Roles ডিফাইন করুন

Devise ইনস্টল এবং User মডেল সেটআপ হওয়ার পর রোল ডিফাইন করুন:

  • Writer — নিজের পোস্ট create, edit, update, delete করতে পারবে। অন্য writer-এর পোস্ট view করতে পারবে।
  • Editor — যেকোনো ইউজারের পোস্ট edit, update, view, delete করতে পারবে, কিন্তু নিজের পোস্ট create করতে পারবে না।

User টেবিলে রোল কলাম যোগ করুন (মাইগ্রেশন):

$ rails g migration AddRoleToUsers role:string
$ rails db:migrate

User মডেলে রোল ইনক্লুড করুন:

enum role: { writer: 0, editor: 1 }

Post মডেল সেটআপ (title, body, user_id রেফারেন্স):

$ rails g scaffold Post title:string body:text user:references
$ rails db:migrate

Posts কন্ট্রোলারের create মেথডে user_id করেন্ট ইউজার সেট করুন। User মডেলে has_many :posts যোগ করুন।

ডেটাবেস সিড করুন ভিন্ন রোলের ইউজার দিয়ে।

Pundit দিয়ে Ruby App-এ Authorization

Pundit হল একটি অথোরাইজেশন লাইব্রেরি যা অবজেক্ট-ওরিয়েন্টেড আর্কিটেকচার এবং প্লেইন Ruby ক্লাসের ওপর বিল্ট। এটি একটি স olid অথোরাইজেশন লেয়ার দেয় যা অ্যাপের সাথে স্কেল করতে পারে।

Pundit ইনস্টলেশন

# Gemfile
gem 'pundit'

$ bundle install
# অথবা
$ bundle add pundit

Application Controller-এ Pundit-এর অথোরাইজেশন মডিউল যোগ করুন:

include Pundit::Authorization

বেস পলিসি ক্লাস জেনারেট করুন (যে বাকি পলিসি ইনহেরিট করবে):

$ rails g pundit:policy

Pundit Policies কনফিগার করুন

Pundit-এ "policy" হল একটি প্লেইন Ruby ক্লাস যেখানে ইউজার রোলের রিসোর্স অ্যাক্সেস রুল ডিফাইন করা হয়।

বিশেষতা:

  • পলিসি নাম: মডেলের নাম + "Policy" (যেমন PostPolicy)
  • attr_reader: user (current_user) এবং মডেল (যেমন post)
  • কোয়েরি মেথড যা কন্ট্রোলার মেথডে ম্যাপ করে (app/policies ফোল্ডারে রাখা ভালো)

Writer রোলের জন্য পলিসি ডিফাইন

Writer রোলের Post রিসোর্স অ্যাক্সেস:

  • নিজের পোস্ট Create করা
  • নিজের পোস্ট Edit, Update করা
  • নিজের এবং অন্যের পোস্ট View করা
  • নিজের পোস্ট Delete করা
$ rails g pundit:policy Post

জেনারেটেড পলিসি ক্লাসে Writer রোলের রুল যোগ করুন (বেস পলিসি থেকে ইনহেরিট করবে):

class PostPolicy < ApplicationPolicy
  def create?
    user.writer?
  end

  def update?
    user.writer? && record.user == user
  end

  def destroy?
    user.writer? && record.user == user
  end

  def show?
    true # সবাই দেখতে পারবে
  end
end

Pundit-এ Policy ব্যবহার

কন্ট্রোলারের মেথডে Pundit-এর authorize মেথড কল করুন:

def create
  @post = Post.new(post_params)
  authorize @post
  # ...
end

Editor হিসেবে লগইন করে পোস্ট create করলে NotAuthorizedError আসবে।

Ruby Authorization মাস্টার করুন: Pundit vs CanCanCan তুলনা

Pundit-এর NotAuthorizedError রেসকিউ করুন

ApplicationController-এ এডিট করুন:

rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

private

def user_not_authorized
  flash[:alert] = "You are not authorized to perform this action."
  redirect_to(request.referrer || root_path)
end

এখন অনঅথরাইজড ইউজার রিডাইরেক্ট হবে রিলেভেন্ট ফ্ল্যাশ মেসেজসহ।

Ruby Authorization মাস্টার করুন: Pundit vs CanCanCan তুলনা

Pundit Scopes: ফাইন-গ্রেইন্ড পারমিশন

Pundit scopes ActiveRecord scopes-এর মত — কিন্তু রিসোর্স অ্যাক্সেস ম্যানেজ করে আপনার সেট করা রুল অনুযায়ী।

উদাহরণ: Editorরা "draft" স্ট্যাটাসের পোস্ট ভিউ/এডিট করতে পারবে, Writerরা শুধু নিজের পোস্টে create/view/edit/update/delete করতে পারবে।

PostPolicy-এ scope ডিফাইন করুন:

class PostPolicy::Scope < Scope
  def resolve
    if user.editor?
      scope.where(status: :draft)
    else
      scope.where(user: user)
    end
  end
end

Posts কন্ট্রোলারে অথরাইজ:

def index
  @posts = policy_scope(Post)
end

Pundit with Rails Strong Parameters

Pundit অথোরাইজেশন রুল + Rails Strong Parameters = লকডাউন অ্যাক্সেস রিসোর্স অ্যাট্রিবিউটে।

উদাহরণ: শুধু Editorরা Post-এর excerpt ফিল্ড অ্যাক্সেস করতে পারবে।

Policy-এ ব্লক যোগ করুন:

def permitted_attributes
  if user.editor?
    [:title, :body, :excerpt]
  else
    [:title, :body]
  end
end

Controller-এ permitted params মডিফাই করুন:

def post_params
  params.require(:post).permit(policy(@post).permitted_attributes)
end

CanCanCan পরিচিতি

CanCanCan একটি "ability" ক্লাস ব্যবহার করে ডিফাইন করে কারা কী এক্সেস করবে। একচুইট অথোরাইজেশন মডিউল এবং ভিউ হেল্পার দিয়ে অ্যাক্সেস কন্ট্রোল করা হয়।

CanCanCan ইনস্টলেশন

$ bundle add cancancan

Ability ক্লাস জেনারেট করুন:

$ rails g cancan:ability

CanCanCan Abilities ডিফাইন এবং চেক করুন

একই ইউজার রোল: Writer এবং Editor।

Ability ক্লাসে অ্যাক্সেস রুল ডিফাইন করুন:

class Ability
  include CanCan::Ability

  def initialize(user)
    return unless user.present?

    if user.writer?
      can :create, Post
      can [:read, :update, :destroy], Post, user_id: user.id
      can :read, Post
    elsif user.editor?
      can [:read, :update, :destroy], Post
      cannot :create, Post
    end
  end
end

Controller-এ অ্যাক্সেস চেক করুন (edit অ্যাকশন):

def edit
  @post = Post.find(params[:id])
  authorize! :edit, @post
end

অন্য Writer হিসেবে এডিট করার চেষ্টা করলে এরর আসবে:

Ruby Authorization মাস্টার করুন: Pundit vs CanCanCan তুলনা

CanCanCan AccessDenied এরর হ্যান্ডলিং

ApplicationController-এ মডিফাই করুন:

rescue_from CanCan::AccessDenied do |exception|
  redirect_to root_url, alert: exception.message
end

এরর মেসেজ কাস্টমাইজ করাও যায়। XML রেসপন্স সার্ভ করলে বা ডিপার্ট যেতে চাইলে CanCanCan ডকুমেন্টেশন দেখুন।

Multiple CanCanCan Abilities কম্বাইন করুন

একই রিসোর্সের জন্য মাল্টিপল অ্যাক্সেস রুল ability ক্লাসে ডিফাইন করা যায়। Writer এবং Editor রোল কম্বাইন করলে:

can :create, Post if user.writer?
can [:read, :update, :destroy], Post if user.writer? || user.editor?
cannot :create, Post if user.editor?

সব রুল এক ফাইলে থাকার সুবিধা: সব রুল এক জায়গায়। কিন্তু অনেক রোল/রিসোর্স হলে ability ক্লাস বড় ও কমপ্লেক্স হয়। মেথড ডেফিনিশন দিয়ে রি-অর্গানাইজ করা যায়।

Feature Comparison: Pundit vs CanCanCan

  • File organization: Pundit-এ মাল্টিপল পলিসি ফাইল, CanCanCan-এ একটু ability ফাইল (মাল্টিপল ability ফাইল সম্ভব কিন্তু ডিফল্ট নয়)
  • Tests: CanCanCan-এ পারমিশন কোড এক ক্লাসে থাকায় টেস্ট লিখা সহজ
  • Helpers: CanCanCan-এ can? মেথড, Pundit-এ policy হেল্পার
  • Devise integration: দুটোই Devise-সহ ভালো ইন্টিগ্রেট হয়

উপসংহার

Ruby on Rails ইকোসিস্টেমের দুটি জনপ্রিয় অথোরাইজেশন gem: Pundit এবং CanCanCan। দুটোই জটিল পারমিশন সেটআপ হ্যান্ডেল করতে পারে। কোনটি বেছে নেব সেটা আপনার অ্যাপের প্রয়োজনের ওপর নির্ভর করে। দুটোই ট্রাই করে দেখুন কোনটি ফিট করে।

Happy coding!

P.S. Ruby Magic পোস্টগুলি প্রকাশ होते সাথে সাথে পেতে আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন!

  1. অ্যান্ড্রয়েডে সফট কীবোর্ড প্রোগ্রাম্যাটিক্যালি লুকানো

  2. ডেটা স্ট্রাকচরে ইন্টারভাল ট্রি

  3. অ্যান্ড্রয়েড WebView-এ ক্যাশে (Cache) থেকে লোড করার পদ্ধতি

  4. Tkinter ব্যবহার করে ইমেজ PDF-তে কনভার্ট করা