Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

at icy/yrolzt 9.1 kB View raw
1package rbac 2 3import ( 4 "database/sql" 5 "slices" 6 "strings" 7 8 adapter "github.com/Blank-Xu/sql-adapter" 9 "github.com/casbin/casbin/v2" 10 "github.com/casbin/casbin/v2/model" 11) 12 13const ( 14 ThisServer = "thisserver" // resource identifier for local rbac enforcement 15) 16 17const ( 18 Model = ` 19[request_definition] 20r = sub, dom, obj, act 21 22[policy_definition] 23p = sub, dom, obj, act 24 25[role_definition] 26g = _, _, _ 27 28[policy_effect] 29e = some(where (p.eft == allow)) 30 31[matchers] 32m = r.act == p.act && r.dom == p.dom && r.obj == p.obj && g(r.sub, p.sub, r.dom) 33` 34) 35 36type Enforcer struct { 37 E *casbin.SyncedEnforcer 38} 39 40func NewEnforcer(path string) (*Enforcer, error) { 41 m, err := model.NewModelFromString(Model) 42 if err != nil { 43 return nil, err 44 } 45 46 db, err := sql.Open("sqlite3", path+"?_foreign_keys=1&_journal_mode=WAL&_busy_timeout=5000") 47 if err != nil { 48 return nil, err 49 } 50 51 a, err := adapter.NewAdapter(db, "sqlite3", "acl") 52 if err != nil { 53 return nil, err 54 } 55 56 e, err := casbin.NewSyncedEnforcer(m, a) 57 if err != nil { 58 return nil, err 59 } 60 61 e.EnableAutoSave(false) 62 63 return &Enforcer{e}, nil 64} 65 66func (e *Enforcer) AddKnot(knot string) error { 67 // Add policies with patterns 68 _, err := e.E.AddPolicies([][]string{ 69 {"server:owner", knot, knot, "server:invite"}, 70 {"server:member", knot, knot, "repo:create"}, 71 }) 72 if err != nil { 73 return err 74 } 75 76 // all owners are also members 77 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", knot) 78 return err 79} 80 81func (e *Enforcer) AddSpindle(spindle string) error { 82 // the internal repr for spindles is spindle:foo.com 83 spindle = intoSpindle(spindle) 84 85 _, err := e.E.AddPolicies([][]string{ 86 {"server:owner", spindle, spindle, "server:invite"}, 87 }) 88 if err != nil { 89 return err 90 } 91 92 // all owners are also members 93 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", spindle) 94 return err 95} 96 97func (e *Enforcer) RemoveSpindle(spindle string) error { 98 spindle = intoSpindle(spindle) 99 _, err := e.E.DeleteDomains(spindle) 100 return err 101} 102 103func (e *Enforcer) RemoveKnot(knot string) error { 104 _, err := e.E.DeleteDomains(knot) 105 return err 106} 107 108func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) { 109 keepFunc := isNotSpindle 110 stripFunc := unSpindle 111 return e.getDomainsForUser(did, keepFunc, stripFunc) 112} 113 114func (e *Enforcer) GetSpindlesForUser(did string) ([]string, error) { 115 keepFunc := isSpindle 116 stripFunc := unSpindle 117 return e.getDomainsForUser(did, keepFunc, stripFunc) 118} 119 120func (e *Enforcer) AddKnotOwner(domain, owner string) error { 121 return e.addOwner(domain, owner) 122} 123 124func (e *Enforcer) RemoveKnotOwner(domain, owner string) error { 125 return e.removeOwner(domain, owner) 126} 127 128func (e *Enforcer) AddKnotMember(domain, member string) error { 129 _, err := e.addMember(domain, member) 130 return err 131} 132 133func (e *Enforcer) RemoveKnotMember(domain, member string) error { 134 _, err := e.removeMember(domain, member) 135 return err 136} 137 138func (e *Enforcer) TryAddKnotMember(domain, member string) (bool, error) { 139 return e.addMember(domain, member) 140} 141 142func (e *Enforcer) TryRemoveKnotMember(domain, member string) (bool, error) { 143 return e.removeMember(domain, member) 144} 145 146func (e *Enforcer) AddSpindleOwner(domain, owner string) error { 147 return e.addOwner(intoSpindle(domain), owner) 148} 149 150func (e *Enforcer) RemoveSpindleOwner(domain, owner string) error { 151 return e.removeOwner(intoSpindle(domain), owner) 152} 153 154func (e *Enforcer) AddSpindleMember(domain, member string) error { 155 _, err := e.addMember(intoSpindle(domain), member) 156 return err 157} 158 159func (e *Enforcer) RemoveSpindleMember(domain, member string) error { 160 _, err := e.removeMember(intoSpindle(domain), member) 161 return err 162} 163 164func (e *Enforcer) TryAddSpindleMember(domain, member string) (bool, error) { 165 return e.addMember(intoSpindle(domain), member) 166} 167 168func (e *Enforcer) TryRemoveSpindleMember(domain, member string) (bool, error) { 169 return e.removeMember(intoSpindle(domain), member) 170} 171 172func repoPolicies(member, domain, repo string) [][]string { 173 return [][]string{ 174 {member, domain, repo, "repo:settings"}, 175 {member, domain, repo, "repo:push"}, 176 {member, domain, repo, "repo:owner"}, 177 {member, domain, repo, "repo:invite"}, 178 {member, domain, repo, "repo:delete"}, 179 {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo 180 } 181} 182func (e *Enforcer) AddRepo(member, domain, repo string) error { 183 err := checkRepoFormat(repo) 184 if err != nil { 185 return err 186 } 187 188 _, err = e.E.AddPolicies(repoPolicies(member, domain, repo)) 189 return err 190} 191func (e *Enforcer) RemoveRepo(member, domain, repo string) error { 192 err := checkRepoFormat(repo) 193 if err != nil { 194 return err 195 } 196 197 _, err = e.E.RemovePolicies(repoPolicies(member, domain, repo)) 198 return err 199} 200 201var ( 202 collaboratorPolicies = func(collaborator, domain, repo string) [][]string { 203 return [][]string{ 204 {collaborator, domain, repo, "repo:collaborator"}, 205 {collaborator, domain, repo, "repo:settings"}, 206 {collaborator, domain, repo, "repo:push"}, 207 } 208 } 209) 210 211func (e *Enforcer) AddCollaborator(collaborator, domain, repo string) error { 212 err := checkRepoFormat(repo) 213 if err != nil { 214 return err 215 } 216 217 _, err = e.E.AddPolicies(collaboratorPolicies(collaborator, domain, repo)) 218 return err 219} 220 221func (e *Enforcer) RemoveCollaborator(collaborator, domain, repo string) error { 222 err := checkRepoFormat(repo) 223 if err != nil { 224 return err 225 } 226 227 _, err = e.E.RemovePolicies(collaboratorPolicies(collaborator, domain, repo)) 228 return err 229} 230 231func (e *Enforcer) WipeRepoPolicies(domain, repo string) error { 232 if err := checkRepoFormat(repo); err != nil { 233 return err 234 } 235 _, err := e.E.RemoveFilteredPolicy(1, domain, repo) 236 return err 237} 238 239func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) { 240 var membersWithoutRoles []string 241 242 // this includes roles too, casbin does not differentiate. 243 // the filtering criteria is to remove strings not starting with `did:` 244 members, err := e.E.GetImplicitUsersForRole(role, domain) 245 for _, m := range members { 246 if strings.HasPrefix(m, "did:") { 247 membersWithoutRoles = append(membersWithoutRoles, m) 248 } 249 } 250 if err != nil { 251 return nil, err 252 } 253 254 slices.Sort(membersWithoutRoles) 255 return slices.Compact(membersWithoutRoles), nil 256} 257 258func (e *Enforcer) GetKnotUsersByRole(role, domain string) ([]string, error) { 259 return e.GetUserByRole(role, domain) 260} 261 262func (e *Enforcer) GetSpindleUsersByRole(role, domain string) ([]string, error) { 263 return e.GetUserByRole(role, intoSpindle(domain)) 264} 265 266func (e *Enforcer) GetCollaboratorsByRepo(domain string) (map[string][]string, error) { 267 policies, err := e.E.GetFilteredNamedPolicy("p", 3, "repo:collaborator") 268 if err != nil { 269 return nil, err 270 } 271 272 byRepo := make(map[string][]string) 273 for _, p := range policies { 274 subject, dom, repo := p[0], p[1], p[2] 275 if dom != domain || !strings.HasPrefix(subject, "did:") { 276 continue 277 } 278 byRepo[repo] = append(byRepo[repo], subject) 279 } 280 281 for repo, users := range byRepo { 282 slices.Sort(users) 283 byRepo[repo] = slices.Compact(users) 284 } 285 286 return byRepo, nil 287} 288 289func (e *Enforcer) IsKnotOwner(user, domain string) (bool, error) { 290 return e.isRole(user, "server:owner", domain) 291} 292 293func (e *Enforcer) IsKnotMember(user, domain string) (bool, error) { 294 return e.isRole(user, "server:member", domain) 295} 296 297func (e *Enforcer) IsSpindleOwner(user, domain string) (bool, error) { 298 return e.isRole(user, "server:owner", intoSpindle(domain)) 299} 300 301func (e *Enforcer) IsSpindleMember(user, domain string) (bool, error) { 302 return e.isRole(user, "server:member", intoSpindle(domain)) 303} 304 305func (e *Enforcer) IsKnotInviteAllowed(user, domain string) (bool, error) { 306 return e.isInviteAllowed(user, domain) 307} 308 309func (e *Enforcer) IsSpindleInviteAllowed(user, domain string) (bool, error) { 310 return e.isInviteAllowed(user, intoSpindle(domain)) 311} 312 313func (e *Enforcer) IsRepoCreateAllowed(user, domain string) (bool, error) { 314 return e.E.Enforce(user, domain, domain, "repo:create") 315} 316 317func (e *Enforcer) IsRepoDeleteAllowed(user, domain, repo string) (bool, error) { 318 return e.E.Enforce(user, domain, repo, "repo:delete") 319} 320 321func (e *Enforcer) IsRepoOwner(user, domain, repo string) (bool, error) { 322 return e.E.Enforce(user, domain, repo, "repo:owner") 323} 324 325func (e *Enforcer) IsRepoCollaborator(user, domain, repo string) (bool, error) { 326 return e.E.Enforce(user, domain, repo, "repo:collaborator") 327} 328 329func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) { 330 return e.E.Enforce(user, domain, repo, "repo:push") 331} 332 333func (e *Enforcer) IsSettingsAllowed(user, domain, repo string) (bool, error) { 334 return e.E.Enforce(user, domain, repo, "repo:settings") 335} 336 337func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) { 338 return e.E.Enforce(user, domain, repo, "repo:invite") 339} 340 341// given a repo, what permissions does this user have? repo:owner? repo:invite? etc. 342func (e *Enforcer) GetPermissionsInRepo(user, domain, repo string) []string { 343 var permissions []string 344 res := e.E.GetPermissionsForUserInDomain(user, domain) 345 for _, p := range res { 346 // get only permissions for this resource/repo 347 if p[2] == repo { 348 permissions = append(permissions, p[3]) 349 } 350 } 351 352 return permissions 353}