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