2020年12月31日木曜日

アクセス制御の実装サンプル解説(6) - 認可スキーム

 こちらの記事の継続で、本シリーズの最後の記事になります。

今までは認可スキームありきで、各種のページ・タイプへアクセス制御を実装してきました。この記事では、認可スキームについて説明します。

認可スキームは共有コンポーネントとして登録されます。登録済みの認可スキームを確認してみましょう。共有コンポーネントから認可スキームを開きます。

コントリビューション権限リーダー権限管理権限の3つが登録済みです。

この認可スキームはアプリケーション作成ウィザード機能で、アクセス制御チェックを入れているとウィザードによって、アプリケーションに作成されます。

または、ページ作成ウィザードページ・タイプとして機能を選択し、アクセス制御を追加することもできます。

横道にそれますが、導入されたアクセス制御のコンポーネントはビルド・オプション機能: アクセス制御に紐づけられています。

共有コンポーネントビルド・オプションがあります。

これを開くと、ビルド・オプションの一覧が確認できます。アプリケーション作成ウィザード、ページ作成ウィザードで追加できる機能は(ログイン・ページを除き)、ビルド・オプションに紐づけられています。機能: アクセス制御をクリックして開いてみます。

アクセス制御に関連するコンポーネントの確認や、無効化(ステータス除外へ変更)、機能の削除(コンポーネントの削除)が可能であることがわかります。

さて、認可スキームです。実体が分かりやすいのはリーダー権限です。こちらを開いてみます。


リーダー権限は、スキーム・タイプとしてはブールを戻すPL/SQLファンクションとして実装されています。


スキーム・タイプは他に以下の種類があります。
  • EXISTS SQL問合せ
  • NOT EXISTS SQL問合せ
  • ブールを戻すPL/SQLファンクション
  • ロールまたはグループ内にある
  • ロールまたはグループ内にない
  • 式1のアイテムがNOT NULL
  • 式1のアイテムがNULL
  • 式1のアイテムの値 != 式2
  • 式1のアイテムの値 = 式2
  • 式1のプリファレンスの値 != 式2
  • 式1のプリファレンスの値 = 式2
これらは還元すると、ブールを戻すPL/SQLファンクションになります。つまり、真偽値を返すPL/SQLファンクションが認可スキームです。戻り値がTRUEであればアクセスが許可され、FALSEであればアクセスが許可されません。

この認可スキームとなるファンクションが呼び出されるタイミングが評価ポイントであり、認可スキームの検証の頻度が高いとパフォーマンス面ではマイナスになりますが、権限の変更の反映が早くなります。

例えば認可スキームの検証セッションごとに1回であると、サインインした後、再度サインインするまで権限の状態は変わりません。一般にOracle APEXのアプリケーションではサインアウトを意図的に行うことは少ないので、セッションのタイムアウトまで一旦許可されたアクセス権限は、そのまま維持されることになります。

ページ・ビューごとに1回であれば、サインアウト/サインインを行うことなく、新たにページを表示するたびにアクセス権限が評価され、変更された権限が適用されます。

リーダー権限として定義されているコードを見てみましょう。
if nvl(apex_app_setting.get_value(
   p_name => 'ACCESS_CONTROL_SCOPE'),'x') = 'ALL_USERS' then
    -- allow user not in the ACL to access the application
    return true;
else
    -- require user to have at least one role
    return apex_acl.has_user_any_roles (
        p_application_id => :APP_ID, 
        p_user_name      => :APP_USER);
end if;

アプリケーション定義ACCESS_CONTROL_SCOPEALL_USERSであればTRUEを返す、それ以外はapex_acl.has_user_any_rolesの結果(何かロールが登録されていればTRUE)を返しています。

次にコントリビューション権限を見てみましょう。

コントリビューション権限スキーム・タイプとして、ロールまたはグループ内にあるが設定され、タイプアプリケーション・ロール名前管理者、コントリビュータとなっています。つまり、ユーザーがアプリケーション・ロールとして管理者かコントリビュータ・ロールを持っているとコントリビューション権限がTRUEとなります。

今回の要件では、管理者はデータの編集を行なわず(自分が担当者の場合に限定)、コントリビューション権限は持たないことにしているので、名前から管理者を除きます。

管理権限は、こちらの名前管理者のみ(アプリケーション・ロールとして管理者を持っている)の認可スキームです。コントリビューション権限と大差ないので確認は省きます。

次に、アプリケーション・ロールの設定を確認します。共有コンポーネントからアプリケーション・アクセス制御を開きます。

アプリケーションに登録されているアプリケーション・ロールユーザー・ロール割当てとして、ユーザーに割り当てられたアプリケーション・ロールを確認することができます。


共有コンポーネントアプリケーション・アクセス制御を開いて、ユーザー・ロール割当てを実施するには、Oracle APEX開発環境にログインする必要があります。つまりOracle APEXの開発者アカウントが必要ですが、機能としてアクセス制御がアプリケーションに組み込んであれば、サイド・メニューの管理よりアクセス制御の設定を変更したり、ユーザーへのアプリケーション・ロールの追加/削除をすることができます。


機能: アクセス制御によって提供されるアクセス制御は、これを導入しないとアプリケーションのアクセス制御ができない、といったものではありません。

そうではなく、以下の作業をアプリケーションに行います。
  • アプリケーション・ロールとして、管理者、コントリビュータ、リーダーを登録する。
  • アプリケーション定義にACCESS_CONTROL_SCOPEを登録し、ACL_ONLYを設定する。
  • 認可スキームとして管理権限(アプリケーション・ロールの管理者を持っている)、コントリビューター権限(アプリケーション・ロールの管理者かコントリビュータを持っている)、リーダー権限(アプリケーション定義がALL_USERSであるか、または、何かひとつでもアプリケーション・ロールを持っている)の3つを登録する。
  • 管理権限を持ったユーザーによって、アプリケーション定義ACCESS_CONTROL_SCOPEの変更と、ユーザーへのアプリケーション・ロールの割当てを可能とする画面を、アプリケーションに登録する。

アプリケーションを開発する側としては、自力で同様の機能をアプリケーションに組み込むこともできますが、提供されている機能が開発するアプリケーションの要件を満足するのであれば、わざわざ開発する必要はなく、そのまま利用することで開発工数の削減になります。アプリケーション自体に組み込まれた設定であるため、組み込まれた後の認可スキームやページは自由に改変可能です。

ウィザードのよって組み込まれるアクセス制御の機能は、そのアプリケーションに閉じた作業です。複数のアプリケーションに跨ったアプリケーション・ロールの登録やユーザーへのロールの割り当てといった機能は含まれていません。そのような機能が必要な場合は、認可スキームを作成する必要があります。

伝統的な企業アプリケーションであれば、LDAPを認証/認可のためのサーバーとして使用していることが多いと思います。Oracle APEXではLDAPの扱いを容易にするためのAPI、APEX_LDAPを提供しています。これらのAPIを使った認可スキームの実装については、また記事を改めて行えたらと思います。

今回の認可に関するシリーズは以上になります。

アクセス制御の実装サンプル解説(5) - 対話グリッド

 こちらの記事の継続です。

対話グリッドの追加


アプリケーションに対話グリッドのページを追加します。アプリケーション・ビルダーからページの作成を実行します。


ページ・タイプコンポーネントに含まれるフォームをクリックします。


編集可能対話グリッドをクリックします。


ページ名は任意ですが、ここではグリッド編集としています。ページ・モード標準を選択し、へ進みます。


ナビゲーションのプリファレンス新規ナビゲーション・メニュー・エントリの作成を選択します。新規ナビゲーション・メニュー・エントリはページ名と同一のグリッド編集になるので、そのままにして、へ進みます。


表/ビューの名前としてPAC_TASKS(表)を選択します。

列がデフォルトですべて選択されますが、をクリックして開いて、ダイアログを拡大するか下にスクロールするかしないと、主キー列の設定が見つかりません。

主キー列の設定をデフォルトのROWIDからTASK_ID(Number)へ変更し、作成をクリックします。


ページが作成されたら、実行して作成された対話グリッドのページを確認します。サインインする従業員は誰でもかまいません。


対話グリッドのページが表示されます。


対話モード・レポートと同様にProject Id列とAssigned To列が数値のままなので、LOVに基づく表示に変更します。

ページ・デザイナにて対話グリッドのページを開きます。

最初に列PROJECT_IDを選択し、タイプ選択リストLOVタイプ共有コンポーネントLOVPAC_PROJECTS.PROJECT_NAME追加値の表示OFFNULL表示値-プロジェクトを選択 -と設定します。


次に列ASSIGNED_TOを選択し、LOVとしてPAC_EMPLOYEES.EMPLOYEE_NAMENULL表示値- 従業員を選択 -とし、その他は同じ設定を行います。


最後に列STATUSを選択し、LOVとしてLOV_TASK_STATUSNULL表示値- ステータスを選択 -とし、その他は同じ設定を行います。


これで変更が完了です。結果を確認するために、ページの保存と実行をします。

対話グリッドのProject Id列、Assigned To列、Status列が設定されたLOVを元に表示されていることが確認できます。


以上で対話グリッドの追加は完了しました。

対話グリッドのアクセス制限


対話グリッドもレポートの一種なので、クラシック・レポートや対話モード・レポートと同様のSQLによってリストを制限できる、というのは編集可能でない場合のみです。編集可能な対話グリッドのソースとなるSQLは、更新可能な形で記述しなければなりません。具体的には、from句で他の表との結合(JOIN)をしないよう記述します。(対話グリッドで結合を含む複数の表を扱う方法については、こちらの記事が参考になります)。

今回の要件を満たす更新可能な対話グリッドのソースとして、以下のSQLを設定します。
select PROJECT_ID,
       TASK_ID,
       TASK_NAME,
       ASSIGNED_TO,
       STATUS,
       START_DATE,
       END_DATE,
       COST,
       BUDGET,
       (
           select group_name from pac_employees
           where t.assigned_to = employee_id
       ) as group_name
  from PAC_TASKS t
  where 
  :G_IS_ADMINISTRATOR = 'Y'
  or
  exists 
  (
      select 1 from pac_employees
      where t.assigned_to = employee_id
      and group_name = :G_GROUP_NAME
  )
ページ・デザイナにて対話グリッドのページを開き、ソースタイプSQL問合せに変更し、上記のSQLを設定します。


列にGROUP_NAMEが新しく追加されます。この列は表PAC_TASKSに含まれていないので、更新の対象にはできません。そのため、列GROUP_NAMEを選択し、ソース問合せのみONにし、更新の対象から外します。


また、GROUP_NAMEつまり部門の値は、ASSIGNED_TOとして設定される従業員から導出される値であるので、任意に設定はできません。読取り専用タイプ常時実行1回とします。


リストされる行の制御は以上で完了です。ページを実行して確認してみます。管理者ロールを持つ従業員とそれ以外の従業員でそれぞれサインインし、リスト内容を見てみます。

管理者ロールを持つ従業員では、全行リストされていることが確認できます。


管理者ロールを持たない場合は、リストされる行が、サインインした従業員の所属する部門に制限されています。


対話グリッドの更新に適用するアクセス制御はAttributesに含まれるプロパティとして設定します。

編集に含まれる有効実行可能な操作は対話グリッド自体の設定です。今回はすべての編集操作を使うので、すべて有効にします。

最初に認可の編集追加更新削除をすべてコントリビューション権限に設定し、動作を確認してみます。今回の要件では、追加、削除についてはコントリビューション権限の有無のみで決定されますが、更新についてはそれ以外にタスクの担当者も更新可能にする必要があります。こちらの対応は、後ほど実装します。


最初にコントリビュータ・ロールを持つ従業員でサインインします。対話グリッドのページを表示します。

対話グリッドに編集保存行の追加ボタンが表示されています。


アクション・メニュー選択にも、行の削除など、各種データ操作を行う項目が含まれています。


それぞれの行の操作についても同様です。


一般の従業員でサインインして、これらのメニュー項目について確認します。

編集に関するボタンがなくなり、アクション・メニュー選択からも、項目が無くなっています。


行の操作についても項目が無くなっていて、かつ、どこのデータをクリックしても編集モードにはなりません。


作成と削除については、これで要件を満たしていますが、編集については行単位でアクセス制御を行う必要があります。

これを実装するために、プロパティの許可された行操作列の指定を使います。

対話グリッドのSQLに列ALLOWED_OPERATIONSを加えます。サインインした従業員がコントリビュータ・ロールを持っていればUD(U - 編集可、D - 削除可)、コントリビュータ・ロールがなくてもASSIGNED_TO、つまり担当者が自分自身であればU(U - 編集可)を返します。それ以外は空文字列で行の操作を許可しません。
select PROJECT_ID,
       TASK_ID,
       TASK_NAME,
       ASSIGNED_TO,
       STATUS,
       START_DATE,
       END_DATE,
       COST,
       BUDGET,
       (
           select group_name from pac_employees
           where t.assigned_to = employee_id
       ) as group_name,
       case
       when :G_IS_CONTRIBUTOR = 'Y' then
         'UD'
       when assigned_to = (
           select employee_id from pac_employees
           where employee_name = :APP_USER
       ) then
         'U'
       else
         ''
       end allowed_operations
  from PAC_TASKS t
  where 
  :G_IS_ADMINISTRATOR = 'Y'
  or
  exists 
  (
      select 1 from pac_employees
      where t.assigned_to = employee_id
      and group_name = :G_GROUP_NAME
  )

Attributes許可された行操作に列ALLOWED_OPERATIONSを指定し、認可の編集更新よりコントリビューション権限を除きます。


ALLOWED_OPERATIONSがレポートに表示されないよう、タイプ非表示に設定します。アクセス制御の設定は存在自体を、エンドユーザーから隠す必要があります。また、更新もできない列なので、問合せのみONにします。


以上で行単位でのアクセス制御の実装ができました。ページを実行して動作を確認します。

一般従業員でサインインします。Assigned Toがサインインした従業員と同じ行は更新ができ、そうでない行は更新が不可になっていることが確認できます。


ASSIGNED_TO(担当者)の入力について、フォームと同様に、全従業員を選択できてしまう状態になっています。こちらの対応を行います。


一般従業員でサインインした場合は、ASSIGNED_TO(担当者)は変更不可、つまり読取専用とします。

ASSIGNED_TOを選択し、読取り専用タイプとしてアイテム != 値を選択し、アイテムG_IS_CONTRIBUTORを選択します。としてY、つまりG_IS_CONTRIBUTOR != Y、コントリビュータ・ロールを持っていなければ読取り専用とする、という条件を設定します。実行1回で行ごとの評価は行いません。


また、コントリビュータ・ロールを持ったユーザーがASSIGNED_TO(担当者)を選択する際に、自部門の担当者のみがリストされるよう設定します。

最初に共有コンポーネントより、以前に登録したLOVであるLOV_EMPLOYEES_IN_GROUPを開きます。その設定のWHERE句
group_name = :G_GROUP_NAME

から

group_name = :G_GROUP_NAME
or
:G_IS_ADMINISTRATOR = 'Y'

へ変更し、変更の適用をクリックします。


変更したLOVを使用するよう、列ASSIGNED_TOLOVLOV_EMPLOYEES_IN_GROUPに設定し、念のため追加値の表示ONにします。LOVの戻り値に含まれないASSIGNED_TOの値は出現しないはずですが、考え違いで含まれていた場合に、そのデータをそのまま表示します。


ページを実行して、変更した設定の効果を確認してみます。

最初に一般従業員でサインインします。列Assigned Toについては、担当者であっても編集可能にならないことが確認できます。


次に、コントリビュータ・ロールを持つ従業員でサインインします。列Assigned Toをクリックすると選択可能な従業員のリストがポップアップしますが、自部門に所属している従業員に限られています。


最後に、管理者ロールを持つ従業員でサインインします。列Group Name(部門)が異なっていても列Assigned Toに従業員名が表示されています。LOV_EMPLOYEES_IN_GROUPのWHERE句に加えた:G_IS_ADMINISTRATOR = 'Y'の条件が機能していることが確認できます。


対話グリッドのアクセス制御の実装についての説明は以上です。

さて、管理者ロールでサインインした際に表示されている対話グリッドを見返してみましょう。

編集保存行の追加のボタンが表示されているので、コントリビューション権限を持っていることがわかります。


今回の要件では、管理者による編集作業を想定していません。そのため、コントリビュータ・ロールを持つ従業員のみがコントリビューション権限を持つように設定を変更する必要があります。

次は本シリーズの最後の記事になりますが、認可スキームそのものについて説明します。

ちなみに、フォーム・リージョンも同様にAttributes許可された表操作列認可の編集といったプロパティがあります。対話グリッドに追加した列ALLOWED_OPERATIONSをフォームにたいして定義するには、以下のようにフォームのソースであるSQL問合せを変更します。


フォームの場合は対話グリッドと異なり、Attributesの設定と連動して作成変更の適用削除、といったボタン表示/非表示ページ・アイテム編集不可/編集可能といった状態は変わらず、ボタンやページ・アイテムそれぞれに条件を設定する必要があります。そのため、対話グリッドよりは、Attributesに含まれるプロパティを設定してアクセス制御を行う利点は少ないです。とはいえ、リージョンにアクセス制御を設定しておくと、特にボタンの設定に間違いがあった場合でも該当の処理が行われないため、より安全になります。

2020年12月30日水曜日

アクセス制御の実装サンプル解説(4) - 対話モード・レポートとフォーム

 こちらの記事の継続です。

対話モード・レポートとフォームの追加

アプリケーションに対話モード・レポートとフォームのページを追加します。アプリケーション・ビルダーからページの作成を実行します。

ページ・タイプコンポーネントに含まれるフォームをクリックします。

フォーム付きレポートをクリックします。

レポート・タイプとして対話モード・レポートを選択します。レポート・ページ名フォーム・ページ名は任意ですが、それぞれ、タスク一覧タスク編集とします。フォーム・ページ・モードとしてモーダル・ダイアログを選択します。へ進みます。

ナビゲーションのプリファレンス新規ナビゲーション・メニュー・エントリの作成を選択します。新規ナビゲーション・メニュー・エントリはレポート・ページ名と同一のタスク一覧になるので、そのままにして、へ進みます。

表/ビューの名前としてPAC_TASKS(表)を選択します。

それ以外はデフォルトの設定とします。データ・ソースはローカル・データベース、ソース・タイプは表です。レポートの表示する列はすべてを選択します。へ進みます。


主キー型として主キー列の選択を選び、主キー列としてTASK_ID(Nmber)を選択します。作成をクリックします。


ページが作成されたら、実行して作成された対話モード・レポートとフォームのページを確認します。サインインする従業員は誰でも構いません。


対話モード・レポートのページが表示されます。


ファセット検索とは異なり、Project Id列およびAssigned To列は数値のままです。

編集アイコンをクリックし、フォームを開きます。


こちらもProject IdとAssigned Toは数値入力です。またフォームでは、Statusも文字入力なので、入力は容易とはいえません。

アクセス制御を実装する前に、これらを改善します。

最初に共有コンポーネントにLOV_TASK_STATUSとして、表PAC_TASKSの列STATUSで使用するLOVを作成します。

共有コンポーネントLOVを開き、作成をクリックします。


LOVの作成最初からとし、へ進みます。


名前LOV_TASK_STATUSとし、タイプStaticを選択します。へ進みます。


LOVの表示値と戻り値のセットを登録します。表示値Open, Closed, Pending, On-Holdの4つを登録し、それぞれの戻り値としてOPEN, CLOSED, PENDING, ON-HOLDを設定します。LOVの作成をクリックします。


LOV_TASK_STATUSという名前のLOVが作成されました。


次にページ・デザイナにて対話モード・レポートのページタスク一覧を開き、列の表示をLOVに基づく表示に変更します。

PROJECT_IDを選択し、タイププレーンテキスト(LOVに基づく)に変更し、LOVとしてPAC_PROJECTS.PROJECT_NAMEを設定します。元々は数値だったため、ヘッダー、列ともに位置合せが右になっています。位置合せに変更します。


次に列ASSIGNED_TOを選択し、LOVPAC_EMPLOYEES.EMPLOYEE_NAMEとします。こちらも位置合せに変更します。


最後に列STATUSを選択し、LOVLOV_TASK_STAUSとします。


結果を確認するため、ページの保存と実行をします。

対話モード・レポートが、プロジェクト名、従業員名、ステータスが設定されたLOVを元に表示されていることを確認できます。


次にフォームのページを編集します。

ページ・デザイナにてフォームのページタスク編集を開き、ページ・アイテムをLOVを使用する選択リストに変更します。

最初にページ・アイテムP8_PROJECT_IDを選択し、タイプ選択リストLOVタイプ共有コンポーネントLOVPAC_PROJECTS.PROJECT_NAME追加値の表示OFFNULL値の表示ONNULL表示値- プロジェクトを選択 -と設定します。


次にページ・アイテムP8_ASSIGNED_TOを選択し、LOVとしてPAC_EMPLOYEES.EMPLOYEE_NAMENULL表示値- 従業員を選択 -とし、その他は同じ設定を行います。


最後にページ・アイテムP8_STATUSを選択し、LOVとしてLOV_TASK_STATUSNULL表示値- ステータスを選択 -とし、その他は同じ設定を行います。


これでフォームの変更も完了です。変更を保存しアプリケーションを実行します。ダイアログのページからはアプリケーションの実行はできません。対話モード・レポートをページ・デザイナで開いて、ページの実行を行います。


Project Id、Assigned To、Statusのページ・アイテムがそれぞれ選択リストになっていて、表示も数値から名前に変更されていることが確認できます。

以上で対話モード・レポートとフォームのページの追加は完了しました。

対話モード・レポートのアクセス制限


対話モード・レポートもファセット検索で使用されていたクラシック・レポート同様、レポートなので、同じSQLで検索結果を制限することが可能です。ファセット検索で設定したSQLは以下です。
select PROJECT_ID,
       TASK_ID,
       TASK_NAME,
       ASSIGNED_TO,
       STATUS,
       START_DATE,
       END_DATE,
       COST,
       BUDGET,
       e.GROUP_NAME
from PAC_TASKS t join PAC_EMPLOYEES e 
    on t.assigned_to = e.employee_id
where :G_IS_ADMINISTRATOR = 'Y'
or
e.GROUP_NAME = :G_GROUP_NAME

このSQLを対話モード・レポートに設定します。

ページ・デザイナにてタスク一覧のページを開き、レポートのリージョンを選択し、ソースタイプSQL問合せに変更し、上記のSQLを設定します。

変更した結果を確認するため、タスク一覧のページを実行します。一般従業員(管理者およびコントリビュータ・ロールを持たない)でサインインします。

サインインした従業員が所属している部門のタスクのみがリストされていることは確認できますが、今回の要件では、一般従業員はタスクを作成する権限がなく、また、自分が担当していないタスクの編集権限もありません。

対話モード・レポートにはフォームを開くためのコンポーネントがあり、これらを制御する必要があります。


最初に認可スキームによる設定を行ってみます。

レポート上の編集アイコンを非表示にします。レポートのAttributesを開き、リンク認可スキームコントリビューション権限に変更します。


次にボタンCREATEを選択し、認可スキームコントリビューション権限に変更します。


以上の設定で、対話モード・レポートを再度実行します。

作成ボタンが表示されないことは要件通りですが、タスクの編集は、担当者がサインインしている従業員である場合は表示される必要があります。そのため、リンクの認可スキームの設定では十分といえません。


認可スキームでは要件を達成できないため、編集フォームを開くリンクを対話モード・レポートの機能ではなく、SQLから生成するように変更します。

最初に対話モード・レポートよりリンクの生成を除外します。

レポートのAttributesを開き、リンク列リンク列の除外に変更します。


レポートのソースとして設定するSQLは、以下です。編集フォームのへのリンク列をEDIT_LINKとして追加し、リンク自体の生成はCASE文で制御しています。
select 
       case
       when 
        (
          :G_IS_CONTRIBUTOR = 'Y'
          and
          e.group_name = :G_GROUP_NAME
        )
        or
        (
          :G_IS_CONTRIBUTOR = 'N'
          and
          e.employee_name = :APP_USER
        )
       then
         '<a href="' || apex_page.get_url(p_page => 8, p_items => 'P8_TASK_ID', p_values => task_id)
         || '"><span class="fa fa-edit" aria-hidden="true" title="&#x7DE8;&#x96C6;"></span>'
       else 
         ''
       end edit_link,
       PROJECT_ID,
       TASK_ID,
       TASK_NAME,
       ASSIGNED_TO,
       STATUS,
       START_DATE,
       END_DATE,
       COST,
       BUDGET,
       e.group_name
from PAC_TASKS t join PAC_EMPLOYEES e 
on t.assigned_to = e.employee_id
 where 
   :G_IS_ADMINISTRATOR = 'Y'
   or
   e.group_name = :G_GROUP_NAME

コントリビュータ・ロールを持っている場合は、所属している部門が一致していれば編集リンクを生成し、持っていない場合は担当者がサインインした従業員であれば編集リンクを生成するように記述されています。

新規に追加された列EDIT_LINKはHTMLのタグを含むので、列のプロパティの特殊文字をエスケープOFFにする必要があります。

Oracle APEXではクロスサイト・スクリプティング(XSS)攻撃を防ぐため、ページへの出力に含まれる特殊文字は常にエスケープされます。HTMLの出力をブラウザにHTMLとして解釈させるには、この特殊文字をエスケープOFFにする必要があります。

以上の設定で、対話モード・レポートを確認します。

一般従業員でサインインし、担当者が自分自身のタスクに編集リンクがあることを確認します。

コントリビュータ・ロールを持つ従業員でも確認します。作成ボタンも表示されていることが確認できます。

対話モード・レポートへのアクセス制御の実装は以上で完了です。


フォームのアクセス制限


Oracle APEXでは、ページのナビゲーションを辿らずにフォームを開くことができないように、ページが保護されています。この保護機能(ディープ・リンクやページ・アクセスの保護)はデフォルトで有効なので、これらの保護を解除しないかぎりデータの編集フォームを直接開くことはできません。(ページの保護についてはこちらの記事を書いています)。

そのため、必ずしもフォームに認可スキームを設定する必要がない場合が多いです。また、認可スキームでは対応ができないケースもあります。

例えば、タスク編集のページの認可スキームを、以下のようにコントリビューション権限に変更したとします。

結果として、一般従業員による自分が担当者であるタスクの編集が制限されてしまいます。どのようなエラーが発生するかは、ファセット検索の記事ですでに紹介していますので、実際に認可スキームを設定して確認しなくてもよいでしょう。

同様に実際にデータベース操作を行うプロセスにも、認可スキームを設定できます。タスク編集の画面の左ペインにあるプロセス・ビューを開き、プロセス・フォームタスク編集を選択します。認可スキームコントリビューション権限に変更します。

この設定だと、一般従業員による自分自身が担当者であるタスクの編集が禁止されてしまいます。どのような動作になるか確認してみましょう。

何も起こらないのでスクリーンショットは省略します。

エラーは発生しませんが、値を変更して変更の適用をクリックしても、変更は一切反映されないことが確認できます。ボタンなどの画面上に表示されるコンポーネントと同様に、認可スキームで許可されないと、プロセスが存在しないと解釈され、実行がスキップされます。

これからの作業に支障があるので、プロセスに設定した認可スキームは解除しておきます。

それぞれのユーザーがフォームにてタスクの情報を更新するにあたって、以下の2つの制限を適用します。

  1. 一般従業員がタスクを編集する際に、担当者(ASSIGNED_TO)は自分自身から変更できない。
  2. コントリビュータは自部門の従業員しか担当者(ASSIGNED_TO)に指定できない。
第1の要件は、ページ・アイテムP8_ASSIGNED_TO認可スキームコントリビューション権限を指定することで対応できます。

フォームを開いて効果を確認します。一般従業員でサインインしフォームを開くと、Assigned Toのページ・アイテムが表示されていないことが確認できます。


次の要件を満たすために、共有コンポーネントにLOVを追加します。名前をLOV_EMPLOYEES_IN_GROUPとし、サインインしている従業員の部門に所属している従業員だけを対象とします。

共有コンポーネントLOVを開き、コピーを実行します。


LOVのコピーとして、PAC_EMPLOYEES.EMPLOYEE_NAME - 動的を選択し、新規LOV名としてLOV_EMPLOYEES_IN_GROUPを指定します。コピーを実行します。


コピーされたLOVを開いて、SQLを修正します。


WHERE句としてgroup_name = :G_GROUP_NAMEを追加します。変更の適用をクリックします。


これで担当者の設定時に選択可能な従業員を、サインインした従業員の所属している部門に限定するLOVが作成されました。

ページ・アイテムP8_ASSIGNED_TOを、新たに作成したLOV_EMPLOYEES_IN_GROUPをLOVとして使用するように変更します。LOVの戻り値に含まれない値が存在する可能性があるため、追加値の表示ONにします。


コントリビュータ・ロールを持つ従業員でサインインし、フォームを開いてAssigned Toに指定可能な従業員を確認します。


うまくいっているようです。

対話モード・レポートとフォームのアクセス制御の実装についての説明は以上です。

実はフォーム・リージョンのAttributesには、より高度なアクセス制御を可能とするプロパティが提供されています。


フォーム・リージョンは比較的最近導入されたリージョン・タイプであり(19.1より利用可能)、それまではフォームに静的リージョンが使用されていました。静的リージョンには、上記のような編集を制御するプロパティはありません。ですので、本記事は概ね、過去のバージョンでも適用可能です。

このフォーム・リージョンの編集に関するプロパティは、実は対話グリッドに同じ仕様で実装されています。対話グリッドの方の実装が先なので、フォーム・リージョンが仕様を合わせています。

次の記事は対話グリッドですので、このAttributesの設定について説明する予定です。