TT Lab
はじめる
学ぶ 学習パス コース

FastAPI — 型がそのまま契約だ

有効なトークンでも他人の文書は読めない

TT Labで続きを見る

目標

認証・権限・所有権を分離し、同じ404でリソースの存在を隠します。

なぜ重要なのか

ログインしたユーザーが、アドレスの文書番号だけを変えて、ほかのチームの文書を読みました。トークンが有効だという事実と、特定の文書を読む権利は、別のものです。このラボでは、固定のトークン辞書を使って、認証サーバーの代わりにします。トークンを発行したり、JWTの署名を実装したりするラボではなく、認証の結果のあとの権限の境界を実装するラボです。

ステップ

  1. /root/work/fa-ownership-lab/service.pyで、bearer(header)は、正確に'Bearer 'で始まり、そのあとに空白のないトークンが1つあるときに、トークンを返します。None・空のトークン・別のscheme・余分な空白は、ValueErrorです。

最初に一度だけ準備してください。既存のファイルは上書きしません。

mkdir -p /root/work/fa-ownership-lab
test -e /root/work/fa-ownership-lab/service.py || cp /opt/fixtures/ten_labs/fa-ownership-lab/service.py /root/work/fa-ownership-lab/service.py
cd /root/work/fa-ownership-lab
  1. /root/work/fa-ownership-lab/service.pyで、principal(token, users)は、トークン辞書のユーザー{id, scopes}を返しますが、scopesのリストまでコピーします。知らないトークンは、ValueErrorです。

  2. /root/work/fa-ownership-lab/service.pyで、require_scope(user, scope)は、scopesにscopeの文字列が正確にあるときはNone、なければPermissionErrorを出します。read-allは、readではありません。

  3. /root/work/fa-ownership-lab/service.pyで、visible(user, document)は、documentがNoneではなく、ownerがuserのidと正確に同じときだけTrueです。

  4. /root/work/fa-ownership-lab/service.pyで、public_document(document)は、idとtitleだけを持つ新しい辞書です。ownerやinternal_costは含みません。

  5. /root/work/fa-ownership-lab/service.pyで、authenticate(header, users)は、bearerとprincipalをつなぎます。ValueErrorはHTTPException(401)で、headersのWWW-Authenticateの値はBearerです。

  6. /root/work/fa-ownership-lab/service.pyで、read_document(user, documents, document_id)は、read scopeがなければHTTPException(403)、存在しないか他人の文書ならHTTPException(404)、それ以外ならpublic_documentの結果です。

  7. /root/work/fa-ownership-lab/service.pyで、create_app(users, documents)は、GET /documents/{document_id}でAuthorizationヘッダーを受け取って、authenticateとread_documentを呼び出すFastAPIアプリを返します。200・401・403・404と、非公開フィールドの除去を、実際のリクエストで検証してください。

参考

Bearerヘッダーを分離する

/root/work/fa-ownership-lab/service.pyで、bearer(header)は、正確に'Bearer 'で始まり、そのあとに空白のないトークンが1つあるときに、トークンを返します。None・空のトークン・別のscheme・余分な空白は、ValueErrorです。

最初に一度だけ準備してください。既存のファイルは上書きしません。

mkdir -p /root/work/fa-ownership-lab
test -e /root/work/fa-ownership-lab/service.py || cp /opt/fixtures/ten_labs/fa-ownership-lab/service.py /root/work/fa-ownership-lab/service.py
cd /root/work/fa-ownership-lab

ヘッダーを任意に複数の断片に分けると、空白のエラーを正常なトークンとして受け入れてしまうことがあります。

保存したら、bash /opt/lab/checks/fa-ownership-lab/01-contract.shで確認してください。

身元をコピーして返す

/root/work/fa-ownership-lab/service.pyで、principal(token, users)は、トークン辞書のユーザー{id, scopes}を返しますが、scopesのリストまでコピーします。知らないトークンは、ValueErrorです。

返されたscopesを変更して、元のユーザーの権限まで変わってしまうと、リクエスト間で権限が混ざります。

保存したら、bash /opt/lab/checks/fa-ownership-lab/02-contract.shで確認してください。

権限は正確に比較する

/root/work/fa-ownership-lab/service.pyで、require_scope(user, scope)は、scopesにscopeの文字列が正確にあるときはNone、なければPermissionErrorを出します。read-allは、readではありません。

部分文字列の比較は、より長い権限名を、別の権限と取り違えます。

保存したら、bash /opt/lab/checks/fa-ownership-lab/03-contract.shで確認してください。

所有権を別に確認する

/root/work/fa-ownership-lab/service.pyで、visible(user, document)は、documentがNoneではなく、ownerがuserのidと正確に同じときだけTrueです。

リソースがないことと、他人の所有であることを、同じ判定にまとめます。

保存したら、bash /opt/lab/checks/fa-ownership-lab/04-contract.shで確認してください。

レスポンスのフィールドを許可リストで選ぶ

/root/work/fa-ownership-lab/service.pyで、public_document(document)は、idとtitleだけを持つ新しい辞書です。ownerやinternal_costは含みません。

元のデータからフィールドを削除せず、新しいレスポンスを組み立てます。

保存したら、bash /opt/lab/checks/fa-ownership-lab/05-contract.shで確認してください。

エラーをHTTPの契約に合わせる

/root/work/fa-ownership-lab/service.pyで、authenticate(header, users)は、bearerとprincipalをつなぎます。ValueErrorはHTTPException(401)で、headersのWWW-Authenticateの値はBearerです。

認証の失敗とアプリケーションのエラーを、500の1つにまとめません。

保存したら、bash /opt/lab/checks/fa-ownership-lab/06-contract.shで確認してください。

拒否の順序を固定する

/root/work/fa-ownership-lab/service.pyで、read_document(user, documents, document_id)は、read scopeがなければHTTPException(403)、存在しないか他人の文書ならHTTPException(404)、それ以外ならpublic_documentの結果です。

認証のあとでも、scopeと所有権は、それぞれ確認する必要があります。

保存したら、bash /opt/lab/checks/fa-ownership-lab/07-contract.shで確認してください。

実際のリクエストで境界を閉じる

/root/work/fa-ownership-lab/service.pyで、create_app(users, documents)は、GET /documents/{document_id}でAuthorizationヘッダーを受け取って、authenticateとread_documentを呼び出すFastAPIアプリを返します。200・401・403・404と、非公開フィールドの除去を、実際のリクエストで検証してください。

関数が個別に正しくても、経路で呼び出しを抜かすと、アクセス制御は適用されません。

保存したら、bash /opt/lab/checks/fa-ownership-lab/08-contract.shで確認してください。