При разработке приложений в APEX часто нужны разные уровни доступа для пользователей – например, когда приложением пользуются юзеры из разных подразделений, или когда нужно создать отчеты, видимые только админам.

Доступ к объектам приложения управляется через схемы авторизации (Authorization Schemes), которые находятся в Shared Components приложения.

Не нужно путать авторизацию и аутентификацию в APEX. Схемы аутентификации управляют доступом пользователей к приложению (возможностью залогиниться), а авторизации – доступом к определенным объектам приложения. 

Аутентификация находится за рамками данной статью, но про нее уже есть хороший материал.

Одним из вариантов разграничения доступа, то есть внедрения ролевой модели, может быть постраничная авторизация – это схема, при которой явно указываются страницы, доступные для каждого пользователя (или роли).

Рассмотрим для примера приложение, в котором есть два отчета – Admin Report и User Report и пользователи– ADMIN, USER1 и USER2.

Нужно сделать так, чтобы ADMIN видел все страницы, а USER’ы не видел Admin Report.

 

1. Определение ролевой модели

Для начала, нужно определиться, как будет выглядеть ролевая модель: для небольших  приложений имеет смысл определять список доступных страниц напрямую для каждого пользователя, но если в приложении много страниц или пользователей, удобнее будет ввести роли и сделать две связки "пользователь -> роль" и "роль -> страница", то есть каждому пользователю присвоить роль, и для каждой роли настроить доступ к определенным страницам.

Мы пойдем по второму пути, так как ожидаем, что в последствие число пользователей вырастет.

Введем две роли ADMIN и USER и создадим таблицу USER_ROLE

и таблицу ROLE_PAGE

Как видим, для роли USER не указан доступ к странице 2.

Номера страниц можно посмотреть в Application Builder в APEX или через представления APEX в БД:

select *
  from APEX_APPLICATION_PAGES
  where APPLICATION_ID = <номер вашего приложения>

 

2.  Создание схемы авторизации

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

create or replace function PAGE_AUTH (p_user in varchar2,
                                      p_page_id in number)
    return boolean 
  as
   
l_cnt number;
  begin
    select count
(*) into l_cnt
      from USER_ROLE ur 
           join ROLE_PAGE rp on ur.user_role = rp.user_role
      where upper(p_user)=upper(ur.user_name)
            and p_page_id = rp.page_id;
       
      if l_cnt > 0 then
        
return TRUE;
         else return FALSE;
      end if;
   end PAGE_AUTH;

 

В приложении APEX создадим новую схему авторизации. В Shared components -> Authorization Schemes нажимаем на кнопку “Сreate” и указываем “From scratch”. Выбираем имя схемы, указываем созданную выше функцию и выбираем  “Validate one per page view”:

3. Выбор схемы авторизации

Теперь нужно выбрать созданную схему для данного приложения. Для этого идем в Shared components -> User Interface Attributes (блок User Interface). В разделе Security ->Authorization выбираем созданную схему и жмем “Apply Changes”.

 

 

Поздравляем, вы только что создали постраничную схему авторизации!

Теперь при попытке USER1 и USER2 посмотреть Admin Report они будут видеть следующее:

 

4. Изменение навигационного меню (Navigation Menu)

Если страницы в приложении привязаны к объектам навигационного меню, то все эти объекты останутся видимыми для всех пользователей, поскольку авторизация  привязана к страницам, а не к пунктом навигационного меню (Navigation Menu List Entries). То есть пользователи USER1и USER2 будут видеть в навигации Admin Report, не смотря на то, что  при нажатии на него будут получать ошибку доступа.

Можно оставить и такую реализацию, но лучше скрыть от пользователей из меню те страницы, на которые они все равно не могут перейти.

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

Смысл в том, чтобы взять объекты статического меню (обычно называется 'Desktop Navigation Menu'), посмотреть, с какими страницами связаны его объекты, и с использованием созданной ролевой модели, показывать пользователю только те List Entries, которые связаны с доступными ему страницами.

Воспользуемся APEX  представлением (view)  APEX_APPLICATION_LIST_ENTRIES, в котором есть информация, какие страницы прикреплены к каким объектам навигации. Для того, чтобы не поддерживать отдельно еще одну таблицу со связью Роль -> Объект навигации, создадим представление, связывающее роли и List Entries на основе связи Роль –> Страница:

SET DEFINE OFF;
CREATE OR REPLACE EDITIONABLE VIEW ROLE_LIST_ENTRY ("USER_ROLE", "LIST_ENTRY_ID") AS
  select distinct
p.user_role, l.list_entry_id
  from apex_application_list_entries l
      join role_page p on l.entry_target like 'f?p=&APP_ID.:'||p.page_id||':&%SESSION%' or entry_target is null
  where
l.list_name = 'Desktop Navigation Menu'
     
and l.application_id = <номер вашего приложения>

Если сделать select из созданного представления, то можно увидеть, что для роли ADMIN доступны 3 list entry, а для USER – только 2: 

Пришло время сделать динамическое навигационное меню. Для этого в Shared Components -> Navigation Menu создаем новое меню: выбираем “From scratch” и Dynamic, в появившееся окно SQL запроса вписываем следующее:

select level, al.entry_text label, al.entry_target

from apex_application_list_entries al

  join ROLE_LIST_ENTRY rl on al.list_entry_id = rl.list_entry_id

  join USER_ROLE ur on ur.user_role = rl.user_role and upper(ur.user_name) = upper(:APP_USER)

where

  al.LIST_NAME = 'Desktop Navigation Menu' 

  and al.application_id = <номер вашего приложения>

start with al.list_entry_parent_id is null

connect by prior al.list_entry_id = al.list_entry_parent_id 

order siblings by al.display_sequence

 

Теперь нужно выбрать Динамическое навигационное меню для нашего интерфейса. Для этого опять переходим в Shared Components -> User Interface Attributes. В разделе User Interface нажимаем на значок редактирования текущего интерфейса и попадаем в User Interface Details. Во вкладке Navigation Menu выбираем созданное нами меню и жмем  “Apply Changes”

Теперь USER1 и USER2 при входе в приложение не будут видеть в меню навигации Audit Report:

 

Схемы авторизации необходимы для больших приложений, постраничная авторизация отличается простотой поддержки и хорошей масштабируемостью – при создании новых страниц и добавлении новых пользователей нужно только добавить соответствующие наблюдения в USER_ROLE и ROLE_PAGE, для чего можно сделать соответствующие формы в самом приложении (и не забыть указать роль администратора для этих форм).

Другие статьи на тему APEX: