Известно, что роль 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. Места здесь немного, и в связи с этим, наверное, и введено ограничение на максимальное количество разрешённых ролей в сеансе.
Выводы:
- Параметр MAX_ENABLED_ROLE, начиная с версии Oracle 10.1, является устаревшим.
- Изменить параметр можно, но его значение не используется Oracle.
- В сеансе можно разрешить до 148 ролей, это жёстко прописанное ограничение, не относящееся к параметру MAX_ENABLED_ROLE.
- Список включённых ролей находится в UGA, причём в его фиксированной части (Fixed UGA).
- Количество включённых ролей влияет на размер списка, но не на размер выделенной памяти в UGA.