वेब एप्लिकेशन की एक आम आवश्यकता विशिष्ट भूमिकाओं और अनुमतियों को असाइन करने की क्षमता है।
कई प्रकार के वेब एप्लिकेशन प्रतिबंधित पहुंच प्रदान करने में एडमिन और नियमित उपयोगकर्ताओं के बीच अंतर करते हैं। यह अक्सर एक साधारण बूलियन का उपयोग करके किया जाता है जो यह निर्धारित करता है कि उपयोगकर्ता एडमिन है या नहीं। हालांकि, भूमिकाएं और अनुमतियां बहुत अधिक जटिल हो सकती हैं।
आपके एप्लिकेशन का मूल्य कुछ डेटा और एक्शन तक पहुंच को प्रतिबंधित करने में निहित है। यह निश्चित रूप से कुछ ऐसा है जिसे आप गड़बड़ नहीं करना चाहते। इस पोस्ट में, हम बताएंगे कि बेसिक Ruby on Rails एप्लिकेशन में भूमिकाओं और अनुमतियों को कैसे लागू किया जाए।
क्या मुझे अनुमतियों को प्रबंधित करने के लिए जेम की आवश्यकता है?
नहीं, आपको जेम की आवश्यकता नहीं है, विशेष रूप से यदि आपका एप्लिकेशन छोटा है और आप अपने कोड बेस में अधिक निर्भरताएं जोड़ने से बचना चाहते हैं। हालांकि, यदि आप विकल्पों की तलाश कर रहे हैं, तो यहां सबसे लोकप्रिय जेम्स हैं जो भूमिकाओं और अनुमतियों से निपटते हैं:
Devise: Devise प्रमाणीकरण और भूमिका प्रबंधन के लिए एक जेम है, और यह वास्तव में एक जटिल और मजबूत समाधान है। GitHub पर 21.7k स्टार्स के साथ, यह इस पोस्ट में सबसे लोकप्रिय रेपो है, लेकिन यह भूमिका प्रबंधन से अधिक करता है। इसे प्रमाणीकरण समाधान के रूप में जाना जाता है, इसलिए इसे अपने कोडबेस पर केवल तभी लागू करें यदि आपको बहुत मजबूत लाइब्रेरी की आवश्यकता है।
Pundit: Pundit एक जेम है जो सरल Ruby ऑब्जेक्ट्स का उपयोग करता है, और यह शायद सबसे सरल पॉलिसी जेम है जिसे हम कवर करेंगे। उपयोग करना सरल है, इसमें न्यूनतम प्राधिकरण है, और यह शुद्ध Ruby के उपयोग के समान है। GitHub पर 7.3k स्टार्स के साथ, यह वर्तमान में सबसे लोकप्रिय पॉलिसी जेम है।
CanCan: CanCan एक प्राधिकरण लाइब्रेरी है जो उन संसाधनों को प्रतिबंधित करती है जिन्हें एक दिया गया उपयोगकर्ता एक्सेस करने की अनुमति है। हालांकि, CanCan को वर्षों से छोड़ दिया गया है और केवल Rails 3 और पहले के रिलीज़ के साथ काम करता है।
CanCanCan: CanCanCan Ruby और Ruby on Rails के लिए एक और प्राधिकरण लाइब्रेरी है। यह CanCan का विकल्प है और वर्तमान में बनाए रखा जा रहा है। GitHub पर 4.9k स्टार्स के साथ, यह सबसे कम लोकप्रिय है, लेकिन यह बहुत अच्छी तरह से काम करता है और अच्छी तरह से बनाए रखा जाता है।
ये सभी जेम्स महान हैं, लेकिन शुद्ध Ruby में स्वयं अनुमतियों का निर्माण करना बहुत कठिन नहीं है। मैं आपको पॉलिसी ऑब्जेक्ट पैटर्न नामक एक रणनीति का उपयोग करके जेम के बिना अनुमतियों का प्रबंधन करना दिखाऊंगा।
पॉलिसी ऑब्जेक्ट पैटर्न
पॉलिसी ऑब्जेक्ट एक डिज़ाइन पैटर्न है जिसका उपयोग अनुमतियों और भूमिकाओं से निपटने के लिए किया जाता है। आप इसका उपयोग हर बार कर सकते हैं जब आपको यह जांचना हो कि कोई चीज़ या कोई व्यक्ति किसी एक्शन को करने की अनुमति है या नहीं। यह जटिल व्यावसायिक नियमों को एनकैप्सुलेट करता है और इसे अलग नियमों वाले अन्य पॉलिसी ऑब्जेक्ट्स द्वारा आसानी से बदला जा सकता है। सभी बाहरी निर्भरताओं को पॉलिसी ऑब्जेक्ट में इंजेक्ट किया जाता है, जो परमिशन चेक लॉजिक को एनकैप्सुलेट करता है, जिसके परिणामस्वरूप एक क्लीन कंट्रोलर और मॉडल होता है। Pundit, Cancan, और Cancancan जैसे जेम्स इस पैटर्न को लागू करते हैं।
शुद्ध पॉलिसी ऑब्जेक्ट नियम
- रिटर्न एक बूलियन मान होना चाहिए
- लॉजिक सरल होना चाहिए
- मेथड के अंदर, हमें केवल पास किए गए ऑब्जेक्ट्स पर मेथड्स कॉल करने चाहिए
कार्यान्वयन
नामकरण कन्वेंशन से शुरू करते हैं; फाइलनाम में _policy सफ़िक्स लागू होता है और क्लास और पॉलिसी अंत में। इस मेथड में, नाम हमेशा ? कैरेक्टर से समाप्त होते हैं (जैसे, UsersPolicy#allowed?)।
यहाँ कुछ उदाहरण कोड है:
class UsersPolicy
def initialize(user)
@user = user
end
def allowed?
admin? || editor?
end
def editor?
@user.where(editor: true)
end
def admin?
@user.where(admin: true)
end
end
किस परिदृश्य में इनका उपयोग करना चाहिए?
जब आपके ऐप में एक से अधिक प्रकार की प्रतिबंधित पहुंच और प्रतिबंधित एक्शन हों। उदाहरण के लिए, पोस्ट्स को निम्न के साथ बनाया जा सकता है:
- कम से कम एक टैग,
- एक प्रतिबंध कि केवल एडमिन और एडिटर उन्हें बना सकते हैं, और
- एक आवश्यकता कि एडिटर्स को वेरिफाइड होना चाहिए।
यहाँ पॉलिसी ऑब्जेक्ट के बिना एक उदाहरण कंट्रोलर है:
class PostsController < ApplicationController
def create
if @post.tag_ids.size > 0 && (current_user.role == 'admin' || (current_user.role == 'editor' && current_user.verified_email))
# create
end
end
end
क्योंकि उपरोक्त कंडीशन जांच लंबी, बदसूरत, और अपठनीय है, पॉलिसी ऑब्जेक्ट पैटर्न लागू किया जाना चाहिए।
आइए PostsCreationPolicy बनाकर शुरू करें।
class PostsCreationPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def self.create?(user, post)
new(user, post).create?
end
def create?
with_tags? && author_is_allowed?
end
private
def with_tags?
post.tag_ids.size > 0
end
def author_is_allowed?
is_admin? || editor_is_verified?
end
def is_admin?
user.role == 'admin'
end
def editor_is_verified?
user.role == 'editor' && user.verified_email
end
end
पॉलिसी ऑब्जेक्ट के साथ हमारा कंट्रोलर ऐसा दिखता है:
class PostsController < ApplicationController
def create
if PostsCreationPolicy.create?(current_user, @post)
# create
end
end
end
Rails में पॉलिसी ऑब्जेक्ट्स का उपयोग कैसे करें
app के अंदर एक पॉलिसी डायरेक्टरी बनाएं /policies और अपनी सभी पॉलिसी क्लासेस वहाँ रखें। जब आपको कंट्रोलर में कॉल करने की आवश्यकता हो, तो आप सीधे एक एक्शन में या before_action का उपयोग करके कर सकते हैं:
class PostsController < ApplicationController
before_action :authorized?, only: [:edit, :create, :update, :destroy]
def authorized?
unless ::PostsCreationPolicy.create?(current_user, @post)
render :file => "public/404.html", :status => :unauthorized
end
end
end
पॉलिसी ऑब्जेक्ट्स को कैसे टेस्ट करें
कंट्रोलर में व्यवहार का टेस्ट करना सरल है:
require 'rails_helper'
RSpec.describe "/posts", type: :request do
describe "when user is not allowed" do
let(:user_not_allowed) { create(:user, admin: false, editor: false) }
let(:tag) { create(:tag) }
let(:valid_attributes) { attributes_for(:post, tag_id: tag.id) }
before do
sign_in user_not_allowed
end
describe "GET /index" do
it "return code 401" do
diet = Post.create! valid_attributes
get edit_post_url(post)
expect(response).to have_http_status(401)
end
end
end
end
पॉलिसी का टेस्ट करना भी सरल है; हमारे पास बहुत सारे छोटे मेथड्स हैं जिनकी केवल एक जिम्मेदारी है।
require 'rails_helper'
RSpec.describe PostsCreationPolicy do
describe "when user is not allowed" do
let(:user) { create(:user, editor: false, admin: false) }
let(:user_editor) { create(:user, editor: true, email: verified) }
let(:tag) { create(:tag) }
let(:post) { create(:post, tag_id: tag.id) }
describe ".create?" do
context "when user is allowed" do
it "creates a new post" do
expect(described_class.create?(user_editor, post)).to eq(true)
end
end
context "when user is not allowed" do
it "does not create a new post" do
expected(described_class.create?(user, post)).to eq(false)
end
end
end
# ...अधिक टेस्ट केस
end
end
हम टेस्ट करते हैं कि ऑब्जेक्ट को हर परिदृश्य में बनाने की अनुमति है या नहीं।
निष्कर्ष
पॉलिसी पैटर्न अवधारणा छोटी है लेकिन बड़े परिणाम देती है। हर बार जब आपको सरल या जटिल अनुमतियों से निपटना हो तो पॉलिसी ऑब्जेक्ट लागू करने पर विचार करें। जब RSpec के साथ टेस्टिंग की बात आती है, तो आपको डेटाबेस रिकॉर्ड्स का उपयोग करने की आवश्यकता नहीं है; आपकी पॉलिसीज़ शुद्ध Ruby ऑब्जेक्ट्स हैं, और आपका टेस्टिंग सरल और तेज़ होगा।