これはGopher道場 Advent Calendar 2018の3日目の記事です。
昨日はt0w4さんの「golangでエスケープシーケンスを扱う(文字色、カーソル移動)」でした。

Casbinという認証ライブラリがある。
Goで書かれたライブラリで、Java, Node, PHP でも移植されていたりするらしい。
色んなフレームワークやデータストアとの連携用アダプタも用意されていて、今回はGin,GORMと連携してみた。

基本的な使い方

モデルとポリシーを(デフォルトではファイルに)定義し、それらを読み込んでEnforcerを作成する。
EnforcerEnforce()の引数にチェックしたい情報を渡すと、
モデル・ポリシー・引数の情報から認証可否を返すようになっている。

モデルの書き方によって様々なアクセス制御モデルに対応でき、サンプルも多数用意されている。
ちなみにモデルとポリシーのオンラインエディタも用意されていて、
シンタックスのチェックや認証可否テストをWeb上でできるように見えるが、
どうもこのボタンは機能しておらず必ず「No Error!」「Success!」を表示する模様…。

Ginと連携する

gin-contrib/authzというアダプタが提供されているが、微妙に使いにくい…。
authz.goを見る限り、Basic認証のユーザー名にしか対応していないようだ。
それ以上の気の利いた事もしていないようなので、これくらいならアダプタを使わず自前で実装してしまってもいいように思う。

こんな感じのmiddlewareを書けば、いかようにでも対応できそうだ。
func Authorize() gin.HandlerFunc {
	e := casbin.NewEnforcer("config/casbin/model.conf", "config/casbin/policy.csv")
	return func(ctx *gin.Context) {
		req := ctx.Request
		role, ok := ctx.Get("role")
		if !ok {
			ctx.AbortWithError(http.StatusInternalServerError, errors.New("InternalServerError"))
			return
		}
		if !e.Enforce(role, req.URL.Path, req.Method) {
			ctx.AbortWithError(http.StatusForbidden, errors.New("Forbidden"))
			return
		}
		return
	}
}
("role"は事前に別middlewareでctx.Set("role", ...)と設定されている前提)

GORMと連携する

casbin/gorm-adapterというアダプタが提供されているが、これまた微妙に使いにくい…。
このアダプタは、ポリシーをデフォルトのCSVではなくRDBに持たせる事ができる。
casbin_ruleというテーブルで管理する事になっており、テーブルが存在しなければ自動で作成もしてくれるのだが、
カラム名がv0, v1, v2...というものになっていて、見やすいか?というと少し疑問ではある。
しかし、CSVで管理していたものをそのまま愚直に持たせるにはこうするしかなかったのかもしれない。
PTypeによって以降のカラムの意味も変わってくるし…。

ちなみにこのカラム名のスタイルはPostgreSQL AdapterXorm Adapterでも同様なので、
そういうものと割り切るのが正しいのかもしれない。
というか、Gorm Adapter がサードパーティ製ではなく casbin 自体が作成しているアダプタなので、このやり方が正当なのか。

他の選択肢

Casbinは複数のアクセス制御モデルに対応しており、RBAC(ロールベースアクセス制御)にも対応しているのだが、
柔軟なアクセス制御モデル対応のためにポリシー管理が煩雑になっているところもあるように思う。

他の認証ライブラリとしてgoRBACというものもあるようだ。
GitHubのスター数やドキュメントの手厚さはCasbinのほうが上だが、
RBACを使用するのであればそれに特化したgoRBACも選択肢の一つになるかもしれない。

明日は(また)t0w4さんの「Golangのio.Writer と標準出力、標準エラー出力についてちょっと深掘りしてみた」です。

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .