Детальний контроль доступу і контексти додатки. Частина 3

Частина 2

Для здійснення цього завдання використовуються таблиці EMP_BASE_TABLE і HR_REP. Тоді виникає питання "навіщо використовувати таблицю EMP_BASE_TABLE та подання EMP, якщо можна просто виконати select * from emp_base_table? "З двох причин:




  1. Дані таблиці службовців використовуються для реалізації політики безпеки.
  2. Ця таблиця зчитується при створенні контексту програми.

Для того, щоб прочитати дані про службовця, необхідно встановити значення контексту програми, для того, щоб встановити значення контексту програми, необхідно прочитати дані про службовця. Це проблема курки і яйця. Одним з варіантів її вирішення є створення уявлення, яким користуватимуться всі програми (подання EMP) і реалізація безпеки цього подання. Для здійснення цього завдання політика безпеки буде користуватися вихідною таблицею EMP_BASE_TABLE. З таблиці EMP_BASE_TABLE можна дізнатися, хто є менеджером деякого відділу, і хто підпорядковується деякого користувачеві. Додаток і кінцеві користувачі ніколи не будуть звертатися до таблиці EMP_BASE_TABLE – вона буде використовуватися тільки політикою безпеки. Після цього створюється процедура встановлення значень контексту:



SQL> – це процедура, "несе відповідальність" за 
SQL> – створення контексту програми. Вона реалізує
SQL> – політику шляхом надання повноважень,
SQL> – виданих користувачеві в контексті програми.

SQL> create or replace
  2  procedure set_role( p_roleName in varchar2 default null )
  3  as
  4      l_empno       number;
  5      l_cnt      number;
  6      l_roleName varchar2(40) default upper(p_roleName);
  7  begin
8 if (sys_context ("Hr_App_Ctx", "RoleName") is NOT NULL)
  9      then
10 – переривання запиту. Зміна ролі вимагає зміни
11 – предиката, пов'язаного із запитом. Через кешування курсору
12 – у клієнтському додатку не можна гарантувати, що
13 – не існує інших вже розібраних запитів з
14 – предикатами з поточної ролі.
15 – Якщо, наприклад, роль вже встановлена в значення MGR і
16 – розібрано кілька запитів, а тепер спробувати
17 – змінити роль на EMP, то розібрані запити все ще
18 – будуть використовувати предикати MGR, а не EMP.
19 raise_application_error (-20 001, "Рольужеустановлена");
20      end if;
21
22 – Так як використовується користувач СЕСІЇ, а не ПОТОЧНИЙ
25 – користувач і, крім того, empno поточного користувача
23 – використовується досить часто, то він буде зберігатися в цьому
24 – контексті. Користувач СЕСІЇ – це ім'я приєднаного в
26 – даний момент користувача. ПОТОЧНИЙ користувач – це ім'я
27 – користувача, який володіє привілеями на виконання
28 – запиту, їм може бути власник процедури,
29– Тобто не приєднаних користувач!
30      select empno into l_empno
31        from emp_base_table
32       where ename = sys_context( “userenv”, “session_user”);
33
34 dbms_session.set_context ("Hr_App_Ctx", "Empno", l_empno);
35
36      if ( l_roleName = “EMP” )
37      then
38 – будь-хто може скористатися роллю EMP
39 dbms_session.set_context ("Hr_App_Ctx", "RoleName", "EMP");
40      elsif ( l_roleName = “MGR” )
41      then
42 – перевіримо, чи є користувач MGR (менеджером),
43 – якщо немає, то видамо повідомлення про помилку і перервемо виконання.
44– Користувач може спробувати знову.
45          select count(*) into l_cnt
46            from dual
47           where exists
48             ( select NULL
49             from emp_base_table
50 where mgr = to_number (sys_context ("Hr_App_Ctx", "Empno"))
51             );
52          if ( l_cnt = 0 )
53          then
54 raise_application_error (-20 002, "Винеменеджер");
55          end if;
56 dbms_session.set_context ("Hr_App_Ctx", "RoleName", "MGR");
57      elsif ( l_roleName = “HR_REP” )
58      then59 – перевіримо, чи є користувач HR_REP, якщо немає,
60 – видамо повідомлення про помилку і перервемо виконання.
61    – Користувач може спробувати знову.
62          select count(*) into l_cnt
63            from dual
64           where exists
65             ( select NULL
66             from hr_reps
67 where username = sys_context ("userenv", "session_user")
68             );
69
70          if ( l_cnt = 0 )
71          then
72 raise_application_error (-20 002, "Винеконтролер");
73          end if;
74 dbms_session.set_context ("Hr_App_Ctx", "RoleName", "HR_REP");
75      else
76 raise_application_error (-20 003, "Роль" / / l_roleName / /
77 "неможливо розпізнати");
78      end if;
79  end;
80  /
Procedure created.

SQL> grant execute on set_role to public
  2  /
Grant succeeded.

Отже, до цього моменту зроблено: створена процедура, яка приймає ім'я ролі в якості параметра. На початку цієї процедури забезпечується, щоб атрибут RoleName ще не був встановлений. Так як в політиці безпеки будуть повертатися різні предикати, що залежать від значення RoleName, то не можна дозволяти користувачеві змінювати його роль, якщо вона вже встановлена. Якщо допустити зміна ролі, то може виникнути проблема, пов'язана з кешуванням курсору і 'old'-предикатами. Далі подивимося на EMPNO поточного користувача. Ця процедура виконує дві операції:



  1. Перевіряє, чи є користувач службовцям – при отриманні помилки "NO DATA FOUND", стає відомим, що він не є службовцем. Тому значення його контексту ніколи не будуть встановлені, а сам користувач не побачить ніяких даних.

  2. Поміщає часто використовується значення в контекст програми. Тепер через EMPNO поточного користувача можна швидко отримати доступ до таблиці EMP – це буде реалізовано в предикатной функції, описаної нижче.

Далі процедура переходить до перевірки, чи дозволено поточному користувачеві отримати роль, якої він потребує. Кожен, хто запрошує роль EMP, може встановити її. Тільки той, хто дійсно управляє іншими людьми, може встановити роль MGR. Тільки той, про кого є дані в таблиці HR_REPS, може встановити роль HR_REP.


Далі створимо об'єкт контексту програми бази даних і зв'яжемо його з щойно створеної процедурою SET_HR_APP_DEPT:



SQL> – Створення контексту програми. Ім'я контексту –
SQL> – HR_APP_CTX. Процедура, за якою він пов'язаний у даному випадку –
SQL> – це SET_ROLE

SQL> create or replace context Hr_App_Ctx using SET_ROLE
  2  /
Context created.

Отже, тепер створені контекст з ім'ям Hr_App_Ctx і процедура для його установки. Важливо мати на увазі, що, так як контекст пов'язаний з процедурою Set_Role, то вона є єдиним засобом установки значень контексту. Якщо, наприклад, спробувати встановити в цьому контексті RoleName з метою отримання доступу до заборонених даними, то виявиться, що зробити це не можна:



SQL> REM Виконання наступних далі операцій буде ПЕРЕРВАНО. 
SQL> REM Це показує, що процедура dbms_session.set_context
SQL> REM може встановити контекст Hr_App_Ctx тільки через процедуру
SQL> REM SET_ROLE

SQL> exec dbms_session.set_context ("Hr_App_Ctx", "RoleName", "MGR");
BEGIN dbms_session.set_context ("Hr_App_Ctx", "RoleName", "MGR"); END

*
ERROR at line 1:
ORA-01031: прівілегійнедостаточно
ORA-06512: at “SYS.DBMS_SESSION”, line 55
ORA-06512: at line 1

Цей атрибут контексту відповідає за безпеку. Значення контексту можна встановити тільки через процедуру. У цьому випадку гарантується, що, якщо значення й існує, то воно перевірено і встановлено. Тепер для тестування логіки процедури спробуємо виконати збережену процедуру під різними користувачами і подивимося, які ролі можна встановити і які значення існують в контексті.



SQL> grant select on sys.v_$context to rls_smith;
Grant succeeded.

SQL> connect rls_smith/rls_smith
Connected.

SQL> set serveroutput on
SQL> show user
USER is “RLS_SMITH

SQL> exec rls.set_role( “Mgr” )
BEGIN rls.set_role( “Mgr” ); END;

*
ERROR at line 1:
ORA-20002: Ви не менеджер
ORA-06512: at “RLS.SET_ROLE”, line 53
ORA-06512: at line 1

До теперішнього часу можна було побачити, що процедура доступна RLS_SMITH, але не дозволяє йому встановити контекст 'MGR' до тих пір, поки він фактично не стане менеджером. Якщо зараз подивитися на цей контекст через динамічне представлення v $ context, то можна побачити:



SQL> select * from v$context;

NAMESPACE  ATTRIBUTE  VALUE
———- ———- —–
HR_APP_CTX EMPNO      7369

що RLS_SMITH дійсно міг отримати список номерів його службовців, але не атрибут RoleName. Тепер повторимо операцію з RoleName, задавши правильне значення для RLS_SMITH:



SQL> exec rls.set_role( “emp” )
PL/SQL procedure successfully completed.

SQL> select * from v$context;

NAMESPACE  ATTRIBUTE  VALUE
———- ———- —–
HR_APP_CTX ROLENAME   EMP
HR_APP_CTX EMPNO      7369

Можна побачити, що тепер все працює, як очікувалося. RLS_SMITH може отримати номер його службовця і атрибут RoleName, встановлений в контексті HR_APP_CTX. Для подальшого тестування логіки виконаємо:

SQL> exec rls.set_role( “emp” )
BEGIN rls.set_role( “emp” ); END;

*
ERROR at line 1:
ORA-20001: Роль вже встановлена
ORA-06512: at “RLS.SET_ROLE”, line 18
ORA-06512: at line 1

SQL> select * from v$context;

NAMESPACE  ATTRIBUTE  VALUE
———- ———- —–
HR_APP_CTX ROLENAME   EMP
HR_APP_CTX EMPNO      7369

Результат показує, що логіка не дозволяє користувачеві змінити його роль після того, як вона вже встановлена, далі буде видно, що в контексті залишаються колишні значення. Це повідомлення про помилку не критично, воно означає тільки те, що не можна змінити роль під час сесії.


Далі приєднаємось різними користувачами, переглянемо результати роботи процедури і розглянемо різні способи отримання значень контексту сесії:



SQL> connect rls_blake/rls_blake
Connected.

SQL> exec rls.set_role( “mgr” )
PL/SQL procedure successfully completed.

SQL> set serveroutput on
SQL> declare
  2      l_AppCtx     dbms_session.AppCtxTabTyp;
  3      l_size        number;
  4  begin
  5      dbms_session.list_context( l_AppCtx, l_size );
  6      for i in 1 .. l_size loop
  7          dbms_output.put( l_AppCtx(i).namespace // “.” );
  8          dbms_output.put( l_AppCtx(i).attribute // ” = ” );
  9          dbms_output.put_line( l_AppCtx(i).value );
10      end loop;
11  end;
/

HR_APP_CTX.ROLENAME = MGR
HR_APP_CTX.EMPNO = 7698

PL/SQL procedure successfully completed.

На цей раз приєднаємося як RLS_BLAKE, завідувач відділом 30. Коли RLS_BLAKE викликає процедуру Set_Role з параметром RoleName = 'MGR', видно, що контекст встановлений правильно: він – менеджер, і кількість його службовців встановлено. Крім того, цей тест показує, як переглянути пари значень атрибутів у контексті сесії за допомогою пакету dbms_session.list_context. Функції цього пакета може викликати будь-який користувач (так як відбувається звернення до подання sys.v $ context, яке використовувалося раніше), тому всі користувачі можуть використовувати такий метод для перевірки значень контексту сесії.


Тепер, так як контекст сесії встановлений належним чином, можна підготуватися до створення процедури політики безпеки. Ця процедура буде викликатися засобами бази даних під час їх роботи для формування динамічного предиката. Динамічний предикат обмежує такі можливості користувача, як читання і запис.



SQL> – створимо предикатний пакет (пакет для генерації умови where)
SQL> – додатки HR. Для створення унікального умови where кожної
SQL> – операції SELECT / INSERT / UPDATE / DELETE повинна відповідати
SQL> – окрема функція.

SQL> create or replace package hr_predicate_pkg
  2  as
  3     function select_function( p_schema in varchar2,
4 p_object in varchar2) return varchar2;
  5
  6     function update_function( p_schema in varchar2,
7 p_object in varchar2) return varchar2;
  8
  9     function insert_delete_function( p_schema in varchar2,
10   p_object in varchar2 ) return varchar2;
11  end;
12  /

Для кожної операції Мови Маніпулювання Даними (Data Manipulation Language – DML) напишемо предикати, трохи відрізняються один від одного. Кожна DML-операція буде підкорятися своїм правилам. Це дозволить пропозицією DELETE бачити набір даних (в даному прикладі менший), що відрізняється від набору даних, видимого при виконанні пропозиції SELECT. Далі показана реалізація тіла пакету HR_PREDICATE_PKG:



SQL> create or replace package body hr_predicate_pkg
  2  as
  3
  4  g_app_ctx constant varchar2(30) default “Hr_App_Ctx”;
  5
  6  g_sel_pred varchar2(1024) default NULL;
  7  g_upd_pred varchar2(1024) default NULL;
  8  g_ins_del_pred varchar2(1024) default NULL;
  9
10
11  function select_function( p_schema in varchar2,
12                    p_object in varchar2 ) return varchar2
13  is
14  begin
15
16      if ( g_sel_pred is NULL )
17      then
18          if ( sys_context( g_app_ctx, “RoleName” ) = “EMP” )
19          then
20 g_sel_pred: = "empno = sys_context (" "Hr_App_Ctx "","" EmpNo "")";
21
22 elsif (sys_context (g_app_ctx, "RoleName") = "MGR")
23          then
24          g_sel_pred :=
25             ” empno in ( select empno
26                    from emp_base_table
27                   start with empno =
28 sys_context ("" Hr_App_Ctx "","" EmpNo "")
29                 connect by prior empno = mgr)”;
30
31 elsif (sys_context (g_app_ctx, "RoleName") = "HR_REP")
32          then
33          g_sel_pred := “deptno in
34                   ( select deptno
35                       from hr_reps
36                      where username =
37 sys_context ("" userenv "","" session_user "")) ";
38
39          else
40 raise_application_error (-20 005, "Рольнеустановлена");
41          end if;
42      end if;
43
44      return g_sel_pred;
45  end;
46
47  function update_function( p_schema in varchar2,
48                    p_object in varchar2 ) return varchar2
49  is
50  begin
51      if ( g_upd_pred is NULL )
52      then
53          if ( sys_context( g_app_ctx, “RoleName” ) = “EMP” )
54          then
55          g_upd_pred := “1=0”;
56
57 elsif (sys_context (g_app_ctx, "RoleName") = "MGR")
58          then
59          g_upd_pred :=
60             ” empno in ( select empno
61                    from emp_base_table
62                   where mgr =
63 sys_context ("" Hr_App_Ctx "","" EmpNo "")) ";
64
65 elsif (sys_context (g_app_ctx, "RoleName") = "HR_REP")
66          then
67          g_upd_pred := “deptno in
68                   ( select deptno
69                       from hr_reps
70                      where username =
71              sys_context(“”userenv””,””session_user””) )”;
72
73          else
74 raise_application_error (-20 005, "Рольнеустановлена");
75          end if;
76      end if;
77
78      return g_upd_pred;
79  end;
80
81  function insert_delete_function( p_schema in varchar2,
82                       p_object in varchar2 ) return varchar2
83  is
84  begin
85      if ( g_ins_del_pred is NULL )
86      then
87 if (sys_context (g_app_ctx, "RoleName") in ("EMP", "MGR"))
88          then
89          g_ins_del_pred := “1=0”;
90 elsif (sys_context (g_app_ctx, "RoleName") = "HR_REP")
91          then
92          g_upd_pred := “deptno in
93                   ( select deptno
94                       from hr_reps
95                      where username =
96 sys_context ("" userenv "","" session_user "")) ";
97          else
98 raise_application_error (-20 005, "Рольнеустановлена");
99          end if;
100      end if;
101      return g_ins_del_pred;
102  end;
103
104  end;
/
Package body created.

Раніше, без Детального Контролю Доступу використання однієї таблиці з описаними вище трьома предикатами можна було досягти за допомогою 3-х уявлень – по одному на кожну операцію SELECT, UPDATE і INSERT / DELETE. Детальний Контроль Доступу дозволяє скоротити кількість об'єктів до однієї таблиці з динамічним предикатом.


Згадайте логіку, описану раніше:



 11  function select_function( p_schema in varchar2,
 12                    p_object in varchar2 ) return varchar2
 13  is
 14  begin
 15
 16      if ( g_sel_pred is NULL )
 17      then 
логіка для присвоєння значення g_sel_pred,
– Глобальної змінної, оголошеної в тілі пакету
 
42      end if;
 43
 44      return g_sel_pred;
 45  end;

У цій функції мінлива g_sel_pred встановлюється в непорожнє значення точно один раз за сесію. Якщо при попередньому виклику цієї предикатной функції предикат вже встановлений – він просто повертається знову. У цьому є дві переваги:


Останній етап цього процесу полягає у зв'язуванні предикатів з кожної DML-операцією і самою таблицею EMP. Далі показана реалізація цієї операції:



SQL> – Додамо політику до подання EMP. Кожна функція пакету
SQL> – HR_PREDICATE_PKG зв'язується з таблицею для виконання операцій
SQL> – SELECT / INSERTUPDATE / DELETE. При INSERT і UPDATE встановимо прапор
SQL> – "update_check" взначеніе TRUE. Це дуже схоже на створення
SQL> – уявлення з "CHECK OPTION"
SQL> – У цьому випадку забезпечується, що дані, створювані в базі
SQL> – даних, – це ті дані, які користувач може
SQL> – побачити.

SQL> begin
  2     dbms_rls.add_policy
  3     ( object_schema   => “RLS”,
  4       object_name     => “EMP”,
  5       policy_name     => “HR_APP_SELECT_POLICY”,
  6       function_schema => “RLS”,
7 policy_function => "HR_PREDICATE_PKG.SELECT_FUNCTION",
  8       statement_types => “select” );
  9  end;
10  /

PL/SQL procedure successfully completed.

 SQL> begin
  2     dbms_rls.add_policy
  3     ( object_schema   => “RLS”,
  4       object_name     => “EMP”,
  5       policy_name     => “HR_APP_UPDATE_POLICY”,
  6       function_schema => “RLS”,
7 policy_function => "HR_PREDICATE_PKG.UPDATE_FUNCTION",
  8       statement_types => “update” ,
  9       update_check    => TRUE );
 10  end;
 11  /

 PL/SQL procedure successfully completed.

SQL> begin
  2     dbms_rls.add_policy
  3     ( object_schema   => “RLS”,
  4       object_name     => “EMP”,
  5       policy_name     => “HR_APP_INSERT_DELETE_POLICY”,
  6       function_schema => “RLS”,
7 policy_function => "HR_PREDICATE_PKG.INSERT_DELETE_FUNCTION",
  8       statement_types => “insert, delete” ,
  9       update_check    => TRUE );
 10  end;
 11  /

 PL/SQL procedure successfully completed.

Таким чином, з кожною DML-операцією зв'язується різна предикатну функція. Коли користувач запитує дані таблиці EMP, до запиту буде приєднуватися предикат, сформований пакетної функцією hr_predicate_pkg.select. Коли користувач оновлює таблицю, буде використовуватися пакетна функція update і так далі.


Тепер протестуємо додаток. Для цього створимо пакет HR_APP. Цей пакет являє собою додаток. У нього входять функції для виконання:


Приєднаємося різними користувачами з різними ролями і простежимо за поведінкою нашого застосування. Це дозволить побачити, як працює детальний контроль доступу .


Нижче показано специфікація програми:



SQL> create or replace package hr_app
  2  as
  3      procedure listEmps;
  4
  5      procedure updateSal;
  6
  7      procedure deleteAll;
  8
  9      procedure insertNew( p_deptno in number );
 10  end;
 11  /

Package created.

Телопакета:
SQL> create or replace package body hr_app
  2  as
  3
  4  procedure listEmps
  5  as
  6      l_AppCtx     dbms_session.AppCtxTabTyp;
  7      l_size       number;
  8  begin
9 dbms_output.put_line ("—— КонтекстСессіі ———-");
 10      dbms_session.list_context( l_AppCtx, l_size );
 11      for i in 1 .. l_size loop
 12          dbms_output.put( l_AppCtx(i).namespace // “.” );
 13          dbms_output.put( l_AppCtx(i).attribute // ” = ” );
 14          dbms_output.put_line( l_AppCtx(i).value );
 15      end loop;
 16
17 dbms_output.put_line ("—— Данниетабліци Emp, коториеможноувідеть —–");
 18      for x in ( select ename, sal, dname
 19               from emp, dept
 20              where emp.deptno = dept.deptno )
 21      loop
22 dbms_output.put_line (x.ename / / "," / / x.sal / / "," / / x.dname);
 23      end loop;
 24  end;
 25
 26
 27  procedure updateSal
 28  is
 29  begin
 30      update emp set sal = 9999;
31 dbms_output.put_line (sql% rowcount / / "строкобновлено");
 32  end;
 33
 34  procedure deleteAll
 35  is
 36  begin
37 delete from emp where empno <> sys_context ("Hr_app_Ctx", "EMPNO");
38 dbms_output.put_line (sql% rowcount / / "строкудалено");
 39  end;
 40
 41  procedure insertNew( p_deptno in number ) 
 42  as
 43  begin
44 insert into emp (empno, deptno, sal) values (123, p_deptno, 1111);
 45  end;
 46
 47  end hr_app;
 48  /

Package body created.

 SQL> grant execute on hr_app to public
 2  /
Grant succeeded.

Отже, "додаток" створено. Процедура listEmps показує всі записи, які можна побачити через подання EMP. Процедура updateSal оновлює кожен запис, до якої можна отримати доступ. Процедура deleteAll видаляє кожен запис, до якої можна отримати доступ, за винятком запису, що ідентифікує користувача. Процедура insertNew створює нового службовця в заданому відділі. Ця програма просто тестує всі DML-операції над поданням EMP, які можна було б виконати.

Частина 4


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*