🦴

Snowflake Native Appの権限管理ガイド - Frosty Friday Week 75

に公開

はじめに

この記事は Frosty Friday Week 75 – Hard, Native App Permissions チャレンジの解説です。

https://www.youtube.com/watch?v=FiLikYNyi6Y

チャレンジ内容(日本語訳・機械翻訳)

https://frostyfriday.org/blog/2023/12/15/week-75-hard/

ネイティブアプリの優れた点の一つは、実際のユーザーアカウントへのアクセス権を持たずに権限を要求し、許可を得られることです。
代わりに、アプリケーションが権限を取得します。
では、権限をどのように要求するのでしょうか? 早速見ていきましょう。あなたの課題は次の権限を要求することです:

  • テーブルに対する SELECT
  • アカウントでの CREATE DATABASE

その後、ネイティブアプリは必要な権限を全て取得していることを検証する必要があります。フローは以下のようになります:

以下にスケルトンコードのzipファイルを用意しました。zip内の全ファイルを変更する必要があります。
頑張ってください!

解法

zip内の全ファイルを変更する必要があります。」と言ったな。あれは本当だ。
本当に全ファイルに渡って、修正が必要なんです。。。

まず、スケルトンコードの中身はこんな感じなんですね。修正箇所はオレンジの個所です。

セットアップスクリプト

この時点で Native Apps をデプロイしようとすると、こうなります。チャレンジはこの時から始まっている。

オリジナルのスケルトンコードでは、update_reference ストアドプロシージャの実装部分がまるごとコメントアウトされており、実装が必要でした。

scripts/setup.sql
-- create the update reference callback here

ここでやりたいことは、ストアドを作ることです。
どんなストアドかというと…

  • the update reference callback」とあるので、このあたりを参考に、コンシューマ側にあるオブジェクトを参照できるようにする
    • 参考に、というかそのまま使わせてもらいました
    • Application の外にあるオブジェクトに、自由にアクセスできてしまうのはあまりにも危険なので、Native Apps のマニフェストとコールバック処理で、参照可能なオブジェクトと方法を制限するってわけ
    • なお、本来の作業順序的にはマニフェストファイルが先かと思います。マニフェストファイルの references に、コンシューマー側で使用するコールバック関数としてこのプロシージャを登録します。ここについては後で...
  • 実装では SYSTEM$SET_REFERENCE() で参照の設定、SYSTEM$REMOVE_REFERENCE() で参照の解除を行う

実装のドキュメントはこちらです↓


https://docs.snowflake.com/en/sql-reference/functions/system_set_reference

https://docs.snowflake.com/en/sql-reference/functions/system_remove_reference

Streamlit アプリのソースと環境設定

Streamlit

「デプロイできましたか?それは良かったですね!まだ足りませんよ!」と言わんばかりに、まだまだ問題が出てきます… [1]

streamlit.py のスケルトンコードも、やはり実装の大事な部分がほとんどコメントアウトされていました。

streamlit.py のスケルトンコード
streamlit/streamlit.py
if hasattr(st.session_state, "permission_granted"):
    pass
else:
    # If not, trigger the table referencing process
    # And add a variable in the session to mark that this is done

if not # check for database permission:
    # otherwise, request it

check = st.button('Shall we check we have what we need?')
if check:
    if  and : # check for permissions again
        st.success('Yup! Looks like we\'ve got all the permissions we need')

ここで実装したいことは:

  1. 初回アクセス時、テーブル参照権限をリクエストする
  2. アカウントへの CREATE DATABASE 権限がない場合、権限をリクエストする
  3. 必要な権限が揃っているかチェックする

です。ここで使用するのが Python Permission SDK です。Snowflake の権限へのアクセスと、Native Apps のマニフェストファイルで定義した参照へのアクセスを提供するAPIです。ドキュメントはこちらです↓

https://docs.snowflake.com/ja/developer-guide/native-apps/requesting-permission-sdk-ref

これを使用して、

  1. 初回アクセス時、テーブル参照権限をリクエストする
  • request_reference(<reference_name>) を使う
    • <reference_name> で指定した参照へのリクエストを出す。参照はマニフェストファイルで定義した名前と一致している必要がある
  1. アカウントへの CREATE DATABASE 権限がない場合、権限をリクエストする
  • まず get_held_account_privileges(<privileges>) を使う
    • <privileges> で指定した権限が付与されているかチェック(このチャレンジでは CREATE DATABASE のみ)
  • 権限がなかったとき、権限付与するために request_account_privileges(<privileges>) を使う
    • <privileges> で指定したアカウントレベルの権限(このチャレンジでは CREATE DATABASE のみ)をリクエストする
    • 権限はマニフェストファイルで privileges にリストされている必要がある
  1. 必要な権限が揃っているかチェックする
  • get_held_account_privileges(<privileges>) を使う
  • あと get_reference_associations(<reference_name>) も使う
    • <reference_name> で指定した参照に紐づくオブジェクトのエイリアスを取得する。これが空じゃなかったらOK

パッケージ

ついでに environment.yml も、大事なところがない状態。

environment.yml のスケルトンコード
streamlit/environment.yml
name: sf_env
channels:
- snowflake
dependencies:
-- what's needed here???

Python Permission SDK を使うため、dependencies に snowflake-native-apps-permission を追加してあげます。

マニフェストファイル

さて、最後に一番大事なマニフェストファイルが残っていますね。
(普通はここから記載し始めると思いますが、いきなりデプロイすると最後に引っかかるんですね)

オリジナルのスケルトンコードでは、privilegesreferences セクションが空でした:

manifest.yml
privileges:

references:

ここでやりたいことは、

  1. アカウントでの CREATE DATABASEを付与できるようにする
  2. テーブルに対する SELECT 権限を付与できるようにする

の2つ。特定のオブジェクトでなくアカウントでのグローバルな権限を要求する 1. は privilege で設定します。一方、特定のオブジェクトに対する権限の要求は reference で設定します。[2]

ドキュメントはこちら:

https://docs.snowflake.com/en/developer-guide/native-apps/requesting-privs

https://docs.snowflake.com/en/developer-guide/native-apps/requesting-refs

1. privileges セクション - アカウント権限の要求

アプリケーションが必要とするアカウントレベルの権限を定義します:

manifest.yml
privileges:
  - CREATE DATABASE:
      description: "This application needs to create a database"

Streamlit アプリ内で request_account_privileges(<privileges>) を呼び出したとき、<privileges> がここにリストされているものであれば、許可されるというわけですね。

2. references セクション - 外部オブジェクトへの参照定義

アプリケーションがアクセスする必要があるオブジェクト(このチャレンジではテーブル)を定義します:

manifest.yml
references:
  - frosty_table:
      label: "Frosty Table"
      description: "Table containing frosty data"
      privileges:
        - SELECT
      object_type: TABLE
      multi_valued: false
      register_callback: app_schema.update_reference

Streamlit アプリ内で request_reference("frosty_table") を呼び出すと、このセクションの frosty_table を読み込み、記載内容に従って権限を付与します。
そして register_callback に記載したストアドプロシージャを実行します。ストアドプロシージャ内部で SYSTEM$SET_REFERENCE()SYSTEM$REMOVE_REFERENCE() を使用して参照の設定・削除を行います。

こうして Native App の権限管理機能が完成します。
アプリの権限を確認するとこんな感じ。

おわりに

今回も骨太なチャレンジでした。。。
ですが、Native App の権限制御を調べる良いきっかけになりました!

Frosty Friday Live Challenge では出演者を募集しています。自分の解法を紹介してみたい方、Snowflake のアップデートを語りたい方、ぜひぜひお声がけください!!

脚注
  1. Frosty Friday の中の人たちはとっっっっても気さくで素敵なData Superheroesの方々ですよ!!! https://frostyfriday.org/meet-the-team/ ↩︎

  2. 特定のオブジェクトを参照するかそうでないかの違いを含めたネーミングなのかな、と思ったりしました ↩︎

DATUM STUDIO

Discussion