Monorepo for Tangled
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354package rbac
import ( "database/sql" "slices" "strings"
adapter "github.com/Blank-Xu/sql-adapter" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model")
const ( ThisServer = "thisserver" // resource identifier for local rbac enforcement)
const ( Model = `[request_definition]r = sub, dom, obj, act
[policy_definition]p = sub, dom, obj, act
[role_definition]g = _, _, _
[policy_effect]e = some(where (p.eft == allow))
[matchers]m = r.act == p.act && r.dom == p.dom && r.obj == p.obj && g(r.sub, p.sub, r.dom)`)
type Enforcer struct { E *casbin.SyncedEnforcer}
func NewEnforcer(path string) (*Enforcer, error) { m, err := model.NewModelFromString(Model) if err != nil { return nil, err }
db, err := sql.Open("sqlite3", path+"?_foreign_keys=1&_journal_mode=WAL&_busy_timeout=5000") if err != nil { return nil, err }
a, err := adapter.NewAdapter(db, "sqlite3", "acl") if err != nil { return nil, err }
e, err := casbin.NewSyncedEnforcer(m, a) if err != nil { return nil, err }
e.EnableAutoSave(false)
return &Enforcer{e}, nil}
func (e *Enforcer) AddKnot(knot string) error { // Add policies with patterns _, err := e.E.AddPolicies([][]string{ {"server:owner", knot, knot, "server:invite"}, {"server:member", knot, knot, "repo:create"}, }) if err != nil { return err }
// all owners are also members _, err = e.E.AddGroupingPolicy("server:owner", "server:member", knot) return err}
func (e *Enforcer) AddSpindle(spindle string) error { // the internal repr for spindles is spindle:foo.com spindle = intoSpindle(spindle)
_, err := e.E.AddPolicies([][]string{ {"server:owner", spindle, spindle, "server:invite"}, }) if err != nil { return err }
// all owners are also members _, err = e.E.AddGroupingPolicy("server:owner", "server:member", spindle) return err}
func (e *Enforcer) RemoveSpindle(spindle string) error { spindle = intoSpindle(spindle) _, err := e.E.DeleteDomains(spindle) return err}
func (e *Enforcer) RemoveKnot(knot string) error { _, err := e.E.DeleteDomains(knot) return err}
func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) { keepFunc := isNotSpindle stripFunc := unSpindle return e.getDomainsForUser(did, keepFunc, stripFunc)}
func (e *Enforcer) GetSpindlesForUser(did string) ([]string, error) { keepFunc := isSpindle stripFunc := unSpindle return e.getDomainsForUser(did, keepFunc, stripFunc)}
func (e *Enforcer) AddKnotOwner(domain, owner string) error { return e.addOwner(domain, owner)}
func (e *Enforcer) RemoveKnotOwner(domain, owner string) error { return e.removeOwner(domain, owner)}
func (e *Enforcer) AddKnotMember(domain, member string) error { _, err := e.addMember(domain, member) return err}
func (e *Enforcer) RemoveKnotMember(domain, member string) error { _, err := e.removeMember(domain, member) return err}
func (e *Enforcer) TryAddKnotMember(domain, member string) (bool, error) { return e.addMember(domain, member)}
func (e *Enforcer) TryRemoveKnotMember(domain, member string) (bool, error) { return e.removeMember(domain, member)}
func (e *Enforcer) AddSpindleOwner(domain, owner string) error { return e.addOwner(intoSpindle(domain), owner)}
func (e *Enforcer) RemoveSpindleOwner(domain, owner string) error { return e.removeOwner(intoSpindle(domain), owner)}
func (e *Enforcer) AddSpindleMember(domain, member string) error { _, err := e.addMember(intoSpindle(domain), member) return err}
func (e *Enforcer) RemoveSpindleMember(domain, member string) error { _, err := e.removeMember(intoSpindle(domain), member) return err}
func (e *Enforcer) TryAddSpindleMember(domain, member string) (bool, error) { return e.addMember(intoSpindle(domain), member)}
func (e *Enforcer) TryRemoveSpindleMember(domain, member string) (bool, error) { return e.removeMember(intoSpindle(domain), member)}
func repoPolicies(member, domain, repo string) [][]string { return [][]string{ {member, domain, repo, "repo:settings"}, {member, domain, repo, "repo:push"}, {member, domain, repo, "repo:owner"}, {member, domain, repo, "repo:invite"}, {member, domain, repo, "repo:delete"}, {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo }}func (e *Enforcer) AddRepo(member, domain, repo string) error { err := checkRepoFormat(repo) if err != nil { return err }
_, err = e.E.AddPolicies(repoPolicies(member, domain, repo)) return err}func (e *Enforcer) RemoveRepo(member, domain, repo string) error { err := checkRepoFormat(repo) if err != nil { return err }
_, err = e.E.RemovePolicies(repoPolicies(member, domain, repo)) return err}
var ( collaboratorPolicies = func(collaborator, domain, repo string) [][]string { return [][]string{ {collaborator, domain, repo, "repo:collaborator"}, {collaborator, domain, repo, "repo:settings"}, {collaborator, domain, repo, "repo:push"}, } })
func (e *Enforcer) AddCollaborator(collaborator, domain, repo string) error { err := checkRepoFormat(repo) if err != nil { return err }
_, err = e.E.AddPolicies(collaboratorPolicies(collaborator, domain, repo)) return err}
func (e *Enforcer) RemoveCollaborator(collaborator, domain, repo string) error { err := checkRepoFormat(repo) if err != nil { return err }
_, err = e.E.RemovePolicies(collaboratorPolicies(collaborator, domain, repo)) return err}
func (e *Enforcer) WipeRepoPolicies(domain, repo string) error { if err := checkRepoFormat(repo); err != nil { return err } _, err := e.E.RemoveFilteredPolicy(1, domain, repo) return err}
func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) { var membersWithoutRoles []string
// this includes roles too, casbin does not differentiate. // the filtering criteria is to remove strings not starting with `did:` members, err := e.E.GetImplicitUsersForRole(role, domain) for _, m := range members { if strings.HasPrefix(m, "did:") { membersWithoutRoles = append(membersWithoutRoles, m) } } if err != nil { return nil, err }
slices.Sort(membersWithoutRoles) return slices.Compact(membersWithoutRoles), nil}
func (e *Enforcer) GetKnotUsersByRole(role, domain string) ([]string, error) { return e.GetUserByRole(role, domain)}
func (e *Enforcer) GetSpindleUsersByRole(role, domain string) ([]string, error) { return e.GetUserByRole(role, intoSpindle(domain))}
func (e *Enforcer) GetCollaboratorsByRepo(domain string) (map[string][]string, error) { policies, err := e.E.GetFilteredNamedPolicy("p", 3, "repo:collaborator") if err != nil { return nil, err }
byRepo := make(map[string][]string) for _, p := range policies { subject, dom, repo := p[0], p[1], p[2] if dom != domain || !strings.HasPrefix(subject, "did:") { continue } byRepo[repo] = append(byRepo[repo], subject) }
for repo, users := range byRepo { slices.Sort(users) byRepo[repo] = slices.Compact(users) }
return byRepo, nil}
func (e *Enforcer) IsKnotOwner(user, domain string) (bool, error) { return e.isRole(user, "server:owner", domain)}
func (e *Enforcer) IsKnotMember(user, domain string) (bool, error) { return e.isRole(user, "server:member", domain)}
func (e *Enforcer) IsSpindleOwner(user, domain string) (bool, error) { return e.isRole(user, "server:owner", intoSpindle(domain))}
func (e *Enforcer) IsSpindleMember(user, domain string) (bool, error) { return e.isRole(user, "server:member", intoSpindle(domain))}
func (e *Enforcer) IsKnotInviteAllowed(user, domain string) (bool, error) { return e.isInviteAllowed(user, domain)}
func (e *Enforcer) IsSpindleInviteAllowed(user, domain string) (bool, error) { return e.isInviteAllowed(user, intoSpindle(domain))}
func (e *Enforcer) IsRepoCreateAllowed(user, domain string) (bool, error) { return e.E.Enforce(user, domain, domain, "repo:create")}
func (e *Enforcer) IsRepoDeleteAllowed(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:delete")}
func (e *Enforcer) IsRepoOwner(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:owner")}
func (e *Enforcer) IsRepoCollaborator(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:collaborator")}
func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:push")}
func (e *Enforcer) IsSettingsAllowed(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:settings")}
func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) { return e.E.Enforce(user, domain, repo, "repo:invite")}
// given a repo, what permissions does this user have? repo:owner? repo:invite? etc.func (e *Enforcer) GetPermissionsInRepo(user, domain, repo string) []string { var permissions []string res := e.E.GetPermissionsForUserInDomain(user, domain) for _, p := range res { // get only permissions for this resource/repo if p[2] == repo { permissions = append(permissions, p[3]) } }
return permissions}