3. クライアント識別子を用いた制御#
クライアント識別子やアプリケーション・コンテキストをアプリケーションから指定することで、同じDBユーザでも異なるwhere句を追加することができます。 ここで示すデモ手順では、クライアント識別子を設定することで、同じデータベースユーザーでも異なる行を表示させる制御を行います。
APPユーザーの作成#
ここでは新しくAPPユーザーを作成します。作成の流れはSALES_APPユーザーと同じです。
-- APPユーザーを作成
CREATE USER APP IDENTIFIED BY <password>
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP;
-- セッション作成権限を付与
GRANT CREATE SESSION TO APP;
-- HRスキーマ内のすべてのテーブルに対するSELECT権限を付与
GRANT SELECT ANY TABLE ON SCHEMA HR TO APP;
これにより、APP ユーザーはHRスキーマ内のデータにアクセスできます。
VPD関数の作成#
クライアント識別子に基づいて、表示する行を制御するVPD関数を作成します。 ここでは、例として以下の3つを識別子として作成および制御することにします。
Viewer: 一般スタッフやコントリビューター職のみ(_ACCOUNT, _REP, _CLERK)
Editor: Viewerが表示可能な行に加え、ミドルマネジメント職を表示(_MGR, _MAN, _PROG)
Admin: すべての行を表示(_PRES, _VP, _ASST)
VPD関数#
CREATE OR REPLACE FUNCTION hr.get_app_predicate(
p_schema IN VARCHAR2,
p_table IN VARCHAR2
)
RETURN VARCHAR2
IS
v_predicate VARCHAR2 (400);
BEGIN
IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'SALES_APP' THEN
v_predicate := 'JOB_ID LIKE ''SA_%''';
ELSIF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'APP' THEN -- APPユーザーの場合、ユーザー識別子でwhere句を決定する
IF SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') = 'VIEWER' THEN
v_predicate := 'REGEXP_LIKE(JOB_ID, ''_(ACCOUNT|CLERK|REP)$'')';
ELSIF SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') = 'EDITOR' THEN
v_predicate := 'NOT REGEXP_LIKE(JOB_ID, ''_(PRES|VP|ASST)$'')';
ELSIF SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') = 'ADMIN' THEN
v_predicate := '1=1';
ELSE
v_predicate := '1=2'; -- どの識別子にも該当しない場合、何も表示しない
END IF;
ELSE
v_predicate := '1=1';
END IF;
RETURN v_predicate;
END get_app_predicate;
/
VPDポリシーの作成#
作成したVPD関数を指定してVPDポリシーを作成していきます。 既存のポリシーがあるため、削除したのちに作成した関数を使用して新しいポリシーを作成します。
既存ポリシーを削除#
BEGIN
DBMS_RLS.DROP_POLICY (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'employees_vpd_policy'
);
END;
/
新規ポリシーを作成#
BEGIN
DBMS_RLS.ADD_POLICY (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'employees_vpd_policy',
function_schema => 'HR',
policy_function => 'get_app_predicate'
);
END;
/
各クライアント識別子での確認#
以下のコマンドはすべてAPPユーザーで実行します。
-- ユーザーがAPPであることを確認
SQL> set pages 200
SQL> show user
USER is "APP"
-- クライアント識別子を設定していない場合は何も結果が返されない
SQL> SELECT employee_id, first_name, job_id FROM hr.employees;
no rows selected
(1) Viewerの場合#
クライアント識別子「VIEWER」を設定し、APP ユーザーでクエリを実行します。
-- クライアント識別子の設定
SQL> EXEC DBMS_SESSION.SET_IDENTIFIER('VIEWER');
-- データの確認
SQL> SELECT employee_id, first_name, job_id FROM hr.employees;
EMPLOYEE_ID FIRST_NAME JOB_ID
----------- -------------------- ----------
109 Daniel FI_ACCOUNT
110 John FI_ACCOUNT
111 Ismael FI_ACCOUNT
112 Jose Manuel FI_ACCOUNT
...
206 William AC_ACCOUNT
84 rows selected.
(2) Editorの場合#
クライアント識別子「EDITOR」を設定し、APP ユーザーでクエリを実行します。
-- クライアント識別子の設定
SQL> EXEC DBMS_SESSION.SET_IDENTIFIER('EDITOR');
-- データの確認
SQL> SELECT employee_id, first_name, job_id FROM hr.employees;
EMPLOYEE_ID FIRST_NAME JOB_ID
----------- -------------------- ----------
103 Alexander IT_PROG
104 Bruce IT_PROG
105 David IT_PROG
106 Valli IT_PROG
107 Diana IT_PROG
...
206 William AC_ACCOUNT
103 rows selected.
(3) Adminの場合#
クライアント識別子「Admin」を設定し、APP ユーザーでクエリを実行します。
-- クライアント識別子の設定
SQL> EXEC DBMS_SESSION.SET_IDENTIFIER('ADMIN');
-- データの確認
SQL> SELECT employee_id, first_name, job_id FROM hr.employees;
EMPLOYEE_ID FIRST_NAME JOB_ID
----------- -------------------- ----------
100 Steven AD_PRES
101 Neena AD_VP
102 Lex AD_VP
103 Alexander IT_PROG
104 Bruce IT_PROG
...
206 William AC_ACCOUNT
107 rows selected.