forked from tangled.org/core
this repo has no description

rbac: add spindle config to rbac

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

oppi.li dc76a404 b2147be9

verified
Changed files
+174 -43
appview
oauth
handler
repo
state
knotclient
knotserver
rbac
+1 -1
appview/oauth/handler/handler.go
···
defaultKnot := "knot1.tangled.sh"
log.Printf("adding %s to default knot", did)
-
err := o.enforcer.AddMember(defaultKnot, did)
+
err := o.enforcer.AddKnotMember(defaultKnot, did)
if err != nil {
log.Println("failed to add user to knot1.tangled.sh: ", err)
return
+1 -1
appview/repo/repo.go
···
switch r.Method {
case http.MethodGet:
user := rp.oauth.GetUser(r)
-
knots, err := rp.enforcer.GetDomainsForUser(user.Did)
+
knots, err := rp.enforcer.GetKnotsForUser(user.Did)
if err != nil {
rp.pages.Notice(w, "repo", "Invalid user account.")
return
+1 -1
appview/state/knotstream.go
···
return err
}
-
knownKnots, err := enforcer.GetDomainsForUser(record.CommitterDid)
+
knownKnots, err := enforcer.GetKnotsForUser(record.CommitterDid)
if err != nil {
return err
}
+5 -5
appview/state/state.go
···
}
// add basic acls for this domain
-
err = s.enforcer.AddDomain(domain)
+
err = s.enforcer.AddKnot(domain)
if err != nil {
log.Println("failed to setup owner of domain", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
}
// add this did as owner of this domain
-
err = s.enforcer.AddOwner(domain, reg.ByDid)
+
err = s.enforcer.AddKnotOwner(domain, reg.ByDid)
if err != nil {
log.Println("failed to setup owner of domain", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
}
}
-
ok, err := s.enforcer.IsServerOwner(user.Did, domain)
+
ok, err := s.enforcer.IsKnotOwner(user.Did, domain)
isOwner := err == nil && ok
p := pages.KnotParams{
···
return
}
-
err = s.enforcer.AddMember(domain, subjectIdentity.DID.String())
+
err = s.enforcer.AddKnotMember(domain, subjectIdentity.DID.String())
if err != nil {
w.Write([]byte(fmt.Sprint("failed to add member: ", err)))
return
···
switch r.Method {
case http.MethodGet:
user := s.oauth.GetUser(r)
-
knots, err := s.enforcer.GetDomainsForUser(user.Did)
+
knots, err := s.enforcer.GetKnotsForUser(user.Did)
if err != nil {
s.pages.Notice(w, "repo", "Invalid user account.")
return
-4
knotclient/events.go
···
}
}
-
func (cc *ConsumerConfig) AddEventSource(es EventSource) {
-
cc.Sources[es] = struct{}{}
-
}
-
type EventSource struct {
Knot string
}
+1 -1
knotserver/handler.go
···
init: make(chan struct{}),
}
-
err := e.AddDomain(ThisServer)
+
err := e.AddKnot(ThisServer)
if err != nil {
return nil, fmt.Errorf("failed to setup enforcer: %w", err)
}
+1 -1
knotserver/jetstream.go
···
return fmt.Errorf("failed to enforce permissions: %w", err)
}
-
if err := h.e.AddMember(ThisServer, record.Subject); err != nil {
+
if err := h.e.AddKnotMember(ThisServer, record.Subject); err != nil {
l.Error("failed to add member", "error", err)
return fmt.Errorf("failed to add member: %w", err)
}
+2 -2
knotserver/routes.go
···
h.jc.AddDid(did)
-
if err := h.e.AddMember(ThisServer, did); err != nil {
+
if err := h.e.AddKnotMember(ThisServer, did); err != nil {
l.Error("adding member", "error", err.Error())
writeError(w, err.Error(), http.StatusInternalServerError)
return
···
h.jc.AddDid(data.Did)
-
if err := h.e.AddOwner(ThisServer, data.Did); err != nil {
+
if err := h.e.AddKnotOwner(ThisServer, data.Did); err != nil {
l.Error("adding owner", "error", err.Error())
writeError(w, err.Error(), http.StatusInternalServerError)
return
+79 -27
rbac/rbac.go
···
import (
"database/sql"
-
"fmt"
+
"slices"
"strings"
adapter "github.com/Blank-Xu/sql-adapter"
···
return &Enforcer{e}, nil
}
-
func (e *Enforcer) AddDomain(domain string) error {
+
func (e *Enforcer) AddKnot(knot string) error {
// Add policies with patterns
_, err := e.E.AddPolicies([][]string{
-
{"server:owner", domain, domain, "server:invite"},
-
{"server:member", domain, domain, "repo:create"},
+
{"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", domain)
+
_, err = e.E.AddGroupingPolicy("server:owner", "server:member", spindle)
return err
}
-
func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) {
-
return e.E.GetDomainsForUser(did)
+
func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) {
+
keepFunc := isNotSpindle
+
stripFunc := unSpindle
+
return e.getDomainsForUser(did, keepFunc, stripFunc)
}
-
func (e *Enforcer) AddOwner(domain, owner string) error {
-
_, err := e.E.AddGroupingPolicy(owner, "server:owner", domain)
-
return err
+
func (e *Enforcer) GetSpindlesForUser(did string) ([]string, error) {
+
keepFunc := isSpindle
+
stripFunc := unSpindle
+
return e.getDomainsForUser(did, keepFunc, stripFunc)
}
-
func (e *Enforcer) AddMember(domain, member string) error {
-
_, err := e.E.AddGroupingPolicy(member, "server:member", domain)
-
return err
+
func (e *Enforcer) AddKnotOwner(domain, owner string) error {
+
return e.addOwner(domain, owner)
+
}
+
+
func (e *Enforcer) AddKnotMember(domain, member string) error {
+
return e.addMember(domain, member)
+
}
+
+
func (e *Enforcer) AddSpindleOwner(domain, owner string) error {
+
return e.addOwner(intoSpindle(domain), owner)
+
}
+
+
func (e *Enforcer) AddSpindleMember(domain, member string) error {
+
return e.addMember(intoSpindle(domain), member)
}
func repoPolicies(member, domain, repo string) [][]string {
···
return nil, err
}
-
return membersWithoutRoles, nil
+
slices.Sort(membersWithoutRoles)
+
return slices.Compact(membersWithoutRoles), nil
}
-
func (e *Enforcer) isRole(user, role, domain string) (bool, error) {
-
return e.E.HasGroupingPolicy(user, role, domain)
+
func (e *Enforcer) GetUserByRoleInRepo(role, domain, repo string) ([]string, error) {
+
var users []string
+
+
policies, err := e.E.GetImplicitUsersForResourceByDomain(repo, domain)
+
for _, p := range policies {
+
user := p[0]
+
if strings.HasPrefix(user, "did:") {
+
users = append(users, user)
+
}
+
}
+
if err != nil {
+
return nil, err
+
}
+
+
slices.Sort(users)
+
return slices.Compact(users), nil
}
-
func (e *Enforcer) IsServerOwner(user, domain string) (bool, error) {
+
func (e *Enforcer) IsKnotOwner(user, domain string) (bool, error) {
return e.isRole(user, "server:owner", domain)
}
-
func (e *Enforcer) IsServerMember(user, domain string) (bool, error) {
+
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) IsPushAllowed(user, domain, repo string) (bool, error) {
return e.E.Enforce(user, domain, repo, "repo:push")
}
···
return permissions
}
-
-
func checkRepoFormat(repo string) error {
-
// sanity check, repo must be of the form ownerDid/repo
-
if parts := strings.SplitN(repo, "/", 2); !strings.HasPrefix(parts[0], "did:") {
-
return fmt.Errorf("invalid repo: %s", repo)
-
}
-
-
return nil
-
}
+83
rbac/util.go
···
+
package rbac
+
+
import (
+
"fmt"
+
"slices"
+
"strings"
+
)
+
+
func (e *Enforcer) getDomainsForUser(did string, keepFunc func(string) bool, stripFunc func(string) string) ([]string, error) {
+
domains, err := e.E.GetDomainsForUser(did)
+
if err != nil {
+
return nil, err
+
}
+
+
n := 0
+
for _, x := range domains {
+
if keepFunc(x) {
+
domains[n] = stripFunc(x)
+
n++
+
}
+
}
+
domains = domains[:n]
+
+
return domains, nil
+
}
+
+
func (e *Enforcer) addOwner(domain, owner string) error {
+
_, err := e.E.AddGroupingPolicy(owner, "server:owner", domain)
+
return err
+
}
+
+
func (e *Enforcer) addMember(domain, member string) error {
+
_, err := e.E.AddGroupingPolicy(member, "server:member", domain)
+
return err
+
}
+
+
func (e *Enforcer) isRole(user, role, domain string) (bool, error) {
+
roles, err := e.E.GetImplicitRolesForUser(user, domain)
+
if err != nil {
+
return false, err
+
}
+
if slices.Contains(roles, role) {
+
return true, nil
+
}
+
return false, nil
+
}
+
+
func (e *Enforcer) isInviteAllowed(user, domain string) (bool, error) {
+
return e.E.Enforce(user, domain, domain, "server:invite")
+
}
+
+
func checkRepoFormat(repo string) error {
+
// sanity check, repo must be of the form ownerDid/repo
+
if parts := strings.SplitN(repo, "/", 2); !strings.HasPrefix(parts[0], "did:") {
+
return fmt.Errorf("invalid repo: %s", repo)
+
}
+
+
return nil
+
}
+
+
const spindlePrefix = "spindle:"
+
+
func intoSpindle(domain string) string {
+
if !isSpindle(domain) {
+
return spindlePrefix + domain
+
}
+
return domain
+
}
+
+
func unSpindle(domain string) string {
+
if !isSpindle(domain) {
+
return domain
+
}
+
return strings.TrimPrefix(domain, spindlePrefix)
+
}
+
+
func isSpindle(domain string) bool {
+
return strings.HasPrefix(domain, spindlePrefix)
+
}
+
+
func isNotSpindle(domain string) bool {
+
return !isSpindle(domain)
+
}