Monorepo for Tangled tangled.org
6

Configure Feed

Select the types of activity you want to include in your feed.

1package knotacl 2 3import ( 4 "context" 5 "fmt" 6 "slices" 7 8 "tangled.org/core/appview/db" 9 "tangled.org/core/appview/models" 10 "tangled.org/core/appview/pages" 11 "tangled.org/core/orm" 12 "tangled.org/core/rbac" 13) 14 15type reader interface { 16 repoPerms(ctx context.Context, repo *models.Repo, userDid string) ([]string, error) 17 knotMembers(ctx context.Context, host string) []string 18 collaborators(ctx context.Context, repo *models.Repo) []pages.Collaborator 19 isRepoCreateAllowed(ctx context.Context, host, userDid string) bool 20 isKnotMember(ctx context.Context, host, userDid string) bool 21} 22 23type legacyReader struct { 24 enforcer *rbac.Enforcer 25} 26 27func (r *legacyReader) repoPerms(ctx context.Context, repo *models.Repo, userDid string) ([]string, error) { 28 return r.enforcer.GetPermissionsInRepo(userDid, repo.Knot, repo.RepoIdentifier()), nil 29} 30 31func (r *legacyReader) knotMembers(ctx context.Context, host string) []string { 32 members, err := r.enforcer.GetUserByRole("server:member", host) 33 if err != nil { 34 return nil 35 } 36 return members 37} 38 39func (r *legacyReader) collaborators(ctx context.Context, repo *models.Repo) []pages.Collaborator { 40 policies, err := r.enforcer.E.GetImplicitUsersForResourceByDomain(repo.RepoIdentifier(), repo.Knot) 41 if err != nil { 42 return nil 43 } 44 return filterMap(policies, func(p []string) (pages.Collaborator, bool) { 45 // currently only two roles: owner and member 46 switch p[3] { 47 case "repo:owner": 48 return pages.Collaborator{Did: p[0], Role: "owner"}, true 49 case "repo:collaborator": 50 return pages.Collaborator{Did: p[0], Role: "collaborator"}, true 51 default: 52 return pages.Collaborator{}, false 53 } 54 }) 55} 56 57func (r *legacyReader) isRepoCreateAllowed(ctx context.Context, host, userDid string) bool { 58 ok, err := r.enforcer.IsRepoCreateAllowed(userDid, host) 59 return err == nil && ok 60} 61 62func (r *legacyReader) isKnotMember(ctx context.Context, host, userDid string) bool { 63 knots, err := r.enforcer.GetKnotsForUser(userDid) 64 return err == nil && slices.Contains(knots, host) 65} 66 67type nativeReader struct { 68 client *cache 69 execer db.Execer 70} 71 72func (r *nativeReader) repoPerms(ctx context.Context, repo *models.Repo, userDid string) ([]string, error) { 73 if userDid == repo.Did { 74 return ownerPermissions(), nil 75 } 76 var perms []string 77 if r.isRegisteredOwner(ctx, repo.Knot, userDid) { 78 perms = serverOwnerRepoPermissions() 79 } 80 collabs, err := r.client.GetRepoCollaborators(ctx, repo.Knot, repo.RepoDid) 81 if err != nil { 82 return dedup(perms), fmt.Errorf("%w: %v", ErrKnotUnreachable, err) 83 } 84 if slices.Contains(collabs, userDid) { 85 perms = append(perms, collaboratorPermissions()...) 86 } 87 return dedup(perms), nil 88} 89 90func (r *nativeReader) knotMembers(ctx context.Context, host string) []string { 91 members, err := r.client.GetKnotMembers(ctx, host) 92 if err != nil { 93 return dedup(r.registeredOwners(ctx, host)) 94 } 95 return dedup(append(members, r.registeredOwners(ctx, host)...)) 96} 97 98func (r *nativeReader) collaborators(ctx context.Context, repo *models.Repo) []pages.Collaborator { 99 owner := pages.Collaborator{Did: repo.Did, Role: "owner"} 100 collabs, err := r.client.GetRepoCollaborators(ctx, repo.Knot, repo.RepoDid) 101 if err != nil { 102 return []pages.Collaborator{owner} 103 } 104 rows := filterMap(collabs, func(d string) (pages.Collaborator, bool) { 105 if d == repo.Did { 106 return pages.Collaborator{}, false 107 } 108 return pages.Collaborator{Did: d, Role: "collaborator"}, true 109 }) 110 return append([]pages.Collaborator{owner}, rows...) 111} 112 113func (r *nativeReader) isRepoCreateAllowed(ctx context.Context, host, userDid string) bool { 114 members, err := r.client.GetKnotMembers(ctx, host) 115 if err == nil && slices.Contains(members, userDid) { 116 return true 117 } 118 return r.isRegisteredOwner(ctx, host, userDid) 119} 120 121func (r *nativeReader) isKnotMember(ctx context.Context, host, userDid string) bool { 122 return slices.Contains(r.knotMembers(ctx, host), userDid) 123} 124 125func (r *nativeReader) registeredOwners(ctx context.Context, host string) []string { 126 key := "r\x00" + host 127 if memo := memoFrom(ctx); memo != nil { 128 if v, ok := memo.get(key); ok { 129 return slices.Clone(v) 130 } 131 } 132 regs, err := db.GetRegistrations(r.execer, orm.FilterEq("domain", host)) 133 if err != nil { 134 return nil 135 } 136 owners := filterMap(regs, func(reg models.Registration) (string, bool) { 137 return reg.ByDid, reg.Registered != nil 138 }) 139 if memo := memoFrom(ctx); memo != nil { 140 memo.put(key, slices.Clone(owners)) 141 } 142 return owners 143} 144 145func (r *nativeReader) isRegisteredOwner(ctx context.Context, host, userDid string) bool { 146 return slices.Contains(r.registeredOwners(ctx, host), userDid) 147}