一覧SD-06-1 · 認可とテナント分離

リソース・操作・項目ごとの認可

認可をIDの存在確認だけで終わらせず、要求した操作と項目まで確認します。

説明

同じ資源でも、読む権限、変更する権限、機密項目を読む権限は異なり得ます。画面にボタンがないことやIDが推測しにくいことを、サーバー側の認可の代わりにしないようにします。

文書7・8・9を使い、主体と操作を変えて取得・更新します。最後に費用欄の参照を追加し、資源の権限と項目の権限が両方必要な条件を確認します。

認可をIDの存在確認だけで終わらせず、要求した操作と項目まで確認します。

1文書と操作権限を対応付ける

aliceは文書7のread/update、文書9のreadを持ちます。bobの対象は文書8です。

from sd06 import * db = database() print(db.execute("SELECT subject,doc,action主体・資源・操作の組を読む FROM grants WHERE subject IN ('alice','bob') ORDER BY subject,doc,action").fetchall())

同じ主体でも、文書ごとに操作の範囲が違います。IDが存在するかだけでは許可を決められません。

2許可された文書と他者の文書を取得する

同じaliceでIDだけを変えます。

print(read(db,"alice",7)許可された資源を取得する) print(read(db,"alice",8)IDだけを他者の資源へ変える)

7は取得でき、8は空です。SQLの取得条件に主体のread権限を含めています。

3主体を変えて同じIDを取得する

文書8をbobとして読みます。

print(read(db,"bob",8)文書8の権限を持つ主体で取得する) print(read(db,"bob",7))

8は取得でき、7は空です。要求ごとの認証主体を条件へ渡すことが必要です。クライアントが送る自己申告の主体名をそのまま使う設計にはしません。

4読取り専用の文書を変更する

aliceは文書9を読めますが、updateは持っていません。

print(read(db,"alice",9)) print(edit(db,"alice",9,"changed")読取りだけの資源へ更新を要求する) print(db.execute("SELECT body FROM docs WHERE id=9").fetchone())

読取りは成功し、更新行数0でred-noteを保ちます。readの成功をupdateの許可へ流用していません。

5更新権限を持つ文書を変更する

aliceの文書7を変更します。

print(edit(db,"alice",7,"revised")許可された対象を更新する) print(read(db,"alice",7)) print(edit(db,"alice",8,"changed")他者の対象への更新を比較する)

7は更新1、8は更新0です。変更する行の条件へ認可も含めることで、対象と許可のずれを避けます。

6費用欄の追加権限を検査する

費用costはfield_grantsにも許可が必要です。financeには文書7のreadとcost欄の権限があります。

print(cost(db,"alice",7)資源だけ許可された主体で費用欄を読む) print(cost(db,"finance",7)両方の権限がある場合を読む) print(cost(db,"finance",8)資源の権限がない場合を読む)

aliceは欄の権限がなく空、financeの7は100、8は資源の権限がなく空です。項目の権限だけでも資源の権限だけでも足りません。

7全保存行から更新範囲を確認する

検証用に元表を参照し、実際に変わった行を確認します。

print(db.execute("SELECT id,body,cost許可した変更以外が残っていないか照合する FROM docs ORDER BY id").fetchall())

変更は文書7のbodyだけです。他者の文書8、読取り専用の9、各costは維持されています。

まとめ

主体・資源・操作を組として認可します。

readの許可をupdateへ流用しません。

機密項目の許可に加え、その資源の取得権限も確認します。

操作履歴と解説を振り返れます。

Python 3.15 / SQLite / 固定の主体・資源・時刻

1
from sd06 import * db = database() print(db.execute("SELECT subject,doc,action FROM grants WHERE subject IN ('alice','bob') ORDER BY subject,doc,action").fetchall())
[('alice', 7, 'read'), ('alice', 7, 'update'), ('alice', 9, 'read'), ('bob', 8, 'read'), ('bob', 8, 'update')]
2
print(read(db,"alice",7)) print(read(db,"alice",8))
[(7, 'report', 'red-secret')]
[]
3
print(read(db,"bob",8)) print(read(db,"bob",7))
[(8, 'report', 'blue-secret')]
[]
4
print(read(db,"alice",9)) print(edit(db,"alice",9,"changed")) print(db.execute("SELECT body FROM docs WHERE id=9").fetchone())
[(9, 'memo', 'red-note')]
0
('red-note',)
5
print(edit(db,"alice",7,"revised")) print(read(db,"alice",7)) print(edit(db,"alice",8,"changed"))
1
[(7, 'report', 'revised')]
0
6
print(cost(db,"alice",7)) print(cost(db,"finance",7)) print(cost(db,"finance",8))
[]
[(100,)]
[]
7
print(db.execute("SELECT id,body,cost FROM docs ORDER BY id").fetchall())
[(7, 'revised', 100), (8, 'blue-secret', 200), (9, 'red-note', 300)]

演習完了