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の設定について説明する予定です。