認証と認可
主体の識別、操作別の許可、拒否後の保存状態、権限変更後の再評価を一つずつ確認します。
説明
認証は誰として要求しているかの確認、認可はその主体が対象の操作を行ってよいかの判断です。ログインできたことだけでは、すべての文書を読んだり削除したりできません。対象と操作を要求ごとに確認する必要があります。
SQLiteに保存したセッション、文書、許可表を使います。aliceを識別して読める文書を取得し、許可のない文書と削除要求を試します。その後に権限を変更し、以前取得した値と再評価した結果を比較します。
主体の識別、操作別の許可、拒否後の保存状態、権限変更後の再評価を一つずつ確認します。
1セッションから主体を得る
公開試料のセッションIDからaliceを取得します。利用者名を要求側の申告からそのまま採用する処理ではありません。
from sc03 import access
db = access()独立した認証・文書・許可表を作る
principal = db.execute("SELECT principal FROM sessions WHERE token=?セッションに対応する主体を取得する", ("example-session-a",)).fetchone()[0]
print(principal)識別した主体を読むaliceとして認証された、という固定の初期資料を得ました。これから行う認可とは別の段階です。
2不明なセッションは主体を得られない
登録のないIDを検索します。Noneを管理者や既定ユーザーへ置き換えると、拒否すべき要求を通してしまいます。
print(db.execute("SELECT principal FROM sessions WHERE token=?未知のセッションを検索する", ("unknown-session",)).fetchone())Noneです。認証済みの主体が得られなかった要求は、この先の許可済み処理へ進めません。
3操作と対象を含む許可
grantsにはaliceのread・文書7だけがあります。
print(db.execute("SELECT * FROM grants主体・操作・対象の許可を読む").fetchall())
query = "SELECT body FROM authorized許可表と文書を結合したビューを検索する WHERE principal=? AND action=? AND id=?"
cached = db.execute取得済みの値を保存する(query, (principal, "read", 7)).fetchall()
print(cached)許可された本文を読む文書7のreport-redが返ります。読取り許可は、同じ文書への削除許可を含むものではありません。
4許可のない文書を要求する
同じaliceで文書8を要求します。許可表にない組合せは返さない構成です。
print(db.execute(query, (principal, "read", 8))対象だけを文書8へ変更する.fetchall())空の結果です。明示的な許可がない要求を通さないdefault denyの例です。文書8がDBから消えたわけではありません。
5読取り権限で削除しようとする
DELETEにもaction=deleteの許可条件を付けます。
changed = db.execute("DELETE FROM docs削除時にも許可条件を検査する WHERE id=? AND EXISTS(SELECT 1 FROM grants WHERE principal=? AND action='delete' AND document=docs.id)", (7, principal)).rowcount
print(changed)削除件数を読む
print(db.execute("SELECT * FROM docs ORDER BY id拒否後の文書を確認する").fetchall())削除0件で、2文書とも残ります。操作ごとの最小権限を、保存状態まで確認できました。
6読取り権限を失効する
aliceの文書7への許可を削除し、同じ要求を再評価します。
db.execute("DELETE FROM grants文書7の読取り許可を取り消す WHERE principal=? AND action='read' AND document=7", (principal,))
print(db.execute(query同じ要求の権限を再評価する, (principal, "read", 7)).fetchall())再評価では空になります。一度認可に成功した事実だけを保持し、後の要求を無条件に通すと失効を反映できません。
7過去に取得した値は別に残る
最初の要求で保存したcachedを表示し、DBの現在の許可表と比較します。
print(cached)過去の取得結果を読む
print(db.execute("SELECT * FROM grants現在の許可状態を読む").fetchall())過去に取得したreport-redは変数に残りますが、現在の許可表は空です。失効はこれからのアクセスを止めるもので、取得済みの情報を自動で消すものではありません。
まとめ
認証で得た主体と、対象・操作の許可を別々に確認します。
許可がない組合せを拒否し、保存状態を変えないことも検査します。
要求ごとに認可を再評価して失効を反映します。
操作履歴と解説を振り返れます。
Python 3.15 / SQLite / 明示した読取り認可モデル
from sc03 import access
db = access()
principal = db.execute("SELECT principal FROM sessions WHERE token=?", ("example-session-a",)).fetchone()[0]
print(principal)alice
print(db.execute("SELECT principal FROM sessions WHERE token=?", ("unknown-session",)).fetchone())None
print(db.execute("SELECT * FROM grants").fetchall())
query = "SELECT body FROM authorized WHERE principal=? AND action=? AND id=?"
cached = db.execute(query, (principal, "read", 7)).fetchall()
print(cached)[('alice', 'read', 7)]
[('report-red',)]print(db.execute(query, (principal, "read", 8)).fetchall())[]
changed = db.execute("DELETE FROM docs WHERE id=? AND EXISTS(SELECT 1 FROM grants WHERE principal=? AND action='delete' AND document=docs.id)", (7, principal)).rowcount
print(changed)
print(db.execute("SELECT * FROM docs ORDER BY id").fetchall())0 [(7, 'report-red'), (8, 'report-blue')]
db.execute("DELETE FROM grants WHERE principal=? AND action='read' AND document=7", (principal,))
print(db.execute(query, (principal, "read", 7)).fetchall())[]
print(cached)
print(db.execute("SELECT * FROM grants").fetchall())[('report-red',)]
[]演習完了