کنترل دسترسی به شکل کد-مبنا (CBAC):

اختصاص‌دادن نقش به واحدهای برنامه‌نویسی PL/SQL در پایگاه داده‌ اوراکل 12.1

به طور پیش‌فرض: واحدهای برنامه‌نویسی PL/SQL با استفاده از اختیارات تعریف‌کننده‌ها ساخته می‌شوند و به همین خاطر هم با تمام اجازه‌ای که دارند، مستقیما به کاربری که آن‌ها را به‌وجود آورده، داده می‌شوند. چنین چیزی در زمانی که می‌خواهید کاری با اجازه بالا را به کاربری با اجازه پایین واگذار کنید، بسیار مفید خواهد بود. در این مواقع می‌توان کارها را در لفاف یه واحد برنامه‌ای PL/SQL پوشاند، و همراه با آن، اجازه اجراکردن آن را به کاربری با اجازه پایین اختصاص‌داد. مشکل تعریف‌کننده‌های اختیارات این است که خیلی ساده می‌توان اجازه‌ی بیش از حد به یک کاربر اختصاص‌داد.

یک راهکار جایگزین می‌تواند به‌وجود آوردن واحدهای برنامه‌ای با اختیارات درخواست‌کننده باشد، تا بدین‌وسیله بتوانند توسط کاربر فراخوانده‌شده اجرا شوند، نه توسط کاربری که آن‌ها را به‌وجود آورده. مزیت این روش این است که واحد برنامه‌ای تنها قادر به اجرای کارهایی خواهد بود که کاربر فراخوانده‌شده اجازه انجام‌دادنش را دارد، نظیر اجازه‌هایی که توسط نقش‌ها به آن‌ها اختصاص‌ داده‌شده است. درخواست‌کننده‌های اختیارات مشکلاتی نظیر چیزهایی که در پی نوشته می‌شوند، دارند:
ممکن است بعضی واحدهای برنامه‌نویسی، ترکیبی از اجازه‌های ضروری و اختیاری را نیاز داشته باشند. اگر کاربر فراخوانده‌شده از آن دسته ازاجازه‌های که ضروری تلقی می‌شوند، برخوردار نباشد، کد، بدون‌استفاده خواهد شد.​
از آنجایی که کاربر فراخوانده‌شده می‌بایست نسبت به شیءهای ارجاع‌داده‌شده توسط واحد برنامه‌ایِ اختیارات درخواست‌‌کننده‌، به شکل مستقیم یا غیر مستقیم، اجازه داشته باشد، این شیءها برای کاربر قابل‌مشاهده هستند. به همین خاطر هم ممکن است کاربر تصمیم بگیرد که از آن اشیاء در جای دیگری استفاده کند.
اگر یک واحد برنامه‌ای درخواست‌کننده اختیارات (prog2) از یک واحد برنامه‌ای تعریف‌کننده‌ی اختیارات (prog1) فراخوانده شود، prog2 توسط کاربری که مالک prog1 است، اجرا می‌شود، نه توسط کاربر فراخوانده شده.
​اوراکل ۱۲c کنترل دسترسی به شکل کد-مبنا (CBAC) را معرفی کرده است که امکان اختصاص مستقیم نقش‌ها به تعریف‌کننده‌ها و درخواست‌کننده‌های اختیارات واحدهای برنامه‌ای را می‌دهد، و در نتیجه شما را از سطح اجازه کاربر فراخوانده شده در لحظه مطمئن می‌سازد، بدون اینکه لازم باشد اشیای اضافی را برای او نمایان کنید. تمرکز این مقاله بر روی این است که  کنترل دسترسی به شکل کد-مبنا (CBAC) چگونه می‌‌تواند برای حل‌کردن دو مشکل اولی که ذکر شدند و با واحدهای برنامه‌ای اختیارات درخواست‌کننده در ارتباط بودند، بکار گرفته شود.

تعریف‌ مشکل

دو کاربر آزمایشی ایجاد کنید. کاربر اولی قادر به ایجاد جدول‌ها و واحدهای برنامه‌ای PL/SQL خواهد بود، و کاربر دوم تنها خواهد توانست به پایگاه‌داده‌ها متصل شود.

CONN / AS SYSDBA
ALTER SESSION SET CONTAINER = pdb1;

DROP USER cbac_user_1 CASCADE;
DROP USER cbac_user_2 CASCADE;

CREATE USER cbac_user_1 IDENTIFIED BY cbac_user_1
QUOTA UNLIMITED ON USERS;

GRANT CREATE SESSION, CREATE TABLE, CREATE PROCEDURE TO cbac_user_1;

CREATE USER cbac_user_2 IDENTIFIED BY cbac_user_2
QUOTA UNLIMITED ON USERS;

GRANT CREATE SESSION TO cbac_user_2;

کاربر را متصل کنید و دو جدول آزمایشی بسازید.

CONN cbac_user_1/cbac_user_1@pdb1

CREATE TABLE tab1 (
id NUMBER
);

INSERT INTO tab1
SELECT level
FROM dual
CONNECT BY level <= 5;

COMMIT;

CREATE TABLE tab2 (
id NUMBER
);

INSERT INTO tab2
SELECT level
FROM dual
CONNECT BY level <= 5;

COMMIT;

یک عملکرد اختیارات تعریف‌کننده ایجاد کنید که به جدول‌های آزمایشی دسترسی داشته باشد. مقدار بازگشتی این عملکرد شامل اطلاعاتی در رابطه با فراخوانی کاربر،‌ کاربری که اجازه‌هایش در حال حاضر فعال هستند، تعداد سطرهای جدول tab1 و تعداد سطرهای جدول tab2 است. می‌دانیم که چون چنین چیزی یک عملکرد اختیارات تعریف‌کننده به‌حساب می‌آید،  دسترسی به هر دو جدول ممکن خواهد بود.

CONN cbac_user_1/cbac_user_1@pdb1

CREATE OR REPLACE FUNCTION get_count_definer
RETURN VARCHAR2
AUTHID DEFINER
AS
l_count1 NUMBER;
l_count2 NUMBER;
l_return VARCHAR2(32767);
BEGIN
SELECT COUNT(*)
INTO l_count1
FROM cbac_user_1.tab1;

SELECT COUNT(*)
INTO l_count2
FROM cbac_user_1.tab2;

l_return := 'CallUser=' || USER ||
' PrivUser=' || SYS_CONTEXT('userenv', 'CURRENT_USER') ||
' T1Count=' || l_count1 ||
' T2Count=' || l_count2;

RETURN l_return;
END;
/

یک عملکرد مشابه بسازید، اما این بار برای اختیارات درخواست‌کننده. بیایید فرض کنیم که دسترسی به tab1 ضروری اما دسترسی به tab2 اختیاری ، و به‌همین‌خاطر هم هندلر استثنائات، اضافی است.

CREATE OR REPLACE FUNCTION get_count_invoker
RETURN VARCHAR2
AUTHID CURRENT_USER
AS
l_count1 NUMBER;
l_count2 NUMBER;
l_return VARCHAR2(32767);
BEGIN
SELECT COUNT(*)
INTO l_count1
FROM cbac_user_1.tab1;

BEGIN
SELECT COUNT(*)
INTO l_count2
FROM cbac_user_1.tab2;
EXCEPTION
WHEN OTHERS THEN
l_count2 := -1;
END;

l_return := 'CallUser=' || USER ||
' PrivUser=' || SYS_CONTEXT('userenv', 'CURRENT_USER') ||
' T1Count=' || l_count1 ||
' T2Count=' || l_count2;

RETURN l_return;
END;
/

به کاربر CBAC_USER_2  دسترسی به هر دو عملکرد را اختصاص می‌دهیم.

GRANT EXECUTE ON get_count_definer TO cbac_user_2;
GRANT EXECUTE ON get_count_invoker TO cbac_user_2;

کاربر CBAC_USER_2  را متصل و برای استفاده از عملکردها تلاش کنید.

CONN cbac_user_2/cbac_user_2@pdb1

SELECT cbac_user_1.get_count_definer FROM dual;

GET_COUNT_DEFINER
----------------------------------------------------------------------------------------------------
CallUser=CBAC_USER_2 PrivUser=CBAC_USER_1 T1Count=5 T2Count=5

1 row selected.

SQL>

SELECT cbac_user_1.get_count_invoker FROM dual;
SELECT cbac_user_1.get_count_invoker FROM dual
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "CBAC_USER_1.GET_COUNT_INVOKER", line 9


SQL>

عملکرد اختیارات تعریف‌کننده به همان شکلی که پیش‌بینی می‌شد، کار کرد. توجه کنید که با وجود اینکه کاربر کنونی، CBAC_USER_2 است، فرآیند با اجازه‌ها کاربر CBAC_USER_1 انجام می‌پذیرد، و به همین خاطر به هر دو جدول آزمایشی دسترسی دارد. کاربر CBAC_USER_2 در حال حاظر هیچ اجازه‌ای بر روی جدول‌های آزمایشی ندارد، و به همین خاطر، عملکرد اختیارات درخواست‌کننده همان‌طور که پیش‌بینی شده بود با شکست مواجه می‌شود. توجه کنید که ما هیچ کدام از جدول‌های آزمایشی را مستقیما برای کاربر CBAC_USER_2 نمایان نکرده‌ایم.

SELECT * FROM cbac_user_1.tab1;
SELECT * FROM cbac_user_1.tab1
*
ERROR at line 1:
ORA-00942: table or view does not exist

SQL>

راهکار نسخه‌های قدیمی‌تر از 12c

پیش از نسخه‌ی 12c، تنها گرینه‌ی پیش روی ما اختصاص‌دادن اجازه  جدول(ها)، به شکل مستقیم یا غیرمستقیم، به کاربر CBAC_USER_2  بود. نقشی که به دنبال می‌آید تنها به جدول ضروری(tab1) دسترسی می‌دهد:

CONN / AS SYSDBA
ALTER SESSION SET CONTAINER = pdb1;

DROP ROLE cbac_role;
CREATE ROLE cbac_role;
GRANT cbac_role TO cbac_user_1, cbac_user_2;
GRANT SELECT ON cbac_user_1.tab1 TO cbac_role;

با نقشی که به کاربر CBAC_USER_2  اختصاص داده شده، می‌توانیم عملکرد را دوباره آزمایش کنیم. حالا عملکرد اختیارات درخواست‌کننده، با استفاده از اجازه‌های داده‌شده به کاربر کنونی، همان‌طور که پیش‌بینی می‌شد کار می‌کند.

CONN cbac_user_2/cbac_user_2@pdb1

SELECT cbac_user_1.get_count_definer FROM dual;

GET_COUNT_DEFINER
----------------------------------------------------------------------------------------------------
CallUser=CBAC_USER_2 PrivUser=CBAC_USER_1 T1Count=5 T2Count=5

1 row selected.

SQL>

SELECT cbac_user_1.get_count_invoker FROM dual;

GET_COUNT_INVOKER
----------------------------------------------------------------------------------------------------
CallUser=CBAC_USER_2 PrivUser=CBAC_USER_2 T1Count=5 T2Count=-1

1 row selected.

SQL>

مشکل این است که ما جدول tab1 را برای کاربر CBAC_USER_2  نمایان کرده‌ایم.

CONN cbac_user_2/cbac_user_2@pdb1

SELECT * FROM cbac_user_1.tab1;

ID
----------
1
2
3
4
5

5 rows selected.

SQL>

راهکار کنترل دسترسی به شکل کد-مبنا

به کمک نحوه‌ی عملکرد کنترل دسترسی به شکل کد-مبنا می‌توانیم مطمئن شویم که عملکرد اختیارات درخواست‌کننده، یدون وابستگی به کاربر فراخوانده‌شده،  با هر نوع اجازه ضروری‌ای اجرا می‌شود.

نقش را از کاربر CBAC_USER_2  فرابخوانید و در مقابل، آن را ضد عملکرد بکار ببرید:

CONN / AS SYSDBA
ALTER SESSION SET CONTAINER = pdb1;

REVOKE cbac_role FROM cbac_user_2;
GRANT cbac_role TO FUNCTION cbac_user_1.get_count_invoker;

کاربر CBAC_USER_2  همچنان همانند قبل نتایج پیش‌بیبنی‌شده را از عملکرد اختیارات درخواست‌کننده به‌دست می‌آورد، اما دیگر جدول tab1 برای کاربر فراخوانده‌شده نمایان نیست.

CONN cbac_user_2/cbac_user_2@pdb1

SELECT cbac_user_1.get_count_invoker FROM dual;

GET_COUNT_INVOKER
----------------------------------------------------------------------------------------------------
User=CBAC_USER_2 Priv User=CBAC_USER_2 T1 Count=5 T2 Count=-1

1 row selected.

SQL>


SELECT * FROM cbac_user_1.tab1;
SELECT * FROM cbac_user_1.tab1
*
ERROR at line 1:
ORA-00942: table or view does not exist


SQL>