forked from tangled.org/core
Monorepo for Tangled

rbac: add spindle config to rbac

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li fc637d68 5102fa4b

verified
Changed files
+174 -43
appview
oauth
handler
repo
state
knotclient
knotserver
rbac
+1 -1
appview/oauth/handler/handler.go
··· 336 336 defaultKnot := "knot1.tangled.sh" 337 337 338 338 log.Printf("adding %s to default knot", did) 339 - err := o.enforcer.AddMember(defaultKnot, did) 339 + err := o.enforcer.AddKnotMember(defaultKnot, did) 340 340 if err != nil { 341 341 log.Println("failed to add user to knot1.tangled.sh: ", err) 342 342 return
+1 -1
appview/repo/repo.go
··· 1090 1090 switch r.Method { 1091 1091 case http.MethodGet: 1092 1092 user := rp.oauth.GetUser(r) 1093 - knots, err := rp.enforcer.GetDomainsForUser(user.Did) 1093 + knots, err := rp.enforcer.GetKnotsForUser(user.Did) 1094 1094 if err != nil { 1095 1095 rp.pages.Notice(w, "repo", "Invalid user account.") 1096 1096 return
+1 -1
appview/state/knotstream.go
··· 71 71 return err 72 72 } 73 73 74 - knownKnots, err := enforcer.GetDomainsForUser(record.CommitterDid) 74 + knownKnots, err := enforcer.GetKnotsForUser(record.CommitterDid) 75 75 if err != nil { 76 76 return err 77 77 }
+5 -5
appview/state/state.go
··· 336 336 } 337 337 338 338 // add basic acls for this domain 339 - err = s.enforcer.AddDomain(domain) 339 + err = s.enforcer.AddKnot(domain) 340 340 if err != nil { 341 341 log.Println("failed to setup owner of domain", err) 342 342 http.Error(w, err.Error(), http.StatusInternalServerError) ··· 344 344 } 345 345 346 346 // add this did as owner of this domain 347 - err = s.enforcer.AddOwner(domain, reg.ByDid) 347 + err = s.enforcer.AddKnotOwner(domain, reg.ByDid) 348 348 if err != nil { 349 349 log.Println("failed to setup owner of domain", err) 350 350 http.Error(w, err.Error(), http.StatusInternalServerError) ··· 409 409 } 410 410 } 411 411 412 - ok, err := s.enforcer.IsServerOwner(user.Did, domain) 412 + ok, err := s.enforcer.IsKnotOwner(user.Did, domain) 413 413 isOwner := err == nil && ok 414 414 415 415 p := pages.KnotParams{ ··· 528 528 return 529 529 } 530 530 531 - err = s.enforcer.AddMember(domain, subjectIdentity.DID.String()) 531 + err = s.enforcer.AddKnotMember(domain, subjectIdentity.DID.String()) 532 532 if err != nil { 533 533 w.Write([]byte(fmt.Sprint("failed to add member: ", err))) 534 534 return ··· 576 576 switch r.Method { 577 577 case http.MethodGet: 578 578 user := s.oauth.GetUser(r) 579 - knots, err := s.enforcer.GetDomainsForUser(user.Did) 579 + knots, err := s.enforcer.GetKnotsForUser(user.Did) 580 580 if err != nil { 581 581 s.pages.Notice(w, "repo", "Invalid user account.") 582 582 return
-4
knotclient/events.go
··· 44 44 } 45 45 } 46 46 47 - func (cc *ConsumerConfig) AddEventSource(es EventSource) { 48 - cc.Sources[es] = struct{}{} 49 - } 50 - 51 47 type EventSource struct { 52 48 Knot string 53 49 }
+1 -1
knotserver/handler.go
··· 46 46 init: make(chan struct{}), 47 47 } 48 48 49 - err := e.AddDomain(ThisServer) 49 + err := e.AddKnot(ThisServer) 50 50 if err != nil { 51 51 return nil, fmt.Errorf("failed to setup enforcer: %w", err) 52 52 }
+1 -1
knotserver/jetstream.go
··· 43 43 return fmt.Errorf("failed to enforce permissions: %w", err) 44 44 } 45 45 46 - if err := h.e.AddMember(ThisServer, record.Subject); err != nil { 46 + if err := h.e.AddKnotMember(ThisServer, record.Subject); err != nil { 47 47 l.Error("failed to add member", "error", err) 48 48 return fmt.Errorf("failed to add member: %w", err) 49 49 }
+2 -2
knotserver/routes.go
··· 1177 1177 } 1178 1178 h.jc.AddDid(did) 1179 1179 1180 - if err := h.e.AddMember(ThisServer, did); err != nil { 1180 + if err := h.e.AddKnotMember(ThisServer, did); err != nil { 1181 1181 l.Error("adding member", "error", err.Error()) 1182 1182 writeError(w, err.Error(), http.StatusInternalServerError) 1183 1183 return ··· 1312 1312 } 1313 1313 h.jc.AddDid(data.Did) 1314 1314 1315 - if err := h.e.AddOwner(ThisServer, data.Did); err != nil { 1315 + if err := h.e.AddKnotOwner(ThisServer, data.Did); err != nil { 1316 1316 l.Error("adding owner", "error", err.Error()) 1317 1317 writeError(w, err.Error(), http.StatusInternalServerError) 1318 1318 return
+79 -27
rbac/rbac.go
··· 2 2 3 3 import ( 4 4 "database/sql" 5 - "fmt" 5 + "slices" 6 6 "strings" 7 7 8 8 adapter "github.com/Blank-Xu/sql-adapter" ··· 59 59 return &Enforcer{e}, nil 60 60 } 61 61 62 - func (e *Enforcer) AddDomain(domain string) error { 62 + func (e *Enforcer) AddKnot(knot string) error { 63 63 // Add policies with patterns 64 64 _, err := e.E.AddPolicies([][]string{ 65 - {"server:owner", domain, domain, "server:invite"}, 66 - {"server:member", domain, domain, "repo:create"}, 65 + {"server:owner", knot, knot, "server:invite"}, 66 + {"server:member", knot, knot, "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", knot) 74 + return err 75 + } 76 + 77 + func (e *Enforcer) AddSpindle(spindle string) error { 78 + // the internal repr for spindles is spindle:foo.com 79 + spindle = intoSpindle(spindle) 80 + 81 + _, err := e.E.AddPolicies([][]string{ 82 + {"server:owner", spindle, spindle, "server:invite"}, 67 83 }) 68 84 if err != nil { 69 85 return err 70 86 } 71 87 72 88 // all owners are also members 73 - _, err = e.E.AddGroupingPolicy("server:owner", "server:member", domain) 89 + _, err = e.E.AddGroupingPolicy("server:owner", "server:member", spindle) 74 90 return err 75 91 } 76 92 77 - func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) { 78 - return e.E.GetDomainsForUser(did) 93 + func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) { 94 + keepFunc := isNotSpindle 95 + stripFunc := unSpindle 96 + return e.getDomainsForUser(did, keepFunc, stripFunc) 79 97 } 80 98 81 - func (e *Enforcer) AddOwner(domain, owner string) error { 82 - _, err := e.E.AddGroupingPolicy(owner, "server:owner", domain) 83 - return err 99 + func (e *Enforcer) GetSpindlesForUser(did string) ([]string, error) { 100 + keepFunc := isSpindle 101 + stripFunc := unSpindle 102 + return e.getDomainsForUser(did, keepFunc, stripFunc) 84 103 } 85 104 86 - func (e *Enforcer) AddMember(domain, member string) error { 87 - _, err := e.E.AddGroupingPolicy(member, "server:member", domain) 88 - return err 105 + func (e *Enforcer) AddKnotOwner(domain, owner string) error { 106 + return e.addOwner(domain, owner) 107 + } 108 + 109 + func (e *Enforcer) AddKnotMember(domain, member string) error { 110 + return e.addMember(domain, member) 111 + } 112 + 113 + func (e *Enforcer) AddSpindleOwner(domain, owner string) error { 114 + return e.addOwner(intoSpindle(domain), owner) 115 + } 116 + 117 + func (e *Enforcer) AddSpindleMember(domain, member string) error { 118 + return e.addMember(intoSpindle(domain), member) 89 119 } 90 120 91 121 func repoPolicies(member, domain, repo string) [][]string { ··· 162 192 return nil, err 163 193 } 164 194 165 - return membersWithoutRoles, nil 195 + slices.Sort(membersWithoutRoles) 196 + return slices.Compact(membersWithoutRoles), nil 166 197 } 167 198 168 - func (e *Enforcer) isRole(user, role, domain string) (bool, error) { 169 - return e.E.HasGroupingPolicy(user, role, domain) 199 + func (e *Enforcer) GetUserByRoleInRepo(role, domain, repo string) ([]string, error) { 200 + var users []string 201 + 202 + policies, err := e.E.GetImplicitUsersForResourceByDomain(repo, domain) 203 + for _, p := range policies { 204 + user := p[0] 205 + if strings.HasPrefix(user, "did:") { 206 + users = append(users, user) 207 + } 208 + } 209 + if err != nil { 210 + return nil, err 211 + } 212 + 213 + slices.Sort(users) 214 + return slices.Compact(users), nil 170 215 } 171 216 172 - func (e *Enforcer) IsServerOwner(user, domain string) (bool, error) { 217 + func (e *Enforcer) IsKnotOwner(user, domain string) (bool, error) { 173 218 return e.isRole(user, "server:owner", domain) 174 219 } 175 220 176 - func (e *Enforcer) IsServerMember(user, domain string) (bool, error) { 221 + func (e *Enforcer) IsKnotMember(user, domain string) (bool, error) { 177 222 return e.isRole(user, "server:member", domain) 178 223 } 179 224 225 + func (e *Enforcer) IsSpindleOwner(user, domain string) (bool, error) { 226 + return e.isRole(user, "server:owner", intoSpindle(domain)) 227 + } 228 + 229 + func (e *Enforcer) IsSpindleMember(user, domain string) (bool, error) { 230 + return e.isRole(user, "server:member", intoSpindle(domain)) 231 + } 232 + 233 + func (e *Enforcer) IsKnotInviteAllowed(user, domain string) (bool, error) { 234 + return e.isInviteAllowed(user, domain) 235 + } 236 + 237 + func (e *Enforcer) IsSpindleInviteAllowed(user, domain string) (bool, error) { 238 + return e.isInviteAllowed(user, intoSpindle(domain)) 239 + } 240 + 180 241 func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) { 181 242 return e.E.Enforce(user, domain, repo, "repo:push") 182 243 } ··· 202 263 203 264 return permissions 204 265 } 205 - 206 - func 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 - }
+83
rbac/util.go
··· 1 + package rbac 2 + 3 + import ( 4 + "fmt" 5 + "slices" 6 + "strings" 7 + ) 8 + 9 + func (e *Enforcer) getDomainsForUser(did string, keepFunc func(string) bool, stripFunc func(string) string) ([]string, error) { 10 + domains, err := e.E.GetDomainsForUser(did) 11 + if err != nil { 12 + return nil, err 13 + } 14 + 15 + n := 0 16 + for _, x := range domains { 17 + if keepFunc(x) { 18 + domains[n] = stripFunc(x) 19 + n++ 20 + } 21 + } 22 + domains = domains[:n] 23 + 24 + return domains, nil 25 + } 26 + 27 + func (e *Enforcer) addOwner(domain, owner string) error { 28 + _, err := e.E.AddGroupingPolicy(owner, "server:owner", domain) 29 + return err 30 + } 31 + 32 + func (e *Enforcer) addMember(domain, member string) error { 33 + _, err := e.E.AddGroupingPolicy(member, "server:member", domain) 34 + return err 35 + } 36 + 37 + func (e *Enforcer) isRole(user, role, domain string) (bool, error) { 38 + roles, err := e.E.GetImplicitRolesForUser(user, domain) 39 + if err != nil { 40 + return false, err 41 + } 42 + if slices.Contains(roles, role) { 43 + return true, nil 44 + } 45 + return false, nil 46 + } 47 + 48 + func (e *Enforcer) isInviteAllowed(user, domain string) (bool, error) { 49 + return e.E.Enforce(user, domain, domain, "server:invite") 50 + } 51 + 52 + func checkRepoFormat(repo string) error { 53 + // sanity check, repo must be of the form ownerDid/repo 54 + if parts := strings.SplitN(repo, "/", 2); !strings.HasPrefix(parts[0], "did:") { 55 + return fmt.Errorf("invalid repo: %s", repo) 56 + } 57 + 58 + return nil 59 + } 60 + 61 + const spindlePrefix = "spindle:" 62 + 63 + func intoSpindle(domain string) string { 64 + if !isSpindle(domain) { 65 + return spindlePrefix + domain 66 + } 67 + return domain 68 + } 69 + 70 + func unSpindle(domain string) string { 71 + if !isSpindle(domain) { 72 + return domain 73 + } 74 + return strings.TrimPrefix(domain, spindlePrefix) 75 + } 76 + 77 + func isSpindle(domain string) bool { 78 + return strings.HasPrefix(domain, spindlePrefix) 79 + } 80 + 81 + func isNotSpindle(domain string) bool { 82 + return !isSpindle(domain) 83 + }