-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
list_fast.go
161 lines (134 loc) · 5.21 KB
/
list_fast.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package teams
import (
"fmt"
"sort"
"strings"
"golang.org/x/net/context"
"github.com/keybase/client/go/libkb"
"github.com/keybase/client/go/protocol/keybase1"
)
// This is server trust version of TeamList functionality. It will not
// perform any team loads to verify if server does not lie about our
// membership in returned teams. It will also rely on the server to
// return member counts for each team. All of this makes this version
// much faster and less heavy on the server - even though UIDMapper is
// used in the untrusting functions, a lot of calls were made anyway
// during team loads (e.g. merkle paths).
// See also: teams/list.go
func ListTeamsUnverified(ctx context.Context, g *libkb.GlobalContext, arg keybase1.TeamListUnverifiedArg) (*keybase1.AnnotatedTeamList, error) {
tracer := g.CTimeTracer(ctx, "TeamList.ListTeamsUnverified", true)
defer tracer.Finish()
m := libkb.NewMetaContext(ctx, g)
tracer.Stage("Resolve QueryUID")
var queryUID keybase1.UID
if arg.UserAssertion != "" {
res := g.Resolver.ResolveFullExpression(m, arg.UserAssertion)
if res.GetError() != nil {
return nil, res.GetError()
}
queryUID = res.GetUID()
}
meUID := g.ActiveDevice.UID()
if meUID.IsNil() {
return nil, libkb.LoginRequiredError{}
}
tracer.Stage("Server")
// We have a very simple cache in case we error out on this call. In the
// case of a network error we try to serve old cached data if we have some.
// The cache is updated on successful requests but otherwise unmaintained
// so should only be used if we are trying to return a best-effort result.
cacheKey := listTeamsUnverifiedCacheKey(meUID, queryUID, arg.IncludeImplicitTeams)
teams, err := getTeamsListFromServer(ctx, g, queryUID,
false /* all */, true /* countMembers */, arg.IncludeImplicitTeams, keybase1.NilTeamID())
switch err.(type) {
case nil:
if err = g.GetKVStore().PutObj(cacheKey, nil, teams); err != nil {
m.Debug("| ListTeamsUnverified unable to put cache item: %v", err)
}
case libkb.APINetError:
if found, cerr := g.GetKVStore().GetInto(&teams, cacheKey); cerr != nil || !found {
// Nothing we can do here.
m.Debug("| ListTeamsUnverified unable to get cache item: %v, found: %v", cerr, found)
return nil, err
}
default:
return nil, err
}
res := &keybase1.AnnotatedTeamList{
Teams: nil,
}
if len(teams) == 0 {
return res, nil
}
if arg.UserAssertion == "" {
queryUID = meUID
}
tracer.Stage("LookupQueryUsername")
queryUsername, queryFullName, err := getUsernameAndFullName(context.Background(), g, queryUID)
if err != nil {
return nil, err
}
for _, memberInfo := range teams {
if memberInfo.IsImplicitTeam && !arg.IncludeImplicitTeams {
m.Debug("| ListTeamsUnverified skipping implicit team: server-team:%v server-uid:%v", memberInfo.TeamID, memberInfo.UserID)
continue
}
anMemberInfo := keybase1.AnnotatedMemberInfo{
TeamID: memberInfo.TeamID,
FqName: memberInfo.FqName,
UserID: memberInfo.UserID,
Role: memberInfo.Role,
IsImplicitTeam: memberInfo.IsImplicitTeam,
IsOpenTeam: memberInfo.IsOpenTeam,
Implicit: memberInfo.Implicit,
Username: queryUsername.String(),
FullName: queryFullName,
MemberCount: g.TeamMemberCountCache.GetWithFallback(memberInfo.TeamID, memberInfo.MemberCount),
Status: keybase1.TeamMemberStatus_ACTIVE,
AllowProfilePromote: memberInfo.AllowProfilePromote,
IsMemberShowcased: memberInfo.IsMemberShowcased,
}
res.Teams = append(res.Teams, anMemberInfo)
}
return res, nil
}
func listTeamsUnverifiedCacheKey(meUID, queryUID keybase1.UID, includeImplicitTeams bool) libkb.DbKey {
return libkb.DbKey{
Typ: libkb.DBTeamList,
Key: fmt.Sprintf("%v-%v-%v", meUID, queryUID, includeImplicitTeams),
}
}
func ListSubteamsUnverified(mctx libkb.MetaContext, name keybase1.TeamName) (res keybase1.SubteamListResult, err error) {
tracer := mctx.G().CTimeTracer(mctx.Ctx(), "TeamList.ListSubteamsUnverified", true)
defer tracer.Finish()
meUID := mctx.G().ActiveDevice.UID()
if meUID.IsNil() {
return res, libkb.LoginRequiredError{}
}
emptyUID := keybase1.UID("")
teams, err := getTeamsListFromServer(mctx.Ctx(), mctx.G(), emptyUID,
false /* all */, true /* countMembers */, false /* includeImplicitTeams */, name.RootID())
if err != nil {
return res, libkb.LoginRequiredError{}
}
var entries []keybase1.SubteamListEntry
for _, potentialSubteam := range teams {
if isSubteamByName(name, potentialSubteam.FqName) {
subteamName, err := keybase1.TeamNameFromString(potentialSubteam.FqName)
if err != nil {
return res, err
}
entries = append(entries, keybase1.SubteamListEntry{Name: subteamName, MemberCount: potentialSubteam.MemberCount, TeamID: potentialSubteam.TeamID})
}
}
// Order alphabetically: e.g. [a, a.b, a.b.c, a.b.d, a.e.f, a.e.g]
sort.Slice(entries, func(i, j int) bool {
return entries[i].Name.String() < entries[j].Name.String()
})
res.Entries = entries
return res, nil
}
func isSubteamByName(teamName keybase1.TeamName, potentialSubteamName string) bool {
// e.g. strings.HasPrefix("keybase.private", "keybase.") => true
return strings.HasPrefix(potentialSubteamName, teamName.String()+".")
}