(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Детальный контроль доступа и контексты приложения. Часть 2

Источник: Oracle Magazine RE
Том Кайт

Часть 1

Важное предупреждение

Существует одна важная особенность реализации вышеописанной предикатной функции безопасности - фактически во время заданной сессии эта функция обязана возвращать постоянный предикат - это существенно. Если посмотреть на описанную выше функцию еще раз, видно логику:

  3  as
4  begin
5      if ( user = 'RLS_ADMIN' ) then
6          return '';
7      else
8          return 'owner = USER';
9      end if;
10  end;

Эта предикатная функция либо не возвращает никакого предиката, либо возвращает "owner = USER". Во время заданной сессии она постоянно будет возвращать один и тот же предикат. Ситуация, когда получен предикат "owner = USER", а затем в этой же сессии - пустой предикат "", возникнуть не может. Для того, чтобы понять, почему это крайне необходимо для корректного проектирования приложений с детальным контролем доступа , следует понять, когда предикат связывается с запросом и как различные среды обрабатывают эту ситуацию.

Предположим, что написана следующая предикатная функция:

SQL> create or replace function rls_examp
  2  ( p_schema in varchar2, p_object in varchar2 )
  3  return varchar2
  4  as
  5  begin
  6          if ( sys_context( 'myctx', 'x' ) is not null )
  7          then
  8                  return 'x > 0';
  9          else
 10                  return '1=0';
 11          end if;
 12  end;
/

Это показывает, что если атрибут "x" контекста установлен, то предикат должен иметь значение "x > 0". Если атрибут "x" контекста не установлен, то предикат должен быть "1=0". При создании таблицы T, добавьте в нее данные, политику и контекст так, как показано ниже:

SQL> create table t ( x int );
Table created.
SQL> insert into t values ( 1234 );
1 row created.
SQL> begin
  2     dbms_rls.add_policy
 
3     ( object_schema   => user, object_name => 'T',
  4       policy_name => 'T_POLICY', function_schema => user,
  5       policy_function => 'rls_examp', statement_types => 'select' );
  6  end;
  7  /
PL/SQL procedure successfully completed.
SQL> create or replace procedure set_ctx( p_val in varchar2 )
  2  as
  3  begin
  4          dbms_session.set_context( 'myctx', 'x', p_val );
  5  end;
  6  /
Procedure created.
SQL> create or replace context myctx using set_ctx;
Context created.

Такая политика означает, что если контекст установлен, можно будет увидеть 1 строку. Если контекст не установлен, ни одной строки не будет видно. Действительно, если провести тест в SQLPLUS, непосредственно выполняя SQL, то получится следующий результат:

SQL> exec set_ctx( null );
PL/SQL procedure successfully completed.
SQL> select * from t;
no rows selected
SQL> exec set_ctx( 1 ) ;
PL/SQL procedure successfully completed.
SQL> select * from t;
     X
---------
  1234

Таким образом, выбрались те данные, которые ожидались. Динамический предикат работает так, как ожидалось. В действительности же, если использовать PL/SQL (Pro*C или приложения, написанные на OCI, а также многие другие исполняемые среды) обнаруживается, что вышеописанный результат неверен. Создадим, например, небольшую PL/SQL-процедуру:

SQL> create or replace procedure dump_t
  2  ( some_input in number default NULL )
  3  as
  4  begin
  5          dbms_output.put_line
  6          ('*** Результат работы SELECT * FROM T' );
  7 
  8          for x in (select * from t ) loop
  9                  dbms_output.put_line( x.x );
 10          end loop;
 11 
 12 
 13          if ( some_input is not null )
 14          then
 15                  dbms_output.put_line
 16                  ('*** Результат работы другого SELECT * FROM T' );
 17 
 18                  for x in (select * from t ) loop
 19                          dbms_output.put_line( x.x );
 20                  end loop;
 21          end if;
 22  end;
 23  /
Procedure created.

В первый раз простой "select * from T" в этой процедуре выполняется, когда входной параметр не задан, и во второй раз, когда задано его некоторое значение. Давайте выполним эту процедуру и посмотрим результат:

SQL> -- Включим вывод на экран результат dbms_output.put_line
SQL> set serveroutput on
SQL> -- отменим установку контекста - присвоим X значение NULL
SQL> exec set_ctx( NULL )
PL/SQL procedure successfully completed.
SQL> -- выполним процедуру. Заметьте, что
SQL> -- some_input по умолчанию может быть NULL.
SQL> -- Выполнится только 1-ый select * from t.
SQL> -- Как и ожидалось, выбрано НОЛЬ строк, так как
SQL> -- использовался предикат 1=0
SQL> exec dump_t
*** Результат работы SELECT * FROM T
PL/SQL procedure successfully completed.
SQL> -- Теперь установим значение контекста в ненулевое значение.
SQL> exec set_ctx( 1 )
PL/SQL procedure successfully completed.
SQL> -- Так как таблица t содержит 1 строку со значением 1234, а
SQL> -- предикат должен быть "x > 0", когда этот атрибут установлен, то
SQL> -- для получения данных можно выполнить запрос к таблице T.
SQL> --
SQL> -- Чтобы убедиться в том, что результат может оказаться неверным,
SQL> -- выполним процедуру dump_t с некоторым НЕНУЛУВЫМ входным параметром.
SQL> -- В этом случае выполнятся оба select * from T
SQL> --
SQL> -- Следует обратить внимание на то, что при первом выполнении
SQL> -- "select * from T" никакие данные не возвращаются!
SQL> -- А при втором - возвращаются!
SQL> --
SQL> -- Почему? Смотрите далее
SQL> exec dump_t( 0 )
*** Результат работы SELECT * FROM T
*** Результат работы другого SELECT * FROM T
1234
PL/SQL procedure successfully completed.

Итак, при запуске процедуры с атрибутом контекста "x", установленным в значение null, получен ожидаемый результат (так как в этой сессии процедура была запущена первый раз). Затем контекстный атрибут "x" был установлен в ненулевое значение, и результат получился "противоречивый". Первый select * from t в процедуре снова не возвратил ни одной строки - он, скорее всего, все еще использует предикат "1=0". Второй запрос (тот, что в первый раз не выполнялся) возвратил, казалось бы, корректный результат - он, как и ожидалось, использует предикат "x > 0",.

Почему первый запрос в этой процедуре не использовал предикат, который предполагался? Это произошло из-за оптимизации, называемой "кэширование курсора". На самом деле PL/SQL и многие другие исполняемые среды не закрывают курсор по команде "закрыть". Вышеописанный пример может быть легко воспроизведен, например, в Pro*C, если опцию предкомпилятора "release_cursor" оставить в значении по умолчанию NO. Если тот же самый код перекомпилировать с опцией release_cursor=YES, то программа Pro*C будет вести себя более похоже на запросы в SQLPLUS. Предикат, используемый DBMS_RLS, связывается с запросом во время фазы PARSE. Первый запрос "select * from T" разбирается во время первого выполнения хранимой процедуры - когда предикат действительно был равен "1=0". Инструмент PL/SQL кэширует этот разобранный курсор. Во второй раз при выполнении хранимой процедуры PL/SQL просто повторно использует разобранный курсор из первого "select * from T", при этом разобранный запрос имеет предикат "1=0" - предикатная функция в этот момент вообще не вызывалась. Так как процедуре передаются также некоторые входные данные, PL/SQL выполнил второй запрос. Этот запрос, однако, уже не является открытым и разобранным, поэтому он разбирается во время его выполнения - когда контекстный атрибут НЕ ПУСТОЙ. Второй "select * from t" использует связанный предикат "x>0". Отсюда и противоречивость. Так как в общем случае контроль за кешированием этих курсоров не осуществляется, то предикатную функцию безопасности, возвращающую более 1 предиката за сессию, следует, во что бы то ни стало, избегать. В противном случае в будущем придется с большим трудом отыскивать ошибки приложения. В следующем примере я продемонстрирую, как построить предикатную функцию безопасности, которая не сможет возвратить более одного предиката за сессию. В этом случае гарантируется, что:

  • Результаты одного запроса не противоречат результатам другого при работе с детальным контролем доступа.
  • Изменить предикат в середине сессии не удастся, иначе можно получить неопределенные непредсказуемые результаты.
  • Предприняты меры для того, чтобы убедить политику безопасности в том, что имеющийся предикат - единственный для пользователя, и не пытаться возвратить предикат, специализированный под пользователя, работающего в текущем окружении.

Я настоятельно советую создавать все нетривиальные предикатные функции так же, как показано в следующем примере. Это позволит создавать хорошо работающие, предикабельные приложения, поддерживающие детальный контроль доступа .

Следует добавить, что в некоторых случаях изменение предиката в середине сессии желательно. Для достижения наилучших результатов клиентские приложения, имеющие доступ к объектам, которые поддерживаются политикой, позволяющей изменять предикаты в середине сессии, должны быть написаны в особой форме. Например, во избежание кэширования курсора в PL/SQL необходимо написать приложение, полностью использующее динамический sql. Если используется этот динамический предикатный метод, то необходимо иметь в виду, что результаты будут зависеть от того, как написано клиентское приложение. Поэтому не следует применять политику безопасности с использованием этой возможности. Мы не будем рассматривать использование всех возможностей DBMS_RLS, а лучше сконцентрируемся на ее конкретном использовании - для защиты данных.

Пример 2. Использование контекстов приложения

Необходимо, например, реализовать политику безопасности в подсистеме "Кадры" (Human Resources Security Policy ). В этом примере будут использоваться таблицы EMP и DEPT демонстрационного пользователя SCOTT/TIGER и добавится еще одна таблица, которая позволит назначить человека на должность контролера. Далее перечислены требования:

  • Менеджер отдела может:
    • читать свою запись, записи всех сотрудников, которые ему подчиняются и всех сотрудников, которые в свою очередь подчиняются этим подчиненным (по иерархии)
    • обновлять записи всех служащих, которые подчиняются ему непосредственно.
    • Служащий может:
      • читать свою запись.
    • Контролер может:
      • читать все записи по отделу, в котором он работает (В этом приложении контролер работает в одном из отделов)
      • обновлять любую запись заданного отдела
      • вставлять в заданный отдел.
      • удалять из заданного отдела.

    Как было сказано ранее, приложение будет использовать существующие таблицы EMP и DEPT пользователя SCOTT и добавочную таблицу HR_REPS для связи контролера с отделом. Схема будет выглядеть следующим образом:

    SQL> -- создадим демонстрационную схему.  Она основывается на таблицах
    SQL> -- EMP и DEPT, владелец которых scott.  Добавим в схему
    SQL> -- описатель RI (идентификатор контролера) и переименуем
    SQL> -- значения поля ENAME в таблице EMP
    SQL> -- так, чтобы они соответствовали именам пользователей тестируемой
    SQL> -- базы данных (например: пользователю RLS_KING соответствует имя
    SQL> -- пользователя RLS_KING в таблице emp)
    SQL> create table dept as select * from scott.dept;
    Table created.
    SQL> alter table dept add constraint dept_pk primary key(deptno);
    Table altered.
    SQL> create table emp_base_table as select * from scott.emp;
    Table created.
    SQL> update emp_base_table set ename = 'RLS_' // ename;
    14 rows updated.
    SQL> alter table emp_base_table add constraint emp_pk primary key(empno);
    Table altered.
    SQL> alter table emp_base_table add constraint emp_fk_to_dept
       2  foreign key (deptno) references dept(deptno);
    Table altered.
    SQL> -- создадим индексы, которые будут использоваться функцией
    SQL> -- контекста приложения для повышения производительности.
    SQL> -- Необходимо быстро определить, является ли
    SQL> -- некоторый пользователь mgr (менеджером) отдела. Кроме того,
    SQL> -- необходимо быстро конвертировать имя пользователя в empno
    SQL> create index emp_mgr_deptno_idx on emp_base_table(mgr);
    Index created.
    SQL> create unique index emp_ename_idx on emp_base_table(ename);
    Index created.
    SQL> -- Кроме того, создадим представление EMP на основе запроса 
    SQL> -- select * from emp_base_table. К этому ПРЕДСТАВЛЕНИЮ
    SQL> -- будет применена политика, и через него
    SQL> -- приложения будут запрашивать/вставлять/обновлять и так далее.
    SQL> create view emp as select * from emp_base_table;
    View created.
    SQL> -- создадим таблицу для управления HR_REPS.  Для этого будет 
    SQL> -- использоваться INDEX ORGANIZED TABLE, так как всегда будет
    SQL> -- выполняться запрос только такого типа, как
    SQL> -- "select * from hr_reps where username = X and deptno = Y".
    SQL> -- В использовании таблицы нет необходимости, достаточно
    SQL> -- использовать только индекс.
    SQL> create table hr_reps
      2  (   username    varchar2(30),
      3      deptno        number,
      4      primary key(username,deptno)
      5  )
      6  organization index;
    Table created.
    SQL> -- Свяжем HR Reps с отделами.  KING может видеть все отделы.
    SQL> insert into hr_reps values ( 'RLS_JONES', 10 );
    SQL> insert into hr_reps values ( 'RLS_BLAKE', 20 );
    SQL> insert into hr_reps values ( 'RLS_CLARK', 30 );
    SQL> insert into hr_reps values ( 'RLS_KING', 10 );
    SQL> insert into hr_reps values ( 'RLS_KING', 20 );
    SQL> insert into hr_reps values ( 'RLS_KING', 30 );
    SQL> insert into hr_reps values ( 'RLS', 10 );
    SQL> commit;
    Commit complete.

    Часть 3



     Распечатать »
     Правила публикации »
      Написать редактору 
     Рекомендовать » Дата публикации: 12.01.2002 
     

    Магазин программного обеспечения   WWW.ITSHOP.RU
    Oracle Database Standard Edition 2 Named User Plus License
    Oracle Database Standard Edition 2 Processor License
    Oracle Database Personal Edition Named User Plus Software Update License & Support
    Oracle Database Personal Edition Named User Plus License
    ABBYY Lingvo x6 Многоязычная Домашняя версия, электронный ключ
     
    Другие предложения...
     
    Курсы обучения   WWW.ITSHOP.RU
     
    Другие предложения...
     
    Магазин сертификационных экзаменов   WWW.ITSHOP.RU
     
    Другие предложения...
     
    3D Принтеры | 3D Печать   WWW.ITSHOP.RU
     
    Другие предложения...
     
    Новости по теме
     
    Рассылки Subscribe.ru
    Информационные технологии: CASE, RAD, ERP, OLAP
    Новости ITShop.ru - ПО, книги, документация, курсы обучения
    Программирование на Microsoft Access
    CASE-технологии
    Программирование в AutoCAD
    СУБД Oracle "с нуля"
    Новости мира 3D-ускорителей
     
    Статьи по теме
     
    Новинки каталога Download
     
    Исходники
     
    Документация
     
     



        
    rambler's top100 Rambler's Top100