Ở phần 1, mình đã làm việc với các role đơn giản. Tức các role mình chỉ cần check xem nó có vào được endpoint hay không. Trong phần này, chũng ta sẽ làm việc với các role phức tạp hơn.
Ví dụ: hệ thống TechMaster có các lớp học, anh A đăng kí lớp Java SpringBoot thì anh A chỉ truy cập được lớp Java chứ không thể truy cập được vào lớp PHP Laravel hay NodeJS. Chúng ta không thể gán role Student cho anh A được, phải làm khác đi 1 chút. Ở đây, các lớp học được phân biệt với nhau bằng ID, chúng ta sẽ dựa vào đây để phân quyền
Xem lại file conf ở phần trước:
// rbac_model.conf
[request_definition]
r = sub, obj, act
r2 = sub, obj, act
r3 = sub, obj, act
[policy_definition]
p = sub, obj, act
p2 = sub, obj, act
p3 = sub, obj, act
[role_definition]
g = _, _
g2 = _, _, _
g3 = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
e2 = some(where (p.eft == allow)) && !some(where (p.eft == deny))
e3 = some(where (p.eft == allow)) && !some(where (p.eft == deny))
[matchers]
m = keyMatch2(r.obj, p.obj) && r.act == p.act && g(r.sub, p.sub)
m2 = keyMatch2(r2.obj, p2.obj) && r2.act == p2.act && g2(r2.sub, keyGet2(r2.obj, p2.obj, 'id'), p2.sub)
m3 = keyMatch2(r3.obj, p3.obj) && r3.act == p3.act && g3(r3.sub, keyGet2(r3.obj, p3.obj, 'id'), p3.sub)
Hãy để ý tới m2. Ý tưởng ở đây là nhóm UserID lại với role và id, chỉ khi user có cùng role và id của lớp học thì mới được phép truy cập.
Khởi tạo lớp học
Việc này chỉ cần làm 1 lần duy nhất cho hệ thống. Chúng ta thêm 2 role: Trainer và Student, củng với endpoint http://localhost:8080/courses/:id, chính là endpoint sau này dùng để truy cập các lớp học.
// courses.go
func AddCourse() iris.Handler {
return func(ctx iris.Context) {
path := "http://localhost:8080/courses/:id"
_, err := rbac.AddNamedPolicy("p2", "trainer", path, "GET")
if err != nil {
ctx.JSON("ERROR")
return
}
ok, err := rbac.AddNamedPolicy("p2", "student", path, "GET")
if !ok || err != nil {
ctx.JSON("ERROR")
return
}
ctx.JSON("success")
}
}
Test:
Thêm, xóa trainer và student
Ta sẽ nhập các UserID và ClassID:
// courses.go
type Class struct {
UserID []string `json:"user_id"`
ClassID string `json:"class_id"`
}
// nhập user_id: []string và class_id: string
func AddTrainer() iris.Handler {
return func(ctx iris.Context) {
var data Class
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
for _, trainer := range data.UserID {
_, err := rbac.AddNamedGroupingPolicy("g2", trainer, data.ClassID, "trainer")
if err != nil {
ctx.JSON("ERROR")
return
}
}
ctx.JSON("success")
}
}
// nhập user_id: []string và class_id: string
func AddStudents() iris.Handler {
return func(ctx iris.Context) {
var data Class
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
for _, student := range data.UserID {
_, err := rbac.AddNamedGroupingPolicy("g2", student, data.ClassID, "student")
if err != nil {
ctx.JSON("ERROR")
return
}
}
ctx.JSON("success")
}
}
// nhập user_id: []string và class_id: string
func DeleteTrainer() iris.Handler {
return func(ctx iris.Context) {
var data Class
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
for _, trainer := range data.UserID {
ok, err := rbac.RemoveNamedGroupingPolicy("g2", trainer, data.ClassID, "trainer")
if !ok || err != nil {
ctx.JSON("ERROR")
return
}
}
ctx.JSON("success")
}
}
// nhập user_id: []string và class_id: string
func DeleteStudent() iris.Handler {
return func(ctx iris.Context) {
var data Class
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
for _, student := range data.UserID {
ok, err := rbac.RemoveNamedGroupingPolicy("g2", student, data.ClassID, "student")
if !ok || err != nil {
ctx.JSON("ERROR")
return
}
}
ctx.JSON("success")
}
}
Test:
Xác thực với lớp học
Nhập vào UserID, hệ thống sẽ xem bạn có được quyền truy cập lớp học không, và truy cập với quyền gì:
// courses.go
// nhập user_id: string
func CheckClassPermission() iris.Handler {
return func(ctx iris.Context) {
var data User
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
enforceContext := casbin.NewEnforceContext("2")
path := "http://localhost:8080" + ctx.Path()
ok, err := rbac.Enforce(enforceContext, data.UserID, path, "GET")
if err != nil {
ctx.JSON(err)
return
}
if !ok {
ctx.JSON("you dont have permission!")
return
}
_, ok = ctx.Values().Set("id", data.UserID)
if !ok {
ctx.JSON("ERROR")
return
}
ctx.Next()
}
}
func ClassNotification() iris.Handler {
return func(ctx iris.Context) {
id := ctx.Params().Get("id")
userid := ctx.Values().GetString("id")
namedGroupingPolicy := rbac.GetFilteredNamedGroupingPolicy("g2", 0, userid, id)
var roles []string
for _, group := range namedGroupingPolicy {
roles = append(roles, group[2])
}
ctx.JSON("Hello, you have role:")
ctx.JSON(roles)
}
}
Test:
Khởi tạo blog
Ngoài lớp học, blog cũng là 1 thứ cần phân quyền dựa trên ID của bài viết để xem bạn có quyền sửa hay không.
// blog.go
// nhập user_id: string
func AddBlog() iris.Handler {
return func(ctx iris.Context) {
var data User
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
path := "http://localhost:8080/posts/:id"
_, err := rbac.AddNamedPolicy("p3", "author", path, "GET")
if err != nil {
ctx.JSON("ERROR")
return
}
blogNumber := rbac.GetNamedGroupingPolicy("g3")
// mặc định blog đầu tiên có id là 1
if len(blogNumber) == 0 {
_, err := rbac.AddNamedGroupingPolicy("g3", data.UserID, "1", "author")
if err != nil {
ctx.JSON("ERROR")
return
}
} else {
// nếu không, id blog sẽ bằng số blog hiện có cộng 1
id := strconv.Itoa(len(blogNumber) + 1)
_, err := rbac.AddNamedGroupingPolicy("g3", data.UserID, id, "author")
if err != nil {
ctx.JSON("ERROR")
return
}
}
id := strconv.Itoa(len(blogNumber) + 1)
ctx.JSON("success add blog " + id + " for user: " + data.UserID)
}
}
Test:
Xóa blog
Nhập vào UserID và các BlogID muốn xóa:
// blog.go
type Blog struct {
UserID string `json:"user_id"`
BlogID []string `json:"blog_id"`
}
// nhập user_id: string và blog_id: []string
func DeleteBlog() iris.Handler {
return func(ctx iris.Context) {
var data Blog
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
for _, blog := range data.BlogID {
_, err := rbac.RemoveNamedGroupingPolicy("g3", data.UserID, blog, "author")
if err != nil {
ctx.JSON("ERROR")
return
}
}
ctx.JSON("success")
}
}
Test:
Xác thực với blog
Nhập vào UserID, hệ thống sẽ xem bạn có được phải là author không. Ta thêm lại user 2 với blog 2:
// blog.go
// nhập user_id: string
func CheckBlogPermission() iris.Handler {
return func(ctx iris.Context) {
var data User
if err := ctx.ReadJSON(&data); err != nil {
ctx.JSON("can not get data")
return
}
enforceContext := casbin.NewEnforceContext("3")
path := "http://localhost:8080" + ctx.Path()
ok, err := rbac.Enforce(enforceContext, data.UserID, path, "GET")
if err != nil {
ctx.JSON(err)
return
}
if !ok {
ctx.JSON("you are not author of this blog")
return
}
ctx.Next()
}
}
func BlogNotification() iris.Handler {
return func(ctx iris.Context) {
ctx.JSON("you are author of this blog")
}
}
Test:
Kết
Vậy mình đã hoàn thành xong. Hi vọng mọi người có thêm nhiều kiến thức bổ ích.
Check code tại đây: https://github.com/lits-06/casbin_rbac
Bình luận