PostgreSQL (या सामान्य रूप से SQL) में व्यावसायिक तर्क अनुमतियों को कैसे लागू करें?


16

मान लें कि मेरे पास मदों की एक तालिका है:

CREATE TABLE items
(
    item serial PRIMARY KEY,
    ...
);

अब, मैं प्रत्येक आइटम के लिए "अनुमति" की अवधारणा को पेश करना चाहता हूं (कृपया ध्यान दें, मैं यहां डेटाबेस एक्सेस अनुमतियों के बारे में बात नहीं कर रहा हूं , लेकिन उस आइटम के लिए व्यावसायिक तर्क अनुमतियाँ)। प्रत्येक आइटम में डिफ़ॉल्ट अनुमतियाँ हैं और प्रति-उपयोगकर्ता अनुमतियाँ भी हैं जो डिफ़ॉल्ट अनुमतियों को ओवरराइड कर सकती हैं।

मैंने इसे लागू करने के कई तरीकों के बारे में सोचने की कोशिश की और निम्नलिखित समाधानों के साथ आया, लेकिन मुझे यकीन नहीं है कि कौन सा सबसे अच्छा है और क्यों:

1) बूलियन समाधान

प्रत्येक अनुमति के लिए बूलियन कॉलम का उपयोग करें:

CREATE TABLE items
(
    item serial PRIMARY KEY,

    can_change_description boolean NOT NULL,
    can_change_price boolean NOT NULL,
    can_delete_item_from_store boolean NOT NULL,
    ...
);

CREATE TABLE item_per_user_permissions
(
    item int NOT NULL REFERENCES items(item),
    user int NOT NULL REFERENCES users(user),

    PRIMARY KEY(item, user),

    can_change_description boolean NOT NULL,
    can_change_price boolean NOT NULL,
    can_delete_item_from_store boolean NOT NULL,
    ...
);

लाभ : प्रत्येक अनुमति का नाम है।

नुकसान : दर्जनों अनुमतियाँ हैं जो स्तंभों की संख्या में काफी वृद्धि करती हैं और आपको उन्हें दो बार (प्रत्येक तालिका में एक बार) परिभाषित करना होगा।

2) द इंटेगर सॉल्यूशन

पूर्णांक का उपयोग करें और इसे एक बिटफील्ड के रूप में मानें (यानी बिट 0 के लिए है can_change_description, बिट 1 के लिए है can_change_price, और इसी तरह, और अनुमतियों को सेट या पढ़ने के लिए बिटवाइज़ संचालन का उपयोग करें)।

CREATE DOMAIN permissions AS integer;

लाभ : बहुत तेज।

नुकसान : आपको इस बात का ध्यान रखना होगा कि कौन सा बिट डेटाबेस और फ्रंट-एंड इंटरफेस दोनों में किस अनुमति के लिए है।

3) बिटफील्ड सॉल्यूशन

2 के रूप में भी), लेकिन उपयोग करें bit(n)। ज्यादातर समान फायदे और नुकसान की संभावना है, शायद थोड़ा धीमा।

4) एनम समाधान

अनुमतियों के लिए एक एनुम प्रकार का उपयोग करें:

CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);

और फिर डिफ़ॉल्ट अनुमतियों के लिए एक अतिरिक्त तालिका बनाएं:

CREATE TABLE item_default_permissions
(
    item int NOT NULL REFERENCES items(item),
    perm permission NOT NULL,

    PRIMARY KEY(item, perm)
);

और प्रति-उपयोगकर्ता परिभाषा तालिका को इसमें बदलें:

CREATE TABLE item_per_user_permissions
(
    item int NOT NULL REFERENCES items(item),
    user int NOT NULL REFERENCES users(user),
    perm permission NOT NULL,

    PRIMARY KEY(item, user, perm)    
);

लाभ : व्यक्तिगत अनुमतियों के नाम पर आसान (आपको बिट पदों को संभालने की आवश्यकता नहीं है)।

नुकसान : यहां तक ​​कि जब केवल डिफ़ॉल्ट अनुमतियों को पुनर्प्राप्त किया जाता है, तो इसके लिए दो अतिरिक्त तालिकाओं तक पहुंचने की आवश्यकता होती है: पहला, डिफ़ॉल्ट अनुमतियाँ तालिका, और दूसरा, सिस्टम कैटलॉग में एनम मानों को संग्रहीत करना।

विशेष रूप से क्योंकि उस आइटम के हर एक पृष्ठ दृश्य के लिए डिफ़ॉल्ट अनुमतियाँ पुनर्प्राप्त की जानी चाहिए , अंतिम विकल्प का प्रदर्शन प्रभाव महत्वपूर्ण हो सकता है।

5) एनुम अर्रे समाधान

4 के समान), लेकिन सभी (डिफ़ॉल्ट) अनुमतियों को रखने के लिए एक सरणी का उपयोग करें:

CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);

CREATE TABLE items
(
    item serial PRIMARY KEY,

    granted_permissions permission ARRAY,
    ...
);

लाभ : व्यक्तिगत अनुमतियों के नाम पर आसान (आपको बिट पदों को संभालने की आवश्यकता नहीं है)।

नुकसान : 1 सामान्य रूप को तोड़ता है और थोड़ा बदसूरत होता है। यदि अनुमतियों की संख्या बड़ी है (लगभग 50) तो एक पंक्ति में काफी संख्या में बाइट्स लेता है।

क्या आप अन्य विकल्पों के बारे में सोच सकते हैं?

कौन सा दृष्टिकोण लिया जाना चाहिए और क्यों?

कृपया ध्यान दें: यह Stackoverflow पर पहले पोस्ट किए गए प्रश्न का एक संशोधित संस्करण है ।


2
दर्जनों विभिन्न अनुमतियों के साथ मैं एक (या अधिक) bigintफ़ील्ड (प्रत्येक 64 बिट के लिए अच्छा) या थोड़ा स्ट्रिंग चुन सकता हूं । मैंने एसओ पर संबंधित जवाबों की एक जोड़ी
एरविन ब्रान्डसेट्टर

जवाबों:


7

मुझे पता है कि आप प्रति सेकेंड डेटाबेस सुरक्षा के बारे में नहीं पूछ रहे हैं , लेकिन आप डेटाबेस सुरक्षा का उपयोग कर सकते हैं। तुम भी एक वेब अनुप्रयोग में इस का उपयोग कर सकते हैं। यदि आप डेटाबेस सुरक्षा का उपयोग नहीं करना चाहते हैं, तो स्कीमा अभी भी लागू होता है।

आप स्तंभ-स्तरीय सुरक्षा, पंक्ति-स्तरीय सुरक्षा और संभवतः श्रेणीबद्ध भूमिका प्रबंधन चाहते हैं। रोल-आधारित सुरक्षा उपयोगकर्ता-आधारित सुरक्षा की तुलना में प्रबंधित करना बहुत आसान है।

यह उदाहरण कोड PostgreSQL 9.4 के लिए है, जो जल्द ही सामने आता है। आप इसे 9.3 के साथ कर सकते हैं, लेकिन अधिक मैनुअल श्रम की आवश्यकता है।

यदि आप प्रदर्शन You से संबंधित हैं, तो आप सब कुछ अनुक्रमित करना चाहते हैं, जो आपको होना चाहिए। इसका मतलब है कि बिट-मास्क और एरे फ़ील्ड शायद एक अच्छा विचार नहीं होगा।

इस उदाहरण में, हम मुख्य डेटा टेबल में रखते हैं data स्कीमा में रखते हैं, और इसी विचार में public

create schema data; --main data tables
create schema security; --acls, security triggers, default privileges

create table data.thing (
  thing_id int primary key,
  subject text not null, --or whatever
  owner name not null
);

आवेषण और अद्यतनों के लिए data.thing पर ट्रिगर रखें जो यह दर्शाता है कि स्वामी स्तंभ current_user है। शायद केवल मालिक को अपने स्वयं के रिकॉर्ड (एक और ट्रिगर) को हटाने की अनुमति दें।

बनाओ WITH CHECK OPTION दृश्य , जो कि उपयोगकर्ता वास्तव में उपयोग करेंगे। इसे अद्यतन करने के लिए वास्तव में कठिन प्रयास करें, अन्यथा आपको ट्रिगर / नियमों की आवश्यकता होगी, जो अधिक काम है।

create view public.thing with(security_barrier) as 
select
thing_id,
subject,
owner,
from data.thing
where
pg_has_role(owner, 'member') --only owner or roles "above" him can view his rows. 
WITH CHECK OPTION;

अगला, एक पहुंच-नियंत्रण सूची तालिका बनाएं:

--privileges r=read, w=write

create table security.thing_acl (
  thing_id int,
  grantee name, --the role to whom your are granting the privilege
  privilege char(1) check (privilege in ('r','w') ),

  primary key (thing_id, grantee, privilege),

  foreign key (thing_id) references data.thing(thing_id) on delete cascade
);

ACL के लिए अपना विचार बदलें:

drop view public.thing;

create view public.thing with(security_barrier) as 
select
thing_id,
subject,
owner
from data.thing a
where
pg_has_role(owner, 'member')
or exists (select 1 from security.thing_acl b where b.thing_id = a.thing_id and pg_has_role(grantee, 'member') and privilege='r')
with check option;

एक डिफ़ॉल्ट पंक्ति विशेषाधिकार तालिका बनाएँ:

create table security.default_row_privileges (
  table_name name,
  role_name name,
  privilege char(1),

  primary key (table_name, role_name, privilege)
);

डेटा पर कुछ डालने के लिए ट्रिगर रखें। यह सुरक्षा के लिए डिफ़ॉल्ट पंक्ति विशेषाधिकारों को कॉपी करता है।

  • तालिका-स्तरीय सुरक्षा को उचित रूप से समायोजित करें (अवांछित उपयोगकर्ताओं से आवेषण को रोकें)। किसी को भी डेटा या सुरक्षा स्कीमा पढ़ने में सक्षम नहीं होना चाहिए।
  • स्तंभ-स्तरीय सुरक्षा को उचित रूप से समायोजित करें (कुछ स्तंभों को देखने / संपादित करने से कुछ उपयोगकर्ताओं को रोकें)। यह देखने के लिए कि उपयोगकर्ता एक कॉलम देख सकता है, आप has_column_privilege () का उपयोग कर सकते हैं।
  • संभवतः आपके दृष्टिकोण पर सुरक्षा निश्चित टैग चाहते हैं।
  • विशेषाधिकार प्राप्त करने के लिए ट्रैक करने के लिए acl तालिकाओं में जोड़ने grantorऔर admin_optionस्तंभों पर विचार करें , और चाहे अनुदानकर्ता उस पंक्ति पर विशेषाधिकारों का प्रबंधन कर सकता है या नहीं।
  • बहुत टेस्ट करें

P इस मामले में pg_has_role शायद अनुक्रमणिका नहीं है। आपको current_user के लिए सभी श्रेष्ठ भूमिकाओं की एक सूची प्राप्त करनी होगी और इसके बजाय स्वामी / अनुदान मूल्य की तुलना करनी होगी।


क्या आपने " मैं यहाँ डेटाबेस एक्सेस अनुमतियों के बारे में बात नहीं कर रहा हूँ " भाग देखा?
a_horse_with_no_name

@a_horse_with_no_name मैंने किया। वह अपनी खुद की आरएलएस / एसीएल प्रणाली लिख सकता है, या वह डेटाबेस की अंतर्निहित सुरक्षा का उपयोग कर सकता है जो वह पूछ रहा है।
नील मैकगिन

आपके विस्तृत उत्तर के लिए धन्यवाद! हालाँकि, मुझे नहीं लगता कि डेटाबेस भूमिका का उपयोग करना यहाँ सही उत्तर है क्योंकि न केवल स्टाफ, बल्कि हर एक उपयोगकर्ता की अनुमति हो सकती है। उदाहरण 'can_view_item', 'can_bulk_order_item' या 'can_review_item' होंगे। मुझे लगता है कि अनुमति नामों की मेरी मूल पसंद ने आपको विश्वास दिलाया कि यह केवल कर्मचारियों की अनुमति के बारे में है, लेकिन ये सभी नाम जटिलताओं को दूर करने के लिए केवल उदाहरण थे। जैसा कि मैंने मूल प्रश्न में कहा, यह प्रति उपयोगकर्ता अनुमतियों के बारे में है , न कि कर्मचारियों की अनुमति के अनुसार
जॉनकंड

वैसे भी, उपयोगकर्ताओं की तालिका में प्रत्येक उपयोगकर्ता पंक्ति के लिए अलग-अलग डेटाबेस भूमिकाएँ होने से ओवरकिल और शायद ही प्रबंधनीय लगता है। हालाँकि, मुझे लगता है कि आपका जवाब डेवलपर्स के लिए मूल्यवान है जो सिर्फ कर्मचारियों की अनुमति को लागू करता है।
जॉनकंड

1
@ जॉन्कंड मैं वास्तव में यह नहीं देखता कि किसी अन्य जगह अनुमतियों को प्रबंधित करना कितना आसान है, लेकिन एक बार मिल जाने के बाद कृपया हमें अपने समाधान की ओर इशारा करें! :)
नील मैकगिन

4

क्या आपने उपयोग करने पर विचार किया है एक्सेस कंट्रोल लिस्ट PostgreSQL एक्सटेंशन ?

इसमें मूल PostgreSQL डेटा प्रकार ACE और फ़ंक्शन का एक सेट शामिल है जो आपको यह जांचने की अनुमति देता है कि क्या किसी उपयोगकर्ता के पास डेटा एक्सेस करने की अनुमति है। यह PostgreSQL रोल्स सिस्टम के साथ या अमूर्त संख्याओं (या UUIDs) के साथ काम करता है जो आपके एप्लिकेशन उपयोगकर्ता / रोल आईडी का प्रतिनिधित्व करता है।

आपके मामले में, आप अपने डेटा तालिकाओं में सिर्फ एक ACL कॉलम जोड़ते हैं और acl_check_accessएक ACL के खिलाफ एक उपयोगकर्ता की जांच करने के लिए एक फ़ंक्शन का उपयोग करते हैं ।

CREATE TABLE items
(
    item serial PRIMARY KEY,
    acl ace[],
    ...
);

INSERT INTO items(acl, ...) VALUES ('{a//<user id>=r, a//<role id>=rwd, ...}');

SELECT * FROM items where acl_check_access(acl, 'r', <roles of the user>, false) = 'r'

ACL का उपयोग करना व्यापारिक तर्क अनुमतियों से निपटने का एक बहुत ही लचीला तरीका है। इसके अलावा, यह अविश्वसनीय रूप से तेज़ है - औसत ओवरहेड को रिकॉर्ड पढ़ने के लिए आवश्यक समय का केवल 25% है। केवल सीमा यह है कि यह प्रति वस्तु प्रकार की अधिकतम 16 कस्टम अनुमति का समर्थन करता है।


1

मैं इस सांकेतिक शब्दों में बदलने की एक और संभावना के बारे में सोच सकता हूं

यदि आपको permission_per_itemमेज की जरूरत नहीं है, तो आप इसे छोड़ सकते हैं Permissionsऔर Itemsसीधे कनेक्ट कर सकते हैं और item_per_user_permissionsमेज पर ले जा सकते हैं।

यहाँ छवि विवरण दर्ज करें

किंवदंती
चित्र

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.