コードにより、ユーザに表示権限がないデータへのアクセスが許可されていることがわかりました。これを修正するには、最初に CDS アクセス制御が定義されている CDS ビューエンティティからコードを読み込むようにします。次に、ユーザが照会する権限のないデータとまったく存在しないデータを区別できるように、明示的な権限チェックを実装します。
タスク 1: テンプレートのコピー (オプション)
テンプレートクラス /LRN/CL_S4D401_ITS_SEC_KEY をコピーします。前の演習問題を完了した場合は、このタスクをスキップし、クラス ZCL_##_SOLUTION の編集を続行することができます。
ステップ
クラス /LRN/CL_S4D401_ITS_SEC_KEY を独自のパッケージのクラスにコピーします (推奨名: ZCL_##_SOLUTION、## はグループ番号)。
Project Explorer で、クラス /LRN/CL_S4D401_ITS_SEC_KEY を右クリックしてコンテキストメニューを開きます。
コンテキストメニューから Duplicate.... を選択します。
Package 項目にパッケージの名称を入力します。Name 項目に、ZCL_##_SOLUTION (## はグループ番号) と入力します。
Next を選択します。
移送依頼を確認し、Finish を選択します。
コピーを有効化します。
Ctrl + F3 を押してクラスを有効化します。
タスク 2: 権限分析
データベーステーブル /LRN/CARRIER から開始して、権限のないアクセスから運送業者データを保護する CDS アクセス制御を見つけます。CDS アクセス制御を分析して、ユーザが運送業者データを照会するために必要な権限を確認します。
ステップ
データベーステーブル /LRN/CARRIER の定義を照会します。
Ctrl + Shift + A を押し、検索項目に /LRN/CARRIER と入力します。
OK を選択します。
このデータベーステーブルの使用先一覧を分析します。
データベーステーブル /LRN/CARRIER の定義で、/lrn/carrier を右クリックし、使用先一覧取得を選択します。または、/lrn/carrier をクリックして Ctrl + Shift + G を押します。
オブジェクトタイプ選択ダイアログで、少なくともオブジェクトタイプデータ定義が選択されていることを確認し、終了を選択します。
エディタの下の検索ビューで、ファンネルアイコンを選択してフィルタダイアログを開きます。
オブジェクトタイプ項目にカーソルを置き、Ctrl + Space を押して入力ヘルプダイアログを開きます。
候補リストから DDLS/DF (データ定義) を選択し、適用を選択します。
CDS ビュー /LRN/I_CARRIERの使用先一覧を分析します。
検索ビューで、 /LRN/I_CARRIER (データ定義) を右クリックし、使用先一覧取得を選択します。または、/LRN/I_CARRIER (データ定義) をクリックし、Ctrl + Shift + G を押します。
オブジェクトタイプ選択ダイアログで、少なくともオブジェクトタイプアクセス制御が選択されていることを確認し、終了を選択します。
エディタの下にある検索ビューの該当対象が多すぎる場合は、ファンネルアイコンを選択してフィルタダイアログを開きます。オブジェクトタイプ項目にカーソルを置き、Ctrl + Space を押して入力ヘルプダイアログを開きます。候補リストから DCLS/DL (Access Control) を選択し、Apply を選択します。
アクセス制御 /LRN/I_CARRIER の定義を開きます。
検索ビューで、 /LRN/I_CARRIER (アクセス制御) をダブルクリックします。
権限オブジェクト名は、機能 pfcg_auth の最初の入力パラメータです。
その他の入力パラメータにより、権限オブジェクトの権限項目に値が指定されます。
権限オブジェクト /LRN/CARR の定義を開き、アクティビティ値の意味を分析します。
Ctrl キーを押したまま、pfcg_auth 関数の呼出で /LRN/CARR をクリックします。
使用可能なアクティビティ値は、許可済アクティビティに一覧表示されます。
タスク 3: CDS アクセス制御の使用
ローカルクラス LCL_CARRIER のコンストラクタメソッドで、データベーステーブル /LRN/CARRIER を CDS アクセス制御 /LRN/I_CARRIER によって保護されている CDS ビュー /LRN/I_CARRIER に置き換えます。コンストラクタ入力として異なる運送業者 ID を使用してコードをテストします。
ステップ
ローカルクラス LCL_CARRIER のコンストラクタメソッドの実装を開きます。
グローバルクラス ZCL_##_SOLUTION のコードに戻ります。## は各自のグループ番号です。
Outline ビューで ZCL_##_SOLUTION→LCL_CARRIER を展開し、CONSTRUCTOR をクリックします。
SELECT 文を調整します。FROM 句で、データベーステーブル /LRN/CARRIER を上記の CDS ビューに置き換えます。
コードを以下のように調整します。
123
SELECT SINGLE
FROM /LRN/I_Carrier
構文エラーを修正します。テーブル項目名を対応する CDS ビューエレメント名に置き換えます。
コードを以下のように調整します。
1234567891011
SELECT SINGLE
FROM /LRN/I_Carrier
FIELDS concat_with_space( AirlineID, Name, 1 ), CurrencyCode
WHERE AirlineID = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
グローバルクラスをコンソールアプリとして有効化してテストします。
Ctrl + F3 を押します。
F9 を押します。
グローバルクラスで、ローカル定数 c_carrier_id の値を UA に変更します。
グローバルクラスタブに切り替えます。
コードを以下のように調整します。
12
CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'UA'.
グローバルクラスをコンソールアプリとして有効化してテストします。
Ctrl + F3 を押します。
F9 を押します。
タスク 4: AUTHORITY-CHECK 命令の実装
ローカルクラス LCL_CARRIER のコンストラクタメソッドで、AUTHORITY-CHECK 命令を実装し、フライトキャリアが存在するものの、ユーザにこの航空会社に対する権限がない場合は、例外 CX_ABAP_AUTH_CHECK_EXCEPTION を発生させます。
ステップ
ローカルクラス LCL_CARRIER のコンストラクタメソッドの実装を開きます。
エディタでグローバルクラス ZCL_##_SOLUTION が開いていることを確認します (## は各自のグループ番号)。
Outline ビューで ZCL_##_SOLUTION→LCL_CARRIER を展開し、CONSTRUCTOR をクリックします。
データベーステーブル /LRN/CARRIER から再度読み込むように SELECT 文を調整します。
コードを以下のように調整します。
1234567891011
SELECT SINGLE
FROM /lrn/carrier
FIELDS concat_with_space( carrier_id, name, 1 ), currency_code
WHERE carrier_id = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
読込アクセスが成功した場合、つまり例外 CX_ABAP_INVALID_VALUE が発生しなかった場合に実行される AUTHORITY-CHECK 命令を実装します。上記の権限オブジェクト、権限項目、および値を使用します。
コードを以下のように調整します。
123456789101112
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
AUTHORITY-CHECK
OBJECT '/LRN/CARR'
ID '/LRN/CARR' FIELD i_carrier_id
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
ENDIF.
ユーザに要求された航空会社 ID の照会権限がない場合は、例外 CX_ABAP_AUTH_CHECK_EXCEPTION を発生させます。
コードを以下のように調整します。
12345678
AUTHORITY-CHECK
OBJECT '/LRN/CARR'
ID '/LRN/CARR' FIELD i_carrier_id
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_auth_check_exception.
ENDIF.
クイックフィックスを使用して、コンストラクタメソッドの定義の RAISING 句に例外 CX_ABAP_AUTH_CHECK_EXCEPTION を追加します。
RAISE EXCEPTION 命令で、cx_abap_auth_check_exception をクリックして Ctrl + 1 を押します。または、構文警告が表示されているコード行の左側にある電球アイコンを選択します。
提案されたクイックフィックスの一覧から、Add raising declaration を選択します。
その結果、コンストラクタメソッドの定義は以下のようになります。
123456
METHODS constructor
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RAISING
cx_abap_invalid_value
cx_abap_auth_check_exception.
グローバルクラスで、新規例外 CX_ABAP_AUTH_CHECK_EXCEPTION の CATCH ブロックを使用して TRY ... ENDTRY 制御構造を拡張します。ユーザに航空会社を照会する権限がないことを示す適切なテキストを発行します。
コードを以下のように調整します。
123456789
TRY.
DATA(carrier) = NEW lcl_carrier( i_carrier_id = c_carrier_id ).
out->write( name = `Carrier Overview`
data = carrier->get_output( ) ).
CATCH cx_abap_invalid_value.
out->write( |Carrier { c_carrier_id } does not exist| ).
CATCH cx_abap_auth_check_exception.
out->write( |No authorization to display carrier { c_carrier_id }| ).
ENDTRY.
同様に、ローカルユニットテストクラスの class_setup メソッドで TRY ... ENDTRY 制御構造を拡張します。例外 CX_ABAP_INVALID_VALUE の捕捉ブロックと同じ実装を使用します。
注記
これは回避策にすぎません。このコースの後半では、同じ Catch ブロックで複数の例外を処理する方法について学習します。
コードを以下のように調整します。
1234567
TRY.
the_carrier = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ).
CATCH cx_abap_invalid_value.
cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ).
CATCH cx_abap_auth_check_exception.
cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ).
ENDTRY.
グローバルクラスをコンソールアプリとして有効化してテストします。コンソールに新しいテキスト (航空会社 UA を表示する権限なし) がコードに表示されていることを確認します。
Ctrl + F3 を押します。
F9 を押します。
グローバルクラスで、ローカル定数 c_carrier_id の値を LH に戻します。
グローバルクラスタブに切り替えます。
コードを以下のように調整します。
12
CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'LH'.
グローバルクラスをコンソールアプリとして有効化してテストします。コードに以前と同様に航空会社およびフライト詳細が表示されていることを確認します。
Ctrl + F3 を押します。
F9 を押します。