「誰」が「何」をできるかをOPAのレスポンスで返す
OPA(Open Policy Agent)で「○○をやっていいですか?」に対して「Allow/Deny」の返答がくるパターンについていくつか記事にしました。
今回は、 「誰」が「何」をしていいか
をOPAからのレスポンスで知る方法について検証したので共有します。
どういった場面でこれを知りたくなるかと言うと、
例えばSPA(Single Page Application)で、 ログイン中のユーザー
が「何」をして良いか知りたいときが考えられます。
この情報があることによって、
- 新規作成ボタンを表示する
- 編集ボタンを表示する
- 削除ボタンを表示する
- 特定のメニューを表示する
などの判断をSPA側で行うことができるようになります。
今回の記事で作成するPolicyもOPAのSlackでやりとりがされていた内容をちょっとカスタマイズさせてもらったものになります。
ドキュメントには登場しないようなユースケースがコアメンバーによってPlaygroundでシェアされていたりするので、OPAに興味がある方はぜひSlackに参加することをおすすめします!
権限の定義
今回の例では以下のような状況があると仮定します。
article
というリソースがあるalice
はarticle
をread
できるしwrite
もできる権限があるbob
はarticle
をread
する権限しかもっていない
これをOPAのRegoで表現すると次のようになります。
package foo.bar permission["alice"] = [ { "resources": ["article"], "actions": ["read", "write"] } ] permission["bob"] = [ { "resources": ["article"], "actions": ["read"]} ]
実際はこの permission
をハードコードすることは無く、データベースなりどこかに保持されているところからOPAに読みこませることになりますが、Playground上で表現するために直でデータを書いておきます。
指定したユーザーの権限を取得
では、データが用意されたのでここから aliceの権限を取得
したいとなったらどうすれば良いか。
欲しいのは以下の情報です。
[ { "resources": ["article"], "actions": ["read", "write"] } ]
OPAでは「Allow/Deny」な答えを返すことが多いのですが、オブジェクトを返すこともできます。 これがドキュメントにも書かれている「Generating Objects」をする方法です。
OPAへのリクエストの input
を { "user": "alice" }
とした場合に、 alice
の権限が返ってくるようにするには次のようなルールを定義します。
user_permission = perm { perm := permission[input.user] }
ここでポイントとなる点は user_permission = perm
の、 = perm
の部分です。
普通のルールの場合は key
だけ指定して value
側を指定することはありません。
value
側を指定(ここで言う perm
)し、その値をルールの中身で用意することでレスポンスとして返すことができます。
実際に試してみましょう!下図はPlayground実行している様子です。
PlaygroundにはちょっとしたTipsがあって、図のように、選択した部分のルールのみを実行してOutputを確認することができます。
下記リンクからPlaygroundが実行できるので、ぜひ試してみてください!
Inputを
{ "user": "alice" }
としたことで、 user_permission
のみを実行した結果が
[ { "actions": [ "read", "write" ], "resources": [ "article" ] } ]
となり、 alice
の権限がレスポンスとしてオブジェクトで返ってきているのがわかります。
また、 bob
であれば以下のように、 article
に対して read
しか許可されていないのがわかります。
[ { "actions": [ "read" ], "resources": [ "article" ] } ]
このように、「Allow/Deny」のレスポンス以外にも、オブジェクトでレスポンスを返すことができるということもわかりました!
そして、例えばこのレスポンスを使ってUI側では
write
があるから新規作成、編集、削除ボタンを表示する
という処理が書けて、ログインしているユーザーの権限に応じて画面の見た目を変えることができます。
やっていいかどうかの判断
「誰」が「何」をしていいか
を確認する方法についてはこれでOKですが、実際に「やっていいかどうか」の判断も必要になります。
具体的には以下の質問への答えが必要になります。
alice
はarticle
にwrite
できますか?bob
はarticle
にwrite
できますか?
この質問をするときのInputはきっとこんなJSONになると思います。
{ "user": "alice", "action": "write", "resource": "article" }
そしてこの質問に対して、「Allow/Deny」の答えを出すためのRuleはRegoで以下のように表現することができます。
default allow = false allow { permission[input.user][i].resources[_] == input.resource permission[input.user][i].actions[_] == input.action }
permission["alice"]
の中のデータを順番にチェック- i番目の情報の
resources
にarticle
が含まれるかチェック - i番目の情報の
actions
にwrite
が含まれるかチェック
結果として、 alice
は { "resources": ["article"], "actions": ["read", "write"] }
という permission
を持っているので、すべて true
となるので、結果も true
です!
逆に bob
の情報を下のように渡してみるとどうなるでしょう?
{ "user": "bob", "action": "write", "resource": "article" }
bob
には { "resources": ["article"], "actions": ["read"]}
という permission
しか定義されていないので、 write
できるかという質問に対しては false
になります。
よって、このリクエストの結果は false
になります。
まとめ
- OPAは「Allow/Deny」以外にもオブジェクトをレスポンスとして返すことができる
- 「誰」が「何」をできるか、を返すことでUIでその情報を使うことができる(ボタンの表示・非表示など)