package models import ( "context" "encoding/json" "github.com/open-policy-agent/opa/v1/rego" "golang.org/x/crypto/bcrypt" "time" ) type Role int32 const ( RoleGuest Role = -1 RoleStudent Role = 0 RoleTeacher Role = 1 RoleAdmin Role = 2 ) func (r Role) String() string { switch r { case RoleGuest: return "guest" case RoleStudent: return "student" case RoleTeacher: return "teacher" case RoleAdmin: return "admin" } panic("invalid role") } type Action string const ( Create Action = "create" Read Action = "read" Update Action = "update" Delete Action = "delete" ) type Resource string const ( ResourceAnotherUser Resource = "another-user" ResourceMeUser Resource = "me-user" ResourceListUser Resource = "list-user" ResourceOwnSession Resource = "own-session" ) type grant struct { Action Action `json:"action"` Resource Resource `json:"resource"` } var Grants = map[string][]grant{ RoleGuest.String(): {}, RoleStudent.String(): { {Read, ResourceAnotherUser}, {Read, ResourceMeUser}, {Update, ResourceOwnSession}, {Delete, ResourceOwnSession}, }, RoleTeacher.String(): { {Create, ResourceAnotherUser}, {Read, ResourceAnotherUser}, {Read, ResourceMeUser}, {Read, ResourceListUser}, {Update, ResourceOwnSession}, {Delete, ResourceOwnSession}, }, RoleAdmin.String(): { {Create, ResourceAnotherUser}, {Read, ResourceAnotherUser}, {Read, ResourceMeUser}, {Read, ResourceListUser}, {Update, ResourceAnotherUser}, {Update, ResourceOwnSession}, {Delete, ResourceAnotherUser}, {Delete, ResourceOwnSession}, }, } const module = `package app.rbac default allow := false allow if { some grant in input.role_grants[input.role] input.action == grant.action input.resource == grant.resource } ` type User struct { Id int32 `db:"id"` Username string `db:"username"` HashedPassword string `db:"hashed_pwd"` CreatedAt time.Time `db:"created_at"` ModifiedAt time.Time `db:"modified_at"` Role Role `db:"role"` } func (user *User) MarshalJSON() ([]byte, error) { m := map[string]interface{}{ "id": user.Id, "username": user.Username, "created_at": user.CreatedAt, "modified_at": user.ModifiedAt, "role": user.Role, } b, err := json.Marshal(m) if err != nil { return nil, err } return b, nil } func (user *User) IsSamePwd(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(user.HashedPassword), []byte(password)) if err != nil { return false } return true } var query rego.PreparedEvalQuery func (r Role) HasPermission(action Action, resource Resource) bool { ctx := context.TODO() input := map[string]interface{}{ "action": action, "resource": resource, "role": r.String(), "role_grants": Grants, } results, err := query.Eval(ctx, rego.EvalInput(input)) if err != nil { panic(err) } return results.Allowed() } func init() { var err error ctx := context.TODO() query, err = rego.New( rego.Query("data.app.rbac.allow"), rego.Module("ms-auth.rego", module), ).PrepareForEval(ctx) if err != nil { panic(err) } }