2023年6月22日木曜日

OCIのシークレットを操作するアプリケーションを作成する(1) - シークレットを扱うRESTデータ・ソース

Oracle Cloudのボールトに保存されているシークレットを操作するAPEXアプリケーションを作成してみます。シークレットを操作するために以下の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データ・ソースです。

エンドポイントURLhttps://vaults.us-ashburn-1.oci.oraclecloud.com/です。エンドポイントURLの末尾に/をつけます。

アプリケーションのインストール時にリージョンが変わることを想定し、インストール時にプロンプトを表示オンにします。

作成をクリックします。


同様に、Vault Secret Retrieval APIのエンドポイントであるリモート・サーバーを作成します。

名前OCI Vault Secret Retrieval Endpointとします。エンドポイントURLhttps://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を与えます。

https://vaults.us-ashburn-1.oci.oraclecloud.com/20180608/secrets

この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データ・ソースの作成をクリックします。


RESTデータ・ソースとしてVault Secret Managementが作成されます。これを開いて編集します。


登録されているパラメータcompartmentIdは、今回作成された操作である行のフェッチに限定されたパラメータです。全体のパラメータから削除します。

鉛筆アイコンをクリックします。


削除します。


行のフェッチ操作鉛筆アイコンをクリックし、パラメータとしてcompartmentIdを追加します。


パラメータの追加をクリックします。


APIの仕様を確認して、パラメータを設定します。


パラメータタイプURL問合せ文字列名前compartmentIdになります。詳細必須オンです。

パラメータの追加をクリックします。


RESTソース操作行のフェッチの設定は、以上で完了です。

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


データ・プロファイルの編集をクリックし、自動検出されたデータ・プロファイルを修正します。


ID(セレクタとしてはId)の主キーYesに変更します。


セレクタは通常キャメルケースになっています。列の名前はセレクタがそのまま大文字に変換されています。列名はスネークケースとするのが一般的なので、今後の作業のことも考えて、手作業で列の名前をスネークケースに変更します。

以上で変更の適用をクリックします。


操作の追加をクリックし、データベース・アクション単一行のフェッチとなるRESTソース操作を追加します。単一行のフェッチは、失われた更新の検出または実行可能な操作の確認が実行されるときにフォーム・コンポーネントによって使用されます。


単一行のフェッチに当たるAPIはGetSecretです。URLは以下になります。secretIdがパラメータです。

リモートサーバー/20180608/secrets/{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_IDCOMPARTMENT_IDSECRET_NAMEKEY_IDは必須パラメータなので、鉛筆アイコンをクリックし編集画面を開き、必須オンに変更しておきます。

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


操作の追加をクリックし、データベース・アクション行の更新となるRESTソース操作を追加します。

UpdateSecretのAPIを呼び出します。


URLパターン/{secretId}または/:secretIdHTTPメソッド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を設定します。

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


ページの作成をクリックし、ページ作成ウィザードを起動します。


対話モード・レポートを選択します。


ページ定義ページ番号名前Secretsページ・モード標準として、フォーム・ページを含めるオンにします。

フォーム・ページ番号フォーム・ページ名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を指定します。


パラメータKEY_IDに、アイテムP3_KEY_IDを指定します。


パラメータSECRET_NAMEに、アイテムP3_SECRET_NAMEを指定します。


パラメータsecretId(単一行のフェッチのGETとPUTの両方)に、アイテムP3_IDを指定します。


パラメータVAULT_IDに、アイテムP3_VAULT_IDを指定します。


実はVault Secret Management APIListSecretsおよびGetSecretのレスポンスに、保存されているシークレット自体は含まれていません。そのため、CreateSecretUpdateSecretのパラメータCONTENTに対応するページ・アイテムがありません。

パラメータCONTENTに対応するページ・アイテムとしてP3_CONTENTを作成します。

識別名前P3_CONTENTタイプテキスト領域ラベルContentとします。セッション・ステートストレージとしてリクエストごと(メモリーのみ)を指定します。


パラメータCONTENTを選択し(POSTとPUTの両方)、として作成したアイテムP3_CONTENTを指定します。


フォームに表示されるページ・アイテムを、データ入力の対象に限定します。

ページ・アイテムP3_CREATED_BYP3_CREATED_ONP3_TIME_CREATEDP3_LIFECYCLE_STATEP3_ROTATION_CONFIGP3_TIME_OF_DELETIONP3_LIFECYCLE_DETAILSP3_IS_AUTO_GENERATION_ENABLEDP3_SECRET_GENERATION_CONTEXTP3_TIME_OF_CURRENT_VERSION_EXPIRYを選択し、識別タイプ非表示に変更します。


ページ・アイテムP3_COMPARTMENT_IDP3_VAULT_IDの下に移動し、P3_COMPARTMENT_IDP3_VAULT_IDレイアウト新規行の開始オフにします。この結果、ページ・アイテムP3_KEY_ID、P3_VAULT_ID、P3_COMPARTMENT_IDが横並びになります。


ページ・アイテムP3_KEY_IDP3_VAULT_IDP3_COMPARTMENT_IDP3_SECRET_NAMEタイプをテキスト領域からテキスト・フィールドに変更します。


OCI Vaultにシークレットを作成するには、シークレットを作成するコンパートメントのOCIDボールトのOCIDおよびマスター・キーのOCIDが必要です。

これらの値をアプリケーション定義置換に設定します。コンパートメントのOCIDはすでにG_COMPARTMENT_IDとして設定済みなので、ボールトのOCIDG_VAULT_IDマスター・キーのOCIDG_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 IdVault IdCompartment Idは、すでに作成済みのシークレットと同じ値にします。

以下ではSecret NameとしてSecretAPITest01Descriptionとしてシークレットを操作するテスト01ContentとしてVGVzdCBTdHJpbmcKを指定しています。

作成をクリックします。


レポートに作成したシークレットが表示されます。


Contentを"Test String 2"に更新します。BASE64でエンコーディングします。

% echo "Test String 2" | base64  

VGVzdCBTdHJpbmcgMgo=


作成したシークレットの鉛筆アイコンをクリックし、編集ダイアログを開きます。

Descriptionシークレットを操作するテスト02ContentVGVzdCBTdHJpbmcgMgo=に変更します。それ以外の値はAPI呼び出しに含まれないため、変更しても無視されます(サーバー側の条件で非表示にすると良いでしょう)。

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


作成および更新したシークレットを、OCIのコンソールから確認します。

ボールトを開き、リソースからシークレットを選びます。

作成したシークレットSecretAPITest01が見つかります。


SecretAPITest01を開きます。一度、シークレットを更新しているため、最新バージョン2になっています。

シークレット・コンテンツの表示を実行します。


デコードされたBase64桁の表示オンにすると、保存した文字列がTest String 2であることを確認できます。


以上で、作成したAPEXアプリケーションの動作確認ができました。

パラメータCONTENTについて、シークレットとして保存されている内容を取り出すにはVault Secret Retrieval APIを呼び出す必要があります。

Vault Secret Retrieval APIを呼び出してシークレットの内容を操作する実装については、記事を分けて紹介します。

続く