package models import ( "context" "errors" "github.com/google/uuid" "github.com/open-policy-agent/opa/v1/rego" ) type JWT struct { SessionId string `json:"session_id"` UserId int32 `json:"user_id"` Role Role `json:"role"` ExpiresAt int64 `json:"exp"` IssuedAt int64 `json:"iat"` NotBefore int64 `json:"nbf"` Permissions []grant `json:"permissions"` } func (j JWT) Valid() error { if uuid.Validate(j.SessionId) != nil { return errors.New("invalid session id") } if j.UserId == 0 { return errors.New("empty user id") } if j.ExpiresAt == 0 { return errors.New("empty expires at") } if j.IssuedAt == 0 { return errors.New("empty issued at") } if j.NotBefore == 0 { return errors.New("empty not before") } if len(j.Permissions) == 0 { return errors.New("empty permissions") } return nil } 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 } ` 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) } }