forked from tangled.org/core
this repo has no description
1package rbac 2 3import ( 4 "database/sql" 5 "fmt" 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 Model = ` 15[request_definition] 16r = sub, dom, obj, act 17 18[policy_definition] 19p = sub, dom, obj, act 20 21[role_definition] 22g = _, _, _ 23 24[policy_effect] 25e = some(where (p.eft == allow)) 26 27[matchers] 28m = r.act == p.act && r.dom == p.dom && r.obj == p.obj && g(r.sub, p.sub, r.dom) 29` 30) 31 32type Enforcer struct { 33 E *casbin.Enforcer 34} 35 36func NewEnforcer(path string) (*Enforcer, error) { 37 m, err := model.NewModelFromString(Model) 38 if err != nil { 39 return nil, err 40 } 41 42 db, err := sql.Open("sqlite3", path) 43 if err != nil { 44 return nil, err 45 } 46 47 a, err := adapter.NewAdapter(db, "sqlite3", "acl") 48 if err != nil { 49 return nil, err 50 } 51 52 e, err := casbin.NewEnforcer(m, a) 53 if err != nil { 54 return nil, err 55 } 56 57 e.EnableAutoSave(false) 58 59 return &Enforcer{e}, nil 60} 61 62func (e *Enforcer) AddDomain(domain string) error { 63 // Add policies with patterns 64 _, err := e.E.AddPolicies([][]string{ 65 {"server:owner", domain, domain, "server:invite"}, 66 {"server:member", domain, domain, "repo:create"}, 67 }) 68 if err != nil { 69 return err 70 } 71 72 // all owners are also members 73 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", domain) 74 return err 75} 76 77func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) { 78 return e.E.GetDomainsForUser(did) 79} 80 81func (e *Enforcer) AddOwner(domain, owner string) error { 82 _, err := e.E.AddGroupingPolicy(owner, "server:owner", domain) 83 return err 84} 85 86func (e *Enforcer) AddMember(domain, member string) error { 87 _, err := e.E.AddGroupingPolicy(member, "server:member", domain) 88 return err 89} 90 91func repoPolicies(member, domain, repo string) [][]string { 92 return [][]string{ 93 {member, domain, repo, "repo:settings"}, 94 {member, domain, repo, "repo:push"}, 95 {member, domain, repo, "repo:owner"}, 96 {member, domain, repo, "repo:invite"}, 97 {member, domain, repo, "repo:delete"}, 98 {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo 99 } 100} 101func (e *Enforcer) AddRepo(member, domain, repo string) error { 102 err := checkRepoFormat(repo) 103 if err != nil { 104 return err 105 } 106 107 _, err = e.E.AddPolicies(repoPolicies(member, domain, repo)) 108 return err 109} 110func (e *Enforcer) RemoveRepo(member, domain, repo string) error { 111 err := checkRepoFormat(repo) 112 if err != nil { 113 return err 114 } 115 116 _, err = e.E.RemovePolicies(repoPolicies(member, domain, repo)) 117 return err 118} 119 120var ( 121 collaboratorPolicies = func(collaborator, domain, repo string) [][]string { 122 return [][]string{ 123 {collaborator, domain, repo, "repo:collaborator"}, 124 {collaborator, domain, repo, "repo:settings"}, 125 {collaborator, domain, repo, "repo:push"}, 126 } 127 } 128) 129 130func (e *Enforcer) AddCollaborator(collaborator, domain, repo string) error { 131 err := checkRepoFormat(repo) 132 if err != nil { 133 return err 134 } 135 136 _, err = e.E.AddPolicies(collaboratorPolicies(collaborator, domain, repo)) 137 return err 138} 139 140func (e *Enforcer) RemoveCollaborator(collaborator, domain, repo string) error { 141 err := checkRepoFormat(repo) 142 if err != nil { 143 return err 144 } 145 146 _, err = e.E.RemovePolicies(collaboratorPolicies(collaborator, domain, repo)) 147 return err 148} 149 150func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) { 151 var membersWithoutRoles []string 152 153 // this includes roles too, casbin does not differentiate. 154 // the filtering criteria is to remove strings not starting with `did:` 155 members, err := e.E.GetImplicitUsersForRole(role, domain) 156 for _, m := range members { 157 if strings.HasPrefix(m, "did:") { 158 membersWithoutRoles = append(membersWithoutRoles, m) 159 } 160 } 161 if err != nil { 162 return nil, err 163 } 164 165 return membersWithoutRoles, nil 166} 167 168func (e *Enforcer) isRole(user, role, domain string) (bool, error) { 169 return e.E.HasGroupingPolicy(user, role, domain) 170} 171 172func (e *Enforcer) IsServerOwner(user, domain string) (bool, error) { 173 return e.isRole(user, "server:owner", domain) 174} 175 176func (e *Enforcer) IsServerMember(user, domain string) (bool, error) { 177 return e.isRole(user, "server:member", domain) 178} 179 180func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) { 181 return e.E.Enforce(user, domain, repo, "repo:push") 182} 183 184func (e *Enforcer) IsSettingsAllowed(user, domain, repo string) (bool, error) { 185 return e.E.Enforce(user, domain, repo, "repo:settings") 186} 187 188func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) { 189 return e.E.Enforce(user, domain, repo, "repo:invite") 190} 191 192// given a repo, what permissions does this user have? repo:owner? repo:invite? etc. 193func (e *Enforcer) GetPermissionsInRepo(user, domain, repo string) []string { 194 var permissions []string 195 res := e.E.GetPermissionsForUserInDomain(user, domain) 196 for _, p := range res { 197 // get only permissions for this resource/repo 198 if p[2] == repo { 199 permissions = append(permissions, p[3]) 200 } 201 } 202 203 return permissions 204} 205 206func checkRepoFormat(repo string) error { 207 // sanity check, repo must be of the form ownerDid/repo 208 if parts := strings.SplitN(repo, "/", 2); !strings.HasPrefix(parts[0], "did:") { 209 return fmt.Errorf("invalid repo: %s", repo) 210 } 211 212 return nil 213}