Skip to content

Commit

Permalink
Experiment with passive private following in chat (#17470)
Browse files Browse the repository at this point in the history
* pegboard

* feedback

* update
  • Loading branch information
mlsteele committed May 20, 2019
1 parent a92d3a9 commit 2f72370
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 38 deletions.
4 changes: 4 additions & 0 deletions go/chat/globals/globals.go
Expand Up @@ -67,6 +67,10 @@ func (c Contextified) G() *Context {
return c.gc
}

func (c Contextified) MetaContext(ctx context.Context) libkb.MetaContext {
return libkb.NewMetaContext(ctx, c.G().ExternalG())
}

type ChatContextified struct {
gc *ChatContext
}
Expand Down
53 changes: 49 additions & 4 deletions go/chat/localizer.go
Expand Up @@ -654,7 +654,8 @@ func (s *localizerPipeline) getConvSettingsLocal(ctx context.Context, uid gregor
return res, nil
}

func (s *localizerPipeline) getResetUserNames(ctx context.Context, uidMapper libkb.UIDMapper,
// returns an incomplete list in case of error
func (s *localizerPipeline) getResetUsernamesMetadata(ctx context.Context, uidMapper libkb.UIDMapper,
conv chat1.Conversation) (res []string) {
if len(conv.Metadata.ResetList) == 0 {
return res
Expand All @@ -666,15 +667,50 @@ func (s *localizerPipeline) getResetUserNames(ctx context.Context, uidMapper lib
}
rows, err := uidMapper.MapUIDsToUsernamePackages(ctx, s.G(), kuids, 0, 0, false)
if err != nil {
s.Debug(ctx, "getResetUserNames: failed to run uid mapper: %s", err)
s.Debug(ctx, "getResetUsernamesMetadata: failed to run uid mapper: %s", err)
return res
}
for _, row := range rows {
res = append(res, row.NormalizedUsername.String())
}

return res
}

// returns an incomplete list in case of error
func (s *localizerPipeline) getResetUsernamesPegboard(ctx context.Context, uidMapper libkb.UIDMapper,
teamIDasTLFID chat1.TLFID, public bool) (res []string, err error) {
// NOTE: If this is too slow, it could be cached on local metadata.
teamID, err := keybase1.TeamIDFromString(teamIDasTLFID.String())
if err != nil {
return nil, err
}
team, err := teams.Load(ctx, s.G().ExternalG(), keybase1.LoadTeamArg{
ID: teamID,
Public: public,
})
if err != nil {
return nil, err
}
var resetUIDs []keybase1.UID
for _, uv := range team.AllUserVersions(ctx) {
err = s.G().Pegboard.CheckUV(s.MetaContext(ctx), uv)
if err != nil {
// Turn peg failures into reset usernames.
s.Debug(ctx, "pegboard rejected %v: %v", uv, err)
resetUIDs = append(resetUIDs, uv.Uid)
}
}
rows, err := uidMapper.MapUIDsToUsernamePackages(ctx, s.G(), resetUIDs, 0, 0, false)
if err != nil {
return nil, err
}
for _, row := range rows {
res = append(res, row.NormalizedUsername.String())
}
return res, nil
}

func (s *localizerPipeline) localizeConversation(ctx context.Context, uid gregor1.UID,
conversationRemote chat1.Conversation) (conversationLocal chat1.ConversationLocal) {
var err error
Expand Down Expand Up @@ -908,7 +944,7 @@ func (s *localizerPipeline) localizeConversation(ctx context.Context, uid gregor
for _, uid := range conversationRemote.Metadata.AllList {
kuids = append(kuids, keybase1.UID(uid.String()))
}
conversationLocal.Info.ResetNames = s.getResetUserNames(ctx, umapper, conversationRemote)
conversationLocal.Info.ResetNames = s.getResetUsernamesMetadata(ctx, umapper, conversationRemote)
rows, err := umapper.MapUIDsToUsernamePackages(ctx, s.G(), kuids, time.Hour*24,
10*time.Second, true)
if err != nil {
Expand All @@ -924,7 +960,16 @@ func (s *localizerPipeline) localizeConversation(ctx context.Context, uid gregor
conversationLocal.Info.Participants[j].Username
})
case chat1.ConversationMembersType_IMPTEAMNATIVE, chat1.ConversationMembersType_IMPTEAMUPGRADE:
conversationLocal.Info.ResetNames = s.getResetUserNames(ctx, umapper, conversationRemote)
resetUsernamesPegboard, err := s.getResetUsernamesPegboard(ctx, umapper, info.ID,
conversationLocal.Info.Visibility == keybase1.TLFVisibility_PUBLIC)
if err != nil {
s.Debug(ctx, "getResetUsernamesPegboard error: %v", err)
resetUsernamesPegboard = nil
}
conversationLocal.Info.ResetNames = utils.DedupStringLists(
s.getResetUsernamesMetadata(ctx, umapper, conversationRemote),
resetUsernamesPegboard,
)
fallthrough
case chat1.ConversationMembersType_KBFS:
conversationLocal.Info.Participants, err = utils.ReorderParticipants(
Expand Down
56 changes: 39 additions & 17 deletions go/chat/teams.go
Expand Up @@ -526,41 +526,63 @@ func NewImplicitTeamsNameInfoSource(g *globals.Context, lookupUpgraded bool) *Im
}
}

func (t *ImplicitTeamsNameInfoSource) identify(ctx context.Context, tlfID chat1.TLFID,
impTeamName keybase1.ImplicitTeamDisplayName) (res []keybase1.TLFIdentifyFailure, err error) {
type identifyFailure struct {
Msg string
}

// Identify participants of a conv.
// Returns as if all IDs succeeded if ctx is in TLFIdentifyBehavior_CHAT_GUI mode.
func (t *ImplicitTeamsNameInfoSource) identify(ctx context.Context, team *teams.Team, impTeamName keybase1.ImplicitTeamDisplayName) (err error) {
var names []string
names = append(names, impTeamName.Writers.KeybaseUsers...)
names = append(names, impTeamName.Readers.KeybaseUsers...)

// identify the members in the conversation
identBehavior, _, ok := globals.CtxIdentifyMode(ctx)
defer t.Trace(ctx, func() error { return err }, fmt.Sprintf("identify(%s, %v)", impTeamName.String(), identBehavior))()
if !ok {
return res, errors.New("invalid context with no chat metadata")
return errors.New("invalid context with no chat metadata")
}
cb := make(chan struct{})
go func(ctx context.Context) {
res, err = t.Identify(ctx, names, true,
var idFails []keybase1.TLFIdentifyFailure
idFails, err = t.Identify(ctx, names, true,
func() keybase1.TLFID {
return keybase1.TLFID(tlfID.String())
return keybase1.TLFID(team.ID.String())
},
func() keybase1.CanonicalTlfName {
return keybase1.CanonicalTlfName(impTeamName.String())
})
if err != nil || len(idFails) > 0 {
t.Debug(ctx, "identify failed err=%v fails=%+v", err, idFails)
}
// ignore idFails
close(cb)
}(globals.BackgroundChatCtx(ctx, t.G()))
switch identBehavior {
case keybase1.TLFIdentifyBehavior_CHAT_GUI:
// For GUI mode, let's just let this identify roll in the background. We will be sending up
// For GUI mode, let the full IDs roll in the background. We will be sending up
// tracker breaks to the UI out of band with whatever chat operation has invoked us here.
return nil, nil

// CORE-10522 peg breaks will need to block sending for non-gui mode too.
// Peg breaks could count as track breaks for real Identify.
// That could nicely cover other applications besides chat. But the UI
// for fixing track breaks doesn't quite make sense for peg breaks.

// But check reset-pegs on the critical path.
for _, uv := range team.AllUserVersions(ctx) {
err = t.G().Pegboard.CheckUV(t.MetaContext(ctx), uv)
if err != nil {
// Turn peg failures into identify failures
t.Debug(ctx, "pegboard rejected %v: %v", uv, err)
return fmt.Errorf("A user may have reset: %v", err)
}
}
return nil
default:
<-cb
if err != nil {
return res, err
}
return err
}
return res, nil
}

func (t *ImplicitTeamsNameInfoSource) transformTeamDoesNotExist(ctx context.Context, err error, name string) error {
Expand Down Expand Up @@ -656,7 +678,7 @@ func (t *ImplicitTeamsNameInfoSource) EncryptionKey(ctx context.Context, name st
if err != nil {
return res, ni, err
}
if _, err := t.identify(ctx, teamID, impTeamName); err != nil {
if err := t.identify(ctx, team, impTeamName); err != nil {
return res, ni, err
}
if res, err = getTeamCryptKey(ctx, team, team.Generation(), public, false); err != nil {
Expand All @@ -682,14 +704,14 @@ func (t *ImplicitTeamsNameInfoSource) DecryptionKey(ctx context.Context, name st
if err != nil {
return res, err
}
if _, err = t.identify(ctx, teamID, impTeamName); err != nil {
if err := t.identify(ctx, team, impTeamName); err != nil {
return res, err
}
return getTeamCryptKey(ctx, team, keybase1.PerTeamKeyGeneration(keyGeneration), public,
kbfsEncrypted)
}

func (t *ImplicitTeamsNameInfoSource) ephemeralLoadAndIdentify(ctx context.Context, tlfName string, tlfID chat1.TLFID,
func (t *ImplicitTeamsNameInfoSource) ephemeralLoadAndIdentify(ctx context.Context, encrypting bool, tlfName string, tlfID chat1.TLFID,
membersType chat1.ConversationMembersType, public bool) (teamID keybase1.TeamID, err error) {
if public {
return teamID, NewPublicTeamEphemeralKeyError()
Expand All @@ -702,15 +724,15 @@ func (t *ImplicitTeamsNameInfoSource) ephemeralLoadAndIdentify(ctx context.Conte
if err != nil {
return teamID, err
}
if _, err := t.identify(ctx, tlfID, impTeamName); err != nil {
if err := t.identify(ctx, team, impTeamName); err != nil {
return teamID, err
}
return team.ID, nil
}

func (t *ImplicitTeamsNameInfoSource) EphemeralEncryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
membersType chat1.ConversationMembersType, public bool) (teamEK keybase1.TeamEk, err error) {
teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), tlfName, tlfID, membersType, public)
teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), true, tlfName, tlfID, membersType, public)
if err != nil {
return teamEK, err
}
Expand All @@ -721,7 +743,7 @@ func (t *ImplicitTeamsNameInfoSource) EphemeralEncryptionKey(mctx libkb.MetaCont
func (t *ImplicitTeamsNameInfoSource) EphemeralDecryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
membersType chat1.ConversationMembersType, public bool,
generation keybase1.EkGeneration, contentCtime *gregor1.Time) (teamEK keybase1.TeamEk, err error) {
teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), tlfName, tlfID, membersType, public)
teamID, err := t.ephemeralLoadAndIdentify(mctx.Ctx(), false, tlfName, tlfID, membersType, public)
if err != nil {
return teamEK, err
}
Expand Down
13 changes: 13 additions & 0 deletions go/chat/utils/utils.go
Expand Up @@ -2301,3 +2301,16 @@ func GetVerifiedConv(ctx context.Context, g *globals.Context, uid gregor1.UID,
}
return inbox.Convs[0], nil
}

func DedupStringLists(lists ...[]string) (res []string) {
seen := make(map[string]struct{})
for _, list := range lists {
for _, x := range list {
if _, ok := seen[x]; !ok {
seen[x] = struct{}{}
res = append(res, x)
}
}
}
return res
}
37 changes: 23 additions & 14 deletions go/engine/track_token.go
Expand Up @@ -131,24 +131,33 @@ func (e *TrackToken) Run(m libkb.MetaContext) (err error) {
e.removeLocalTracks(m)
}
}
if err != nil {
return err
}
themUPAK, err := e.them.ExportToUPKV2AllIncarnations()
if err != nil {
return err
}
err = m.G().Pegboard.TrackUPAK(m, themUPAK.Current)
if err != nil {
return err
}

if err == nil {
// Remove this after desktop notification change complete:
m.G().UserChanged(m.Ctx(), e.them.GetUID())
// Remove this after desktop notification change complete:
m.G().UserChanged(m.Ctx(), e.them.GetUID())

// Remove these after desktop notification change complete, but
// add in: m.G().BustLocalUserCache(e.arg.Me.GetUID())
m.G().UserChanged(m.Ctx(), e.arg.Me.GetUID())
// Remove these after desktop notification change complete, but
// add in: m.G().BustLocalUserCache(e.arg.Me.GetUID())
m.G().UserChanged(m.Ctx(), e.arg.Me.GetUID())

// Keep these:
m.G().NotifyRouter.HandleTrackingChanged(e.arg.Me.GetUID(), e.arg.Me.GetNormalizedName(), false)
m.G().NotifyRouter.HandleTrackingChanged(e.them.GetUID(), e.them.GetNormalizedName(), true)
// Keep these:
m.G().NotifyRouter.HandleTrackingChanged(e.arg.Me.GetUID(), e.arg.Me.GetNormalizedName(), false)
m.G().NotifyRouter.HandleTrackingChanged(e.them.GetUID(), e.them.GetNormalizedName(), true)

// Dismiss any associated gregor item.
if outcome.ResponsibleGregorItem != nil {
err = m.G().GregorState.DismissItem(m.Ctx(), nil,
outcome.ResponsibleGregorItem.Metadata().MsgID())
}
// Dismiss any associated gregor item.
if outcome.ResponsibleGregorItem != nil {
err = m.G().GregorState.DismissItem(m.Ctx(), nil,
outcome.ResponsibleGregorItem.Metadata().MsgID())
}

return err
Expand Down
1 change: 1 addition & 0 deletions go/identify3/service.go
Expand Up @@ -2,6 +2,7 @@ package identify3

import (
"fmt"

"github.com/keybase/client/go/engine"
"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
Expand Down
4 changes: 4 additions & 0 deletions go/libkb/globals.go
Expand Up @@ -96,6 +96,7 @@ type GlobalContext struct {
pvlSource MerkleStore // a cache and fetcher for pvl
paramProofStore MerkleStore // a cache and fetcher for param proofs
PayloadCache *PayloadCache // cache of ChainLink payload json wrappers
Pegboard *Pegboard

GpgClient *GpgCLI // A standard GPG-client (optional)
ShutdownHooks []ShutdownHook // on shutdown, fire these...
Expand Down Expand Up @@ -199,6 +200,7 @@ func NewGlobalContext() *GlobalContext {
switchUserMu: NewVerboseLock(VLog0, "switchUserMu"),
FeatureFlags: NewFeatureFlagSet(),
switchedUsers: make(map[NormalizedUsername]bool),
Pegboard: NewPegboard(),
}
return ret
}
Expand Down Expand Up @@ -344,6 +346,8 @@ func (g *GlobalContext) LogoutWithSecretKill(mctx MetaContext, killSecrets bool)

g.GetUPAKLoader().OnLogout()

g.Pegboard.OnLogout(mctx)

return nil
}

Expand Down

0 comments on commit 2f72370

Please sign in to comment.