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:
51
52

Thêm thành công

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:
53
54

Thêm user 1, 2 làm trainer lớp Java

55
56

Thêm user 3, 4 làm student lớp Java

57
58

Xóa trainer 2 ra khỏi lớp Java

59
60

Xóa student 4 ra khỏi lớp Java

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:
61

Ta sẽ thêm user 2 làm trainer, user 4 làm student của lớp NodeJs

62
63

User 1, 3 được phép truy cập vào http://localhost:8080/courses/java

64
65

Trong khi user 2, 4 thì không

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:
66
67
68

Thêm blog thành công

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:
69
70

Xóa thành công blog 2 của user 2

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:
71
72

Là tác giả của blog

73
74

Không phải tác giả của blog

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