Go x gin x gormなWebアプリでロールベースのアクセス制御を実装する

  • June 30, 2023

アプリの中でユーザごとに権限を分けて見れる画面を制御したく、Goではどのように実装すれば良いか調べました。
PHPのフレームワークだと既にその中にRBACのパッケージが入っていたのですが。今使っているフレームワークginはかなり軽量で必要な機能は都度自分で選んでimportします。
PHPのリッチなフレームワークに慣れていたのでGoってそういうもんなのか?と最初は新鮮でした。

色々調べてみてCasbin というライブラリが良さそうだったので採用してみました。
参考にさせていただいたのはこちら の記事です。
とても詳細にステップバイステップで説明してあり有難かったです。
この記事ではRBAC権限制御について書きます。
ログイン認証は既に実装されている前提です。

設定ファイルの作成

Casbinのアクセス制御モデルを用意するために設定ファイルを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

CasbinはACLやRBAC、ABACなど様々なタイプのアクセス制御の実装ができるようで、そのタイプごとに設定ファイルが異なります。
詳細はこちら をご参照のこと。
今回はRBAC (ロールベースのアクセス制御)を実装するのでこの設定 を使用します。

作成したファイルをrouter.goの中で読み込んでいきます。

Policyの作成

ポリシーはロールごとの権限制御設定のことです。
公式ドキュメントではcsvファイルを用意するように書いてありますが、私はDBに保存しました。

掲示板なら
「一般ユーザー」ロールのユーザは「掲示板」の「閲覧」ができる。
「メンバー」ロールのユーザは「掲示板」の「書き込み」ができる。
といった内容を以下のように設定していきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// gormアダプターの初期化
adapter, err := gormadapter.NewAdapterByDB(db)
if err != nil {
    panic(fmt.Sprintf("failed to initialize casbin adapter: %v", err))
}

// 設定ファイルの読み込み
enforcer, err := casbin.NewEnforcer("config/rbac_model.conf", adapter)
if err != nil {
    panic(fmt.Sprintf("failed to create casbin enforcer: %v", err))
}

//ポリシーの作成
if hasPolicy := enforcer.HasPolicy("anonymous", "board", "read"); !hasPolicy {
    enforcer.AddPolicy("anonymous", "board", "read")
}
if hasPolicy := enforcer.HasPolicy("member", "board", "write"); !hasPolicy {
    enforcer.AddPolicy("member", "board", "write")
}
if hasPolicy := enforcer.HasPolicy("admin", "board", "write"); !hasPolicy {
    enforcer.AddPolicy("admin", "board", "write")
}

実行後、DBにcasbin_ruleというテーブルが作成され、こんな感じにポリシーが登録されます。

sb

Casbin middleware

以下のようなミドルウェアを作成します。
ログインしているユーザが正しい権限をもっているかどうか判定しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// obj -> 上記例の掲示板のポリシー設定においての「board」
// act -> 上記例の掲示板のポリシー設定においての「read」や「write」
func Authorize(obj string, act string, enforcer *casbin.Enforcer) gin.HandlerFunc {
    return func(c *gin.Context) {
    // 現在のログインユーザを取得
    sub, existed := c.Get("userID")
    if !existed {
        c.AbortWithStatusJSON(401, gin.H{"msg": "ログインしてください。"})
        return
    }

    // DBからポリシーの取得
    err := enforcer.LoadPolicy()
    if err != nil {
        c.AbortWithStatusJSON(500, gin.H{"msg": "ポリシーの取得に失敗しました。"})
        return
    }

    // 権限チェック
    ok, err := enforcer.Enforce(fmt.Sprint(sub), obj, act)
    if err != nil {
        c.AbortWithStatusJSON(500, gin.H{"msg": "エラー"})
        return
    }

    if !ok {
        c.AbortWithStatusJSON(403, gin.H{"msg": "権限がありません。"})
        return
    }
		c.Next()
}
}

このミドルウェアをrouter.goの中で

routes.GET("/write", middleware.Authorize("board", "write", enforcer), WriteThread)

こんな感じで使います。

ユーザーへのRoleの付与

アカウントにポリシー権限を付与するのは以下のようにします。

1
2
// メンバー権限を付与
enforcer.AddGroupingPolicy(fmt.Sprint(user.ID), "member")

これをユーザ登録の処理の中で加えれば良いです。
権限を付与するとcasbin_ruleテーブルにはユーザIDに対してどのポリシーが紐づくかの情報が保存されます。
sb
ptypeカラムがg, v0カラムがユーザID, v1カラムがポリシーとなっています。
このレコードをGroupingPolicyとCasbinでは呼ぶようです。
上記のレコードではユーザIDが14のアカウントはメンバー権限を持つので掲示板への書き込みが出来ると読み解けます。


また、特定のGroupingPolicyを持っているアカウントについてだけ処理をしたい場合などはこんな感じにif文を書いて実装しました。

1
2
3
if enforcer.HasNamedGroupingPolicy("g", fmt.Sprint(loginUserId), "admin") {
    // admin権限のユーザにのみ可能な処理
}

さいごに

簡単ですがCasbinでRBACを実現したやり方でした。
Casbinはロールの階層化などさらに発展的な使い方もできるようです。

ログイン認証などから詳しく知りたい方は冒頭にも引用した記事がとても参考になります!
Thanks Dipesh!!!
https://articles.wesionary.team/authorization-in-golang-projects-using-casbin-f8fad744dae5

comments powered by Disqus

関連の投稿

2023年クリスマス・イヴの武蔵野市長選で感じたこと。波乱尽くしの年末年始。

  • February 23, 2024

2023年、外国人参政権や米屋騒動など様々な分断を産んで武蔵野市を荒らしまくった松下前市長が国政に挑むため辞任。 投票率の下がるクリスマス・イヴに市長選挙が設定されるタイミングで辞任するという姑息さを最後まで見せつけました。 松下前市長の後継・笹岡ゆうこ氏が極左天国・武蔵野市で組織票を得て優位かとみられましたが、一般市民の皆さんの熱意によって小美濃新市長が誕生しました。 この結果は吉祥寺徒歩圏内に住む杉並区民としてもとても嬉しいことでした。 SNSでの選挙応援について感じたこと 管理人は杉並区政についての情報収集を目的としてSNSのXにアカウントを作っています。

もっと読む

【考察】岸本聡子区長~黒い人脈~(随時加筆)

  • October 30, 2023

岸本区長の人脈を考える上で外せないのは西荻窪という土地でしょう。 西荻窪には彼女の住まいがあるのは勿論ですが、岸本区長の支持母体とも言える人達が集まっています。 また、岸本区長に近しいブランシャー明日香議員のカフェ「カワセミピプレット」や山名かなこ議員のNPO法人「The F-Word」も西荻に存在します。 西荻平和通り商店街会長 区政ウォッチャーなら言わずと知れた岸本聡子をかつぎだした人物、水越強 氏。

もっと読む

WYSIWYGエディタを導入してみた

  • August 23, 2023

試運転中の掲示板アプリはユーザーがスレッドを立てることが出来るのですが、 自由度が高い部分があり、HTMLをユーザー自身に書いてもらう形になっています。 その際に、エンジニアならまだしも、一般のユーザーには敷居が高いかもしれないと思い、WYSIWYGエディタを試してみることにしました。 CLEditorでサクッと 色々なWYSIWYGエディタがあるようなのですが、シンプルで簡単に導入できそうなCLEditor を使ってみました。

もっと読む