Monorepo for Tangled
at sl/rbac2test 143 lines 3.5 kB view raw
1package rbac2 2 3import ( 4 "database/sql" 5 _ "embed" 6 "fmt" 7 8 adapter "github.com/Blank-Xu/sql-adapter" 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "github.com/casbin/casbin/v2" 11 "github.com/casbin/casbin/v2/model" 12 "github.com/casbin/casbin/v2/util" 13 "tangled.org/core/rbac2/bytesadapter" 14) 15 16const ( 17 Model = ` 18[request_definition] 19r = sub, dom, obj, act 20 21[policy_definition] 22p = sub, dom, obj, act 23 24[role_definition] 25g = _, _, _ 26 27[policy_effect] 28e = some(where (p.eft == allow)) 29 30[matchers] 31m = g(r.sub, p.sub, r.dom) && keyMatch4(r.dom, p.dom) && r.obj == p.obj && r.act == p.act 32` 33) 34 35type Enforcer struct { 36 e *casbin.Enforcer 37} 38 39//go:embed tangled_policy.csv 40var tangledPolicy []byte 41 42func NewEnforcer(path string) (*Enforcer, error) { 43 db, err := sql.Open("sqlite3", path+"?_foreign_keys=1") 44 if err != nil { 45 return nil, err 46 } 47 return NewEnforcerWithDB(db) 48} 49 50func NewEnforcerWithDB(db *sql.DB) (*Enforcer, error) { 51 m, err := model.NewModelFromString(Model) 52 if err != nil { 53 return nil, err 54 } 55 56 a, err := adapter.NewAdapter(db, "sqlite3", "acl2") 57 if err != nil { 58 return nil, err 59 } 60 61 // // PATCH: create unique index to make `AddPoliciesEx` work 62 // _, err = db.Exec(fmt.Sprintf( 63 // `create unique index if not exists uq_%[1]s on %[1]s (p_type,v0,v1,v2,v3,v4,v5);`, 64 // tableName, 65 // )) 66 // if err != nil { 67 // return nil, err 68 // } 69 70 e, _ := casbin.NewEnforcer() // NewEnforcer() without param won't return error 71 // e.EnableLog(true) 72 73 // NOTE: casbin clears the model on init, so we should intialize with temporary adapter first 74 // and then override the adapter to sql-adapter. 75 // `e.SetModel(m)` after init doesn't work for some reason 76 if err := e.InitWithModelAndAdapter(m, bytesadapter.NewAdapter(tangledPolicy)); err != nil { 77 return nil, err 78 } 79 80 // load dynamic policy from db 81 e.EnableAutoSave(false) 82 if err := a.LoadPolicy(e.GetModel()); err != nil { 83 return nil, err 84 } 85 e.AddNamedDomainMatchingFunc("g", "keyMatch4", util.KeyMatch4) 86 e.BuildRoleLinks() 87 e.SetAdapter(a) 88 e.EnableAutoSave(true) 89 90 return &Enforcer{e}, nil 91} 92 93// CaptureModel returns copy of current model. Used for testing 94func (e *Enforcer) CaptureModel() model.Model { 95 return e.e.GetModel().Copy() 96} 97 98func (e *Enforcer) Enforcer() *casbin.Enforcer { 99 return e.e 100} 101 102func (e *Enforcer) hasImplicitRoleForUser(name string, role string, domain ...string) (bool, error) { 103 roles, err := e.e.GetImplicitRolesForUser(name, domain...) 104 if err != nil { 105 return false, err 106 } 107 for _, r := range roles { 108 if r == role { 109 return true, nil 110 } 111 } 112 return false, nil 113} 114 115// setRoleForUser sets single user role for specified domain. 116// All existing users with that role will be removed. 117func (e *Enforcer) setRoleForUser(name string, role string, domain ...string) error { 118 currentUsers, err := e.e.GetUsersForRole(role, domain...) 119 if err != nil { 120 return err 121 } 122 123 for _, oldUser := range currentUsers { 124 _, err = e.e.DeleteRoleForUser(oldUser, role, domain...) 125 if err != nil { 126 return err 127 } 128 } 129 130 _, err = e.e.AddRoleForUser(name, role, domain...) 131 return err 132} 133 134// validateAtUri enforeces AT-URI to have valid did as authority and match collection NSID. 135func validateAtUri(uri syntax.ATURI, expected string) error { 136 if !uri.Authority().IsDID() { 137 return fmt.Errorf("expected at-uri with did") 138 } 139 if expected != "" && uri.Collection().String() != expected { 140 return fmt.Errorf("incorrect repo at-uri collection nsid '%s' (expected '%s')", uri.Collection(), expected) 141 } 142 return nil 143}