Oracle Cloudのボールトに保存されているシークレットを操作するAPEXアプリケーションを作成してみます。シークレットを操作するために以下のAPIを呼び出します。
Vault Secret Management API
Vault Secret Management API
これらのREST APIを呼び出すRESTデータ・ソースを、Vault Secret ManagementおよびVault Secret Retrievalとして作成します。このようにして作成したRESTデータ・ソースを扱うことで、REST APIを意識せずにAPEXアプリケーションを作成します。
特にRESTデータ・ソースVault Secret Managementでは、データベース・アクションの行のフェッチ、単一行のフェッチ、行の更新、行の挿入に対応するREST APIによる操作を定義することにより、APEX標準の対話モード・レポートとフォームを使ったシークレットの操作を実装します。
データベース・アクションの操作には、この他に行の削除があります。シークレットの削除は猶予期間があるため特別な実装が必要です。そのため、今回の実装から除いています。また、データベース・アクションが操作として定義されているRESTデータ・ソースは対話グリッドでも利用できるはずですが、対話グリッドでの行の挿入、行の更新について動作が確認できていません。
今回作成するアプリケーションは、以下のように動作します。
以前にOCI PL/SQL SDKを使ってOCI Vaultのシークレットを操作する解説記事を書いています。ボールトの作成やマスター暗号化キーの作成については、こちらの記事を参照して準備します。今回はOracle APEXからREST APIを呼び出すため、PL/SQL APIのパッケージやタイプの実行権限は不要です。OCI REST APIを呼び出すためのWeb資格証明の作成については、こちらの記事を参考になります。
本記事では、シークレットを操作できるWeb資格証明としてOCI_API_ACCESSが作成済みとして、作業を進めます。
以下よりアプリケーションの作成を進めます。
リモート・サーバーの作成
呼び出すAPIのエンドポイントを確認し、あらかじめリモート・サーバーとして作成しておきます。本記事では北米のAshburnリージョンのエンドポイントを呼び出します。リージョンごとにエンドポイントは異なるため、それぞれの利用状況に合わせてサーバーを選びます。
Vault Secret Management API Homeのページによると、Vault Secret Management APIのエンドポイントは以下でした。
https://vaults.us-ashburn-1.oci.oraclecloud.com
Vault Secret Retrieval API Homeのページによると、Vault Secret Retrieval APIのエンドポイントは以下でした。
https://secrets.vaults.us-ashburn-1.oci.oraclecloud.com
ワークスペース・ユーティリティのリモート・サーバーを開きます。
作成済みのリモート・サーバーが一覧されます。作成をクリックします。
以下のスクリーンショットでは、これから作成するリモート・サーバーはすでに作成されています。
Vault Secret Management APIのエンドポイントであるリモート・サーバーを作成します。
名前はOCI Vault Secret Management Endpointとします。静的識別子は自動的に生成されます。サーバー・タイプはRESTデータ・ソースです。
エンドポイントURLはhttps://vaults.us-ashburn-1.oci.oraclecloud.com/です。エンドポイントURLの末尾に/をつけます。
アプリケーションのインストール時にリージョンが変わることを想定し、インストール時にプロンプトを表示をオンにします。
作成をクリックします。
同様に、Vault Secret Retrieval APIのエンドポイントであるリモート・サーバーを作成します。
名前はOCI Vault Secret Retrieval Endpointとします。エンドポイントURLはhttps://secrets.vaults.us-ashburn-1.oci.oraclecloud.com/です。
作成をクリックします。
以上で、RESTデータ・ソースが参照するリモート・サーバーが作成されました。
RESTデータ・ソースの作成
RESTデータ・ソースを作成する際に操作する対象のシークレットが含まれるコンパートメントのコンパートメントIDを確認しておきます。また、最低限1つはシークレットを作成しておきます。RESTデータ・ソースを作成する際にREST APIの呼び出しが行われ、受け取った応答からデータ・プロファイル列を推定します。
RESTデータ・ソースはAPEXアプリケーションに作成します。そのため、最初に空のアプリケーションを作成します。名前はManage Secretsとします。
アプリケーションの作成をクリックします。
アプリケーションが作成されたら、共有コンポーネントのRESTデータ・ソースを開きます。
Vault Secret Management APIを呼び出すRESTデータ・ソースを作成します。
作成をクリックします。
RESTデータ・ソースの作成は最初からとします。
次へ進みます。
RESTデータ・ソース・タイプとしてOracle Cloud Infrastructure (OCI)を選択します。名前はVault Secret Managementとします。
URLエンドポイントとして、ListSecretsを呼び出すURLを与えます。
このRESTデータ・ソースには、作成後に複数の操作を追加します。今回の操作が作成される最初の操作になります。データ・プロファイル列を生成するために、データの一覧(リスト)が返されるAPIを選択します。
ListSecretsのAPIは引数としてcompartmentIdを取ります。シークレットが存在するコンパートメントIDを与えます。
本来であればURLエンドポイントとして、以下のような指定ができます。
https://vaults.us-ashburn-1.oci.oraclecloud.com/20180608/secrets?compartmentId=:compartmentId
URLパラメータ1としてcompartmentIdが現れ、値にコンパートメントIDを入力します。
しかし、最終的にinvalidParameter - query param compartmentId must not be nullとエラーになります。compartmentIdに指定している値が、APIの呼び出し時に渡されていないようです。
仕方がないので、URLエンドポイントにコンパートメントIDも含めます。RESTデータ・ソースがきちんと設定されない点については、RESTデータ・ソースの作成後に手作業で修正します。REST APIの応答を受け取ってデータ・プロファイル列を生成させることを優先します。
https://vaults.us-ashburn-1.oci.oraclecloud.com/20180608/secrets?compartmentId=コンパートメントID
次へ進みます。
リモート・サーバーとして、先ほど作成したOCI Vault Secret Management Endpointが選択されていることを確認します。編集不要です。
次へ進みます。
認証が必要ですをオンにし、資格証明としてOCI_API_ACCESS(画面上はOCI API Access)を選択します。
検出をクリックします。
データが検出されていることを確認し、RESTデータ・ソースの作成をクリックします。
登録されているパラメータのcompartmentIdは、今回作成された操作である行のフェッチに限定されたパラメータです。全体のパラメータから削除します。
鉛筆アイコンをクリックします。
削除します。
行のフェッチの操作の鉛筆アイコンをクリックし、パラメータとしてcompartmentIdを追加します。
パラメータの追加をクリックします。
APIの仕様を確認して、パラメータを設定します。
パラメータのタイプはURL問合せ文字列、名前はcompartmentIdになります。詳細の必須はオンです。
パラメータの追加をクリックします。
RESTソース操作の行のフェッチの設定は、以上で完了です。
変更の適用をクリックします。
データ・プロファイルの編集をクリックし、自動検出されたデータ・プロファイルを修正します。
列ID(セレクタとしてはId)の主キーをYesに変更します。
以上で変更の適用をクリックします。
操作の追加をクリックし、データベース・アクションが単一行のフェッチとなるRESTソース操作を追加します。単一行のフェッチは、失われた更新の検出または実行可能な操作の確認が実行されるときにフォーム・コンポーネントによって使用されます。
単一行のフェッチに当たるAPIはGetSecretです。URLは以下になります。secretIdがパラメータです。
RESTソース・ベースURL以降がURLパターンとして指定する値になります。パラメータは{パラメータ名}または:パラメータ名として指定します。
/{secretId}
または
/:secretId
HTTPメソッドはGET、データベース操作は単一行のフェッチを選択します。
作成をクリックします。
RESTソース操作が追加されます。パラメータを追加するために、単一行のフェッチの鉛筆アイコンをクリックします。
パラメータの追加をクリックします。
最初にパラメータsecretIdを追加します。
パラメータのタイプはURLパターン、名前はsecretIdです。必須をオンにします。
追加後、さらに追加をクリックします。
パラメータのタイプとしてデータ・プロファイル列を選択し、データ・プロファイル列として選択できる値をすべて追加します。
画面を一旦リロードし、追加した操作パラメータを確認します。全部で17あります。すべて追加されていることを確認し、変更の適用をクリックします。
操作の追加をクリックし、データベース・アクションが行の挿入となるRESTソース操作を追加します。
CreateSecretのAPIを呼び出します。
HTTPメソッドとしてPOST、データベース操作として行の挿入を選択します。リクエスト本文テンプレートとして以下を記述します。
{
"vaultId": "#VAULT_ID#",
"compartmentId": "#COMPARTMENT_ID#",
"secretName": "#SECRET_NAME#",
"description": "#DESCRIPTION#",
"keyId": "#KEY_ID#",
"secretContent":
{
"content": "#CONTENT#",
"contentType": "BASE64"
}
}
以上で、作成をクリックします。
操作が追加されます。鉛筆アイコンをクリックして、再度編集します。
本文との同期をクリックし、リクエスト本文テンプレートに含まれている置換文字列を操作パラメータとして設定します。
操作パラメータが設定されたことを確認します。
必ずしも必要な作業ではありませんが、VAULT_ID、COMPARTMENT_ID、SECRET_NAME、KEY_IDは必須パラメータなので、鉛筆アイコンをクリックし編集画面を開き、必須をオンに変更しておきます。
変更の適用をクリックします。
操作の追加をクリックし、データベース・アクションが行の更新となるRESTソース操作を追加します。
UpdateSecretのAPIを呼び出します。
URLパターンは/{secretId}または/:secretId、HTTPメソッドはPUT、データベース操作として行の更新を選択します。
リクエスト本文テンプレートとして、以下を記述します。
{
"description": "#DESCRIPTION#",
"secretContent":
{
"content": "#CONTENT#",
"contentType": "BASE64"
}
}
以上で作成をクリックします。
RESTソース操作が作成されたら、行の挿入のときと同様に鉛筆アイコンをクリックして編集画面を開きます。
最初に本文との同期をクリックし、リクエスト本文テンプレートに含まれる置換文字列を操作パラメータとして追加します。
その後にパラメータの追加をクリックし、URLパターンのsecretIdを操作パラメータに追加します。
パラメータのタイプはURLパターン、名前はsecretIdです。詳細の必須をオンにします。
パラメータの追加をクリックします。
操作パラメータの追加を確認し、変更の適用をクリックします。
今回はデータベース・アクションの行の削除は実装しないため、以上でシークレットを扱うRESTデータ・ソースVault Secret Managementは完成です。
変更の適用をクリックします。
作成されたRESTデータ・ソースが一覧されます。
対話モード・レポートとフォームの作成
作成したRESTデータ・ソースVault Secret Managementをデータ・ソースとした対話モード・レポートとフォームの画面を作成します。
シークレットが存在しているコンパートメントIDを、置換文字列G_COMPARTMENT_IDとして参照できるように、あらかじめ設定しておきます。
アプリケーション定義の置換を開き、置換文字列がG_COMPARTMENT_ID、それの置換値にシークレットが存在するコンパートメントIDを設定します。
変更の適用をクリックします。
ページの作成をクリックし、ページ作成ウィザードを起動します。
対話モード・レポートを選択します。
ページ定義のページ番号は2、名前はSecrets、ページ・モードは標準として、フォーム・ページを含めるをオンにします。
フォーム・ページ番号は3、フォーム・ページ名はSecret Detail、フォーム・ページ・モードとして、ドロワーを選択します。
データ・ソースとしてRESTデータ・ソースを選択し、RESTデータ・ソースとして、これまでの作業で作成したVault Secret Managementを選択します。
ナビゲーションはデフォルトから変更しません。
次へ進みます。
主キー列1としてID(Varchar2)を選択します。これは単一行のフェッチや行の更新がURLパターンに要求しているsecretIdの値です。
ページの作成をクリックします。
以上で、対話モード・レポートとフォームのページが作成されます。
レポートの表示(つまり行のフェッチ)にはコンパートメントIDの指定が必須です。対話モード・レポートのパラメータを開き、compartmentIdの値のタイプを静的値、静的値として&G_COMPARTMENT_ID.を設定します。
以上でページを実行します。
対話モード・レポートが表示されます(以下のスクリーンショットのレポートの書式は調整しています)。
編集アイコンをクリックし、フォームを開きます。
フォームを開こうとするとエラーが発生します。
このエラーは、以下の原因で発生します。
フォームの初期化を行うプロセス初期化フォームSecret Detailは、データベース・アクションの行のフェッチを呼び出し、すべてのシークレットを取り出した中から、ページ・アイテムP3_IDの値に一致する一行を取り出し、その値でフォームを初期化します。
行のフェッチではcompartmentIdは必須パラメータです。この値が渡されていないためにエラーになります。
エラーを解消するために、フォームを開く際にcompartmentIdも引数として渡されるように、設定を追加します。
対話モード・レポートの属性に含まれる、リンクのターゲットを開きます。
アイテムの設定として、名前がP3_COMPARTMENT_ID、値が\#COMPARTMENT_ID#\となる行を追加します。値の前後にあるバックスラッシュは必須です。
OKをクリックします。
フォームのページを開きます。フォーム・リージョンSecret DetailのパラメータからcompartmentIdを選択します。
操作のHTTPメソッドがGET、データベース操作が行のフェッチであることを確認し、値のタイプをアイテムに変更し、アイテムとしてP3_COMPARTMENT_IDを選択します。
以上の対応でフォームが開くようになります。
行のフェッチのパラメータcompartmentId以外についても、パラメータにページ・アイテムを割り当てます。
パラメータCOMPARTMENT_IDを選択し、値のタイプにアイテム、アイテムとしてP3_COMPARTMENT_IDを指定します。
パラメータDESCRIPTIONを選択し(PUTとPOSTの両方)、アイテムとしてP3_DESCRIPTIONを指定します。
パラメータSECRET_NAMEに、アイテムP3_SECRET_NAMEを指定します。
パラメータsecretId(単一行のフェッチのGETとPUTの両方)に、アイテムP3_IDを指定します。
パラメータVAULT_IDに、アイテムP3_VAULT_IDを指定します。
実はVault Secret Management APIのListSecretsおよびGetSecretのレスポンスに、保存されているシークレット自体は含まれていません。そのため、CreateSecret、UpdateSecretのパラメータCONTENTに対応するページ・アイテムがありません。
パラメータCONTENTに対応するページ・アイテムとしてP3_CONTENTを作成します。
識別の名前をP3_CONTENT、タイプはテキスト領域、ラベルはContentとします。セッション・ステートのストレージとしてリクエストごと(メモリーのみ)を指定します。
パラメータCONTENTを選択し(POSTとPUTの両方)、値として作成したアイテムP3_CONTENTを指定します。
フォームに表示されるページ・アイテムを、データ入力の対象に限定します。
ページ・アイテムP3_CREATED_BY、P3_CREATED_ON、P3_TIME_CREATED、P3_LIFECYCLE_STATE、P3_ROTATION_CONFIG、P3_TIME_OF_DELETION、P3_LIFECYCLE_DETAILS、P3_IS_AUTO_GENERATION_ENABLED、P3_SECRET_GENERATION_CONTEXT、P3_TIME_OF_CURRENT_VERSION_EXPIRYを選択し、識別のタイプを非表示に変更します。
ページ・アイテムP3_COMPARTMENT_IDをP3_VAULT_IDの下に移動し、P3_COMPARTMENT_ID、P3_VAULT_IDのレイアウトの新規行の開始をオフにします。この結果、ページ・アイテムP3_KEY_ID、P3_VAULT_ID、P3_COMPARTMENT_IDが横並びになります。
ページ・アイテムP3_KEY_ID、P3_VAULT_ID、P3_COMPARTMENT_ID、P3_SECRET_NAMEのタイプをテキスト領域からテキスト・フィールドに変更します。
OCI Vaultにシークレットを作成するには、シークレットを作成するコンパートメントのOCID、ボールトのOCIDおよびマスター・キーのOCIDが必要です。
これらの値をアプリケーション定義の置換に設定します。コンパートメントのOCIDはすでにG_COMPARTMENT_IDとして設定済みなので、ボールトのOCIDをG_VAULT_ID、マスター・キーのOCIDをG_KEY_IDとし、それぞれに値を設定します。
作成ボタンをクリックしたときに、これらの値が設定されるように対話モード・レポートのページにあるボタンCREATEのターゲットのアイテムの設定を追加します。
シークレットの新規作成でフォームが開くときに、ページ・アイテムP3_COMPARTMENT_IDに値として\&G_COMPARTMENT_ID.\、P3_VAULT_IDに値として\&G_VAULT_ID.\、P3_KEY_IDに値として\&G_KEY_ID.\が渡されるように設定します。
ページ・デザイナでフォームのページを開き、左ペインでプロセス・ビューを開きます。
プロセスプロセス・フォームSecret Detailを選択し、設定の失われた更新の防止をオフ、行のロックをいいえにします。RESTデータ・ソースの更新処理は、通常、失われた更新の防止は実装されていないため、これらが有効になっていると更新処理が行われません。また、挿入後に主キーも返されないため(POST処理でページ・アイテムP3_IDに割り当てられるOutパラメータがない)、挿入後に主キーを返すもオフにします。
以上でアプリケーションは完成です。
動作の確認を行います。
シークレットとして保存する文字列はBASE64でエンコードしている必要があります。base64コマンドを使って、テストで保存する文字列"Test String"をBASE64でエンコードします。
% echo "Test String" | base64
VGVzdCBTdHJpbmcK
APEXアプリケーションからシークレットを作成します。
対話モード・レポートのページから作成をクリックします。
Key Id、Vault Id、Compartment Idは、すでに作成済みのシークレットと同じ値にします。
% echo "Test String 2" | base64
VGVzdCBTdHJpbmcgMgo=
作成したシークレットの鉛筆アイコンをクリックし、編集ダイアログを開きます。
Descriptionをシークレットを操作するテスト02、ContentをVGVzdCBTdHJpbmcgMgo=に変更します。それ以外の値はAPI呼び出しに含まれないため、変更しても無視されます(サーバー側の条件で非表示にすると良いでしょう)。
変更の適用をクリックします。
作成および更新したシークレットを、OCIのコンソールから確認します。
ボールトを開き、リソースからシークレットを選びます。
作成したシークレットSecretAPITest01が見つかります。
SecretAPITest01を開きます。一度、シークレットを更新しているため、最新バージョンが2になっています。
シークレット・コンテンツの表示を実行します。
デコードされたBase64桁の表示をオンにすると、保存した文字列がTest String 2であることを確認できます。
以上で、作成したAPEXアプリケーションの動作確認ができました。
パラメータCONTENTについて、シークレットとして保存されている内容を取り出すにはVault Secret Retrieval APIを呼び出す必要があります。
Vault Secret Retrieval APIを呼び出してシークレットの内容を操作する実装については、記事を分けて紹介します。
続く