আজকাল অনেক ওয়েব অ্যাপ্লিকেশনে পাবলিকলি অ্যাক্সেসযোগ্য পেজ (হোমপেজ) থাকে এবং আরও সিকিউর পেজ যেখানে লগইন করতে হয়। ইউজার রেজিস্ট্রেশন, লগইন, সেশন স্টেট ট্র্যাকিং — এটাকে 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 আসবে।
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
এখন অনঅথরাইজড ইউজার রিডাইরেক্ট হবে রিলেভেন্ট ফ্ল্যাশ মেসেজসহ।
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 হিসেবে এডিট করার চেষ্টা করলে এরর আসবে:
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 নিউজলেটার সাবস্ক্রাইব করুন!