Известно, что роль Oracle предоставленная пользователю в период действия сеанса может находиться в одном из двух состояний разрешённом или запрещённом. В запрещённом состоянии объектные привилегии, выданные роли не действуют. Роль как бы выключается из использования. Кроме разрешения и запрещения, у роли выданной пользователю имеется ещё один атрибут состояния – роль по умолчанию. Если роль назначена пользователю по умолчанию, то она автоматически разрешается при подключении пользователя к базе данных. Обычно, когда роль выдаётся пользователю, она уже предоставляется в режиме по умолчанию, то есть всегда разрешена. Но если выполнить, к примеру, для пользователя команду ALTER USER … DEFAULT ROLE NONE, то все последующие предоставления ролей пользователю не будут устанавливать для них этот режим, то есть роли при подключении будут всегда выключены.

Изменить список ролей по умолчанию можно с помощью конструкции ALTER USER … DEFAULT ROLE. Например, с помощью оператора ALTER USER … DEFAULT ROLE ALL EXCEPT … можно сделать по умолчанию только часть выданных пользователю ролей. В этом случае привилегии этих выбранных ролей будут автоматически доступны сразу при подключении, тогда как для задействования остальных привилегий надо будет дополнительно включать роли.

Выключенную роль в течение сеанса можно принудительно включить с помощью команды SET ROLE. Правда включение с помощью этой команды только лишь одной роли (или группы ролей), автоматически приведёт к тому, что все остальные неуказанные в списке роли пользователя будут выключены. Поэтому, надо всегда указывать в команде полный список ролей, которые должны находиться в текущий момент времени в разрешённом состоянии. Такой список естественно не безграничен. Он определяется параметром инициализации max_enabled_roles и показывает максимальное количество одновременно разрешённых ролей (включая вложенные) для сеанса. Если обратиться к документации, то данный параметр может принимать граничные значения от 0 до 148. То есть всего для пользователя может быть разрешено максимально в сеансе до 148 ролей (плюс ещё две роли PUBLIC и собственная роль пользователя).

Начиная с версии Oracle 10.1, параметр max_enabled_roles является устаревшим, и имеет значение по умолчанию 150. Значение параметра конечно можно по-прежнему изменять, но максимальное количество одновремённо разрешённых ролей в сеансе от этого, увы, не измениться. Оно по-прежнему будет составлять 148.

Почему Oracle ограничивает максимальное количество разрешенных ролей?

Если заглянуть в документацию Oracle Database Security Guide, то мы найдём небольшой намёк на объяснение этого ограничения. В документации явно указано, что увеличение значения параметра max_enabled_roles ведёт к увеличению памяти используемой сеансом. Далее объясняется, что для каждой включённой роли каждого сеанса в PGA выделяется по 4 байта памяти. Естественно если сеансов будет много и список разрешённых ролей будет большой, то всё это, в конце концов, может привести к неоправданному расходу памяти. Однако, так ли это на самом деле, ведь количество выделяемой памяти для списка ролей не так уж велико. Продолжим поиски.

Следуя материалам вышеуказанной документации логично предположить, что в области PGA для каждого сеанса существует список разрешённых ролей. Поищем подтверждение этого факта в книге Стива Адамса «Oracle8i Internal Services for Waits, Latches, Locks, and Memory». В разделе про UGA мы находим информацию о том, что эта область содержит список, разрешённый ролей для сеанса. Здесь же указано, что от параметра max_enabled_roles зависит размер некоторых частей памяти UGA. Исходя из этого, можно сделать предположение, что в одной из области памяти UGA выбранного сеанса имеется список включённых ролей, размером по 4 байта на каждую роль, максимально ограниченный сверху параметром инициализации max_enabled_roles. Проверим это предположение на практике.

Для примера будем использовать Oracle 10g (версия 10.2.0.5). Первым делом создадим пользователя user1, с помощью которого будем делать дамп областей памяти:

SYSTEM@ALFA10G> GRANT alter session, create session TO user1 IDENTIFIED BY pass;

Grant succeeded

Пользователь создан. Теперь создадим роли и выдадим их пользователю. Так как ролей будет много, для удобства применим следующий PL/SQL блок:

DECLARE
  i INTEGER;
BEGIN
  FOR i IN 1..5 LOOP
    EXECUTE IMMEDIATE 'CREATE ROLE ROLE' || i;
    EXECUTE IMMEDIATE 'GRANT ROLE' || i ||' TO user1';
  END LOOP;
END;
/

Для начала создадим с помощью этого блока пять ролей и выдадим их пользователю user1. Все роли будут предоставлены в режиме по умолчанию и будут сразу разрешены после подключения пользователя к базе данных. Проверим это с помощью системного представления SESSION_ROLES, показывающего список разрешённых ролей для сеанса:

SQL> CONNECT user1/pass@alfa10g;

Подключение к:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production 
With the Partitioning, OLAP, Data Mining and Real Application Testing options
 
SQL> SELECT * FROM SESSION_ROLES;
 
ROLE 
-----
ROLE1
ROLE2
ROLE3
ROLE4
ROLE5

Выбрано: 5 строк

Роли включены. Сделаем расширенный дамп UGA сеанса пользователя user1:

SQL> ALTER SESSION SET EVENTS 'immediate trace name heapdump level 4100';

Сеанс изменён

Здесь следует уточнить, что мы делаем дамп не всей области UGA пользователя. Известно, что UGA состоит из двух частей, так называемой фиксированной (Fixed UGA) и переменной (UGA heap). Так как список разрешённых ролей меняется, то логично предположить, что он будет располагаться в heap области. Поэтому для начала сделаем дамп этой области UGA.

В полученном файле дампа UGA есть интересная часть памяти (chunk) под названием kxs-krole:

  Chunk  81796e0 sz=       40    freeable  "kxs-krole      "
Dump of memory from 0x081796E0 to 0x08179708
81796E0 00000029 08179670 03550630 00000001  [)...p...0.U.....]
81796F0 00000024 00000567 00000568 00000569  [$...g...h...i...]
8179700 0000056A 0000056B                    [j...k...

Последние пять значений длиной по 4 байта этого chunk очень сильно напоминают идентификаторы ролей из системной таблицы SYS.USER$. Выведем их следующим запросом:

SQL>   SELECT TO_CHAR(user#, 'XXX'), user#, name 
  2>     FROM sys.user$ 
  3>    WHERE name LIKE 'ROLE%' 
  4> ORDER BY user#

TO_CHAR(USER#,'XXX') USER# NAME   
-------------------- ----- -------
 567                 1383  ROLE1  
 568                 1384  ROLE2  
 569                 1385  ROLE3  
 56A                 1386  ROLE4  
 56B                 1387  ROLE5

Выбрано: 5 строк

Так и есть. Действительно в данном chunk содержатся идентификаторы включённых ролей предоставленных пользователю. Чтобы убедиться в том, что эти роли, действительно включенные, а не предоставленные попробуем изменить список ролей по умолчанию для пользователя user1. Для этого исключим роль role5 из списка умалчиваемых:

SYSTEM@ALFA10G> ALTER USER user1 DEFAULT ROLE ALL EXCEPT role5;

Пользователь изменён

Снова подключимся под пользователем user1 и выведем список разрешённых ролей:

SQL> SELECT * FROM SESSION_ROLES;

ROLE 
-----
ROLE1
ROLE2
ROLE3
ROLE4

Выбрано: 4 строки

Роль role5 теперь выключена. Сделаем дамп UGA:

  Chunk  817a4b8 sz=       36    freeable  "kxs-krole      "
Dump of memory from 0x0817A4B8 to 0x0817A4DC
817A4B0                   00000025 0817A45C          [%...\...]
817A4C0 03550630 00000001 00000024 00000567  [0.U.....$...g...]
817A4D0 00000568 00000569 0000056A           [h...i...j...]

Ищем в файле дампа уже известный нам chunk. Кода роли 56B в списке нет. Размер chunk тоже уменьшился. Всё как бы подтвердилось, данная структура памяти действительно содержит только список разрешённых ролей для сеанса. Если теперь мы попробуем с помощью скрипта выдать пользователю user1 более чем 148 ролей, то при следующем подключении пользователя мы получим ошибку:

SQL> CONNECT user1/pass@alfa10g;
ORA-28031: превышен максимум 148 разрешенных ролей

Таким образом, параметр max_enabled_roles как бы ограничивает размер памяти выделяемой под список разрешённых ролей. Может это и является действительной причиной ограничения. Не будем спешить.

Роль role5 у нас в сеансе сейчас выключена, так как не находится в списке ролей по умолчанию. Попробуем включить её с помощью команды SET ROLE:

SQL> SET ROLE role5;

Роль включена

SQL> SELECT * FROM SESSION_ROLES;
 
ROLE 
-----
ROLE5

Выбрано: 1 строка

Теперь у нас в сеансе разрешена только одна роль role5. Сделаем дамп UGA:

  Chunk  817a4b8 sz=       36    freeable  "kxs-krole      "
Dump of memory from 0x0817A4B8 to 0x0817A4DC
817A4B0                   00000025 0817A45C          [%...\...]
817A4C0 03550630 00000001 00000024 00000567  [0.U.....$...g...]
817A4D0 00000568 00000569 0000056A           [h...i...j...]    

Участок памяти не изменился. Так значит, данный список содержит в себе только роли находящиеся в режиме по умолчанию, и никакого отношения к действительно включённым ролям этот список не имеет. Поиск по файлу дампа в других структурах памяти успехов так же дал. Списка включённых ролей в этой части области UGA просто нет. А ограничивать максимальное количество включённых ролей ради списка ролей по умолчанию, смысла нет. Ведь если роли не находятся в режиме по умолчанию, то данный chunk будет отсутствовать в UGA.

Раз списка включённых ролей в изменяемой (heap) части UGA нет, то поищем её в фиксированной области. По идее фиксированная область не должна меняться. Она содержит ограниченный круг атомарных переменных, малых структур данных и указателей кучи UGA.

Дам фиксированной области UGA можно получить двумя способами. Так как в случае выделенного сервера память UGA размещается в адресном пространстве PGA то можно попытаться сделать дамп кучи PGA и найти там область памяти фиксированной части UGA. Второй способ заключается в получении дампа UGA как глобальной области памяти (global area). Он более подробен. Там показываются переменные, структуры и их значения.

Для наглядности начнём с первого способа. Но перед этим предварительно исключим из списка по умолчанию все роли выданные пользователю user1:

SQL> ALTER USER user1 DEFAULT ROLE NONE;
Пользователь изменён

Подключившись под пользователем к базе данных, мы обнаружим, что у пользователя нет разрешённых ролей:

SQL> SELECT * FROM SESSION_ROLES;

ROLE
----

Выбрано: 0 строк

Теперь включим все роли:

SQL> SET ROLE ALL;

Роль включена

Где то в недрах UGA сеанса образовался список включённых ролей. Сделаем дамп кучи PGA:

SQL> ALTER SESSION SET EVENTS 'immediate trace name heapdump level 1025';

Сеанс изменён.

Находим в файле дампа область памяти с названием “Fixed Uga”:

  Chunk  81481f0 sz=    20668    freeable  "Fixed Uga      "
Dump of memory from 0x081481F0 to 0x0814D2AC

Почти в самом конце этой области обнаруживается наш список включённых ролей выданных пользователю user1:

814CDD0 00000000 00000001 00000024 00000567  [........$...g...]
814CDE0 00000568 00000569 0000056A 0000056B  [h...i...j...k...]
814CDF0 00000000 00000000 00000000 00000000  [................]

Сделаем более подробный дамп фиксированной области UGA вторым способом, чтобы посмотреть какой переменной или структуре принадлежит этот список:

SQL> ALTER SESSION SET EVENTS 'immediate trace name global_area level 4';
Сеанс изменён

И с удивлением узнаём, что дамп фиксированной области заканчивается на адресе 814CDA4, тоесть гораздо раньше, чем конец участка chunk:

ub4 ksmugmg2 [814CDA0, 814CDA4) = 0000CDCD

Оказывается все оставшееся пространство до конца области выделено под какие-то динамические структуры, среди которых и находится наш список (вернее два одинаковых списка) включённых ролей. Размер фиксированной части UGA не меняется, поэтому списки должны умещаться в оставшееся пространство chunk. Места здесь немного, и в связи с этим, наверное, и введено ограничение на максимальное количество разрешённых ролей в сеансе.

Выводы:

  1. Параметр MAX_ENABLED_ROLE, начиная с версии Oracle 10.1, является устаревшим.
  2. Изменить параметр можно, но его значение не используется Oracle.
  3. В сеансе можно разрешить до 148 ролей, это жёстко прописанное ограничение, не относящееся к параметру MAX_ENABLED_ROLE.
  4. Список включённых ролей находится в UGA, причём в его фиксированной части (Fixed UGA).
  5. Количество включённых ролей влияет на размер списка, но не на размер выделенной памяти в UGA.