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

テストツール実戦

アクセス制御の欠陥を見つけるテスト行列

TT Labで続きを見る

目標

認証なし・権限なし・他人の所有・正常なリクエストを分けた、否定テストを作成します。

なぜ重要なのか

成功レスポンスだけを確認するアクセス制御のテストは、すべて緑でした。ルートでHeaderの注入を忘れたり、scopeの検査を消したりしても、テストは壊れませんでした。検証者は、許可するケースを並べるだけでなく、拒否する理由ごとに別々に観測する必要があります。

ステップ

  1. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: bearer(header)は、ちょうど'Bearer 'で始まり、その後に空白のないトークンが1つあるとき、トークンを返します。None・空のトークン・別のscheme・余分な空白はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

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

mkdir -p /root/work/test-auth-matrix-lab
test -e /root/work/test-auth-matrix-lab/service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/service.py /root/work/test-auth-matrix-lab/service.py
test -e /root/work/test-auth-matrix-lab/test_service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/test_service.py /root/work/test-auth-matrix-lab/test_service.py
cd /root/work/test-auth-matrix-lab
  1. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: principal(token, users)は、トークン辞書のユーザー{id, scopes}を返しますが、scopesのリストまでコピーします。未知のトークンはValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  2. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: require_scope(user, scope)は、scopesにscopeの文字列がちょうど含まれているときNone、含まれていなければPermissionErrorを送出します。read-allはreadではありません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  3. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: visible(user, document)は、documentがNoneではなく、ownerがuserのidとちょうど等しいときだけTrueです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  4. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: public_document(document)は、idとtitleだけを持つ新しい辞書です。ownerやinternal_costは含みません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  5. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: authenticate(header, users)は、bearerとprincipalをつなぎます。ValueErrorはHTTPException(401)になり、headersのWWW-Authenticateの値はBearerです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  6. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: read_document(user, documents, document_id)は、read scopeがなければHTTPException(403)、存在しない、または他人のドキュメントならHTTPException(404)、それ以外ならpublic_documentの結果です。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

  7. /root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(users, documents)は、GET /documents/{document_id}でAuthorizationヘッダーを受け取り、authenticateとread_documentを呼び出すFastAPIアプリを返します。200・401・403・404と、非公開フィールドの除去を、実際のリクエストで検証してください。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

参考

Bearerヘッダーを分離する(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: bearer(header)は、ちょうど'Bearer 'で始まり、その後に空白のないトークンが1つあるとき、トークンを返します。None・空のトークン・別のscheme・余分な空白はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

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

mkdir -p /root/work/test-auth-matrix-lab
test -e /root/work/test-auth-matrix-lab/service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/service.py /root/work/test-auth-matrix-lab/service.py
test -e /root/work/test-auth-matrix-lab/test_service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/test_service.py /root/work/test-auth-matrix-lab/test_service.py
cd /root/work/test-auth-matrix-lab

ヘッダーを適当に複数の断片に分けると、空白のエラーを正常なトークンとして受け入れてしまうことがあります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/01-contract.shで確認してください。

プリンシパルをコピーして返す(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: principal(token, users)は、トークン辞書のユーザー{id, scopes}を返しますが、scopesのリストまでコピーします。未知のトークンはValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

返されたscopesを変更したときに、元のユーザーの権限まで変わると、リクエスト間で権限が混ざります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/02-contract.shで確認してください。

権限を正確に比較する(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: require_scope(user, scope)は、scopesにscopeの文字列がちょうど含まれているときNone、含まれていなければPermissionErrorを送出します。read-allはreadではありません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

部分文字列での比較は、より長い権限名を別の権限と取り違えます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/03-contract.shで確認してください。

所有権を別に確認する(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: visible(user, document)は、documentがNoneではなく、ownerがuserのidとちょうど等しいときだけTrueです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

リソースがない場合と他人の所有を、同じ判定にまとめます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/04-contract.shで確認してください。

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

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: public_document(document)は、idとtitleだけを持つ新しい辞書です。ownerやinternal_costは含みません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

元のデータからフィールドを消さず、新しいレスポンスを組み立てます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/05-contract.shで確認してください。

エラーをHTTPの契約に合わせる(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: authenticate(header, users)は、bearerとprincipalをつなぎます。ValueErrorはHTTPException(401)になり、headersのWWW-Authenticateの値はBearerです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

認証の失敗とアプリケーションのエラーを、500の1つにまとめません。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/06-contract.shで確認してください。

拒否の順序を固定する(テスト)

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: read_document(user, documents, document_id)は、read scopeがなければHTTPException(403)、存在しない、または他人のドキュメントならHTTPException(404)、それ以外ならpublic_documentの結果です。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

認証の後でも、scopeと所有権はそれぞれ確認する必要があります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/07-contract.shで確認してください。

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

/root/work/test-auth-matrix-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(users, documents)は、GET /documents/{document_id}でAuthorizationヘッダーを受け取り、authenticateとread_documentを呼び出すFastAPIアプリを返します。200・401・403・404と、非公開フィールドの除去を、実際のリクエストで検証してください。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。

関数が個別に正しくても、ルートで呼び出しを忘れると、アクセス制御は適用されません。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。

保存後、bash /opt/lab/checks/test-auth-matrix-lab/08-contract.shで確認してください。