github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/keybase_service_base.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/kbfs/data"
    14  	"github.com/keybase/client/go/kbfs/favorites"
    15  	"github.com/keybase/client/go/kbfs/idutil"
    16  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    17  	"github.com/keybase/client/go/kbfs/kbfsmd"
    18  	"github.com/keybase/client/go/kbfs/tlf"
    19  	"github.com/keybase/client/go/kbfs/tlfhandle"
    20  	kbname "github.com/keybase/client/go/kbun"
    21  	"github.com/keybase/client/go/libkb"
    22  	"github.com/keybase/client/go/logger"
    23  	"github.com/keybase/client/go/protocol/keybase1"
    24  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    25  	"github.com/pkg/errors"
    26  	"golang.org/x/net/context"
    27  )
    28  
    29  const (
    30  	cacheNotWriterExpiration = 5 * time.Second
    31  )
    32  
    33  // KeybaseServiceBase implements most of KeybaseService from protocol
    34  // defined clients.
    35  type KeybaseServiceBase struct {
    36  	context         Context
    37  	identifyClient  keybase1.IdentifyInterface
    38  	userClient      keybase1.UserInterface
    39  	teamsClient     keybase1.TeamsInterface
    40  	merkleClient    keybase1.MerkleInterface
    41  	sessionClient   keybase1.SessionInterface
    42  	favoriteClient  keybase1.FavoriteInterface
    43  	kbfsClient      keybase1.KbfsInterface
    44  	kbfsMountClient keybase1.KbfsMountInterface
    45  	gitClient       keybase1.GitInterface
    46  	kvstoreClient   keybase1.KvstoreInterface
    47  	log             logger.Logger
    48  
    49  	config     Config
    50  	merkleRoot *EventuallyConsistentMerkleRoot
    51  
    52  	sessionCacheLock sync.RWMutex
    53  	// Set to the zero value when invalidated.
    54  	cachedCurrentSession idutil.SessionInfo
    55  	sessionInProgressCh  chan struct{}
    56  
    57  	userCacheLock sync.RWMutex
    58  	// Map entries are removed when invalidated.
    59  	userCache map[keybase1.UID]idutil.UserInfo
    60  
    61  	teamCacheLock sync.RWMutex
    62  	// Map entries are removed when invalidated.
    63  	teamCache      map[keybase1.TeamID]idutil.TeamInfo
    64  	notWriterCache map[keybase1.TeamID]map[keybase1.UID]time.Time
    65  }
    66  
    67  // Wrapper over `KeybaseServiceBase` implementing a `merkleRootGetter`
    68  // that gets the merkle root directly from the service, without using
    69  // the cache.
    70  type keybaseServiceMerkleGetter struct {
    71  	k *KeybaseServiceBase
    72  }
    73  
    74  var _ idutil.MerkleRootGetter = (*keybaseServiceMerkleGetter)(nil)
    75  
    76  func (k *keybaseServiceMerkleGetter) GetCurrentMerkleRoot(
    77  	ctx context.Context) (keybase1.MerkleRootV2, time.Time, error) {
    78  	return k.k.getCurrentMerkleRoot(ctx)
    79  }
    80  
    81  func (k *keybaseServiceMerkleGetter) VerifyMerkleRoot(
    82  	_ context.Context, _ keybase1.MerkleRootV2, _ keybase1.KBFSRoot) error {
    83  	panic("constMerkleRootGetter doesn't verify merkle roots")
    84  }
    85  
    86  // NewKeybaseServiceBase makes a new KeybaseService.
    87  func NewKeybaseServiceBase(config Config, kbCtx Context, log logger.Logger) *KeybaseServiceBase {
    88  	k := KeybaseServiceBase{
    89  		config:         config,
    90  		context:        kbCtx,
    91  		log:            log,
    92  		userCache:      make(map[keybase1.UID]idutil.UserInfo),
    93  		teamCache:      make(map[keybase1.TeamID]idutil.TeamInfo),
    94  		notWriterCache: make(map[keybase1.TeamID]map[keybase1.UID]time.Time),
    95  	}
    96  	if config != nil {
    97  		k.merkleRoot = NewEventuallyConsistentMerkleRoot(
    98  			config, &keybaseServiceMerkleGetter{&k})
    99  	}
   100  	return &k
   101  }
   102  
   103  // FillClients sets the client protocol implementations needed for a KeybaseService.
   104  func (k *KeybaseServiceBase) FillClients(
   105  	identifyClient keybase1.IdentifyInterface,
   106  	userClient keybase1.UserInterface, teamsClient keybase1.TeamsInterface,
   107  	merkleClient keybase1.MerkleInterface,
   108  	sessionClient keybase1.SessionInterface,
   109  	favoriteClient keybase1.FavoriteInterface,
   110  	kbfsClient keybase1.KbfsInterface,
   111  	kbfsMountClient keybase1.KbfsMountInterface,
   112  	gitClient keybase1.GitInterface, kvstoreClient keybase1.KvstoreClient) {
   113  	k.identifyClient = identifyClient
   114  	k.userClient = userClient
   115  	k.teamsClient = teamsClient
   116  	k.merkleClient = merkleClient
   117  	k.sessionClient = sessionClient
   118  	k.favoriteClient = favoriteClient
   119  	k.kbfsClient = kbfsClient
   120  	k.kbfsMountClient = kbfsMountClient
   121  	k.gitClient = gitClient
   122  	k.kvstoreClient = kvstoreClient
   123  }
   124  
   125  type addVerifyingKeyFunc func(kbfscrypto.VerifyingKey)
   126  type addCryptPublicKeyFunc func(kbfscrypto.CryptPublicKey)
   127  
   128  // processKey adds the given public key to the appropriate verifying
   129  // or crypt list (as return values), and also updates the given name
   130  // map and parent map in place.
   131  func processKey(publicKey keybase1.PublicKeyV2NaCl,
   132  	addVerifyingKey addVerifyingKeyFunc,
   133  	addCryptPublicKey addCryptPublicKeyFunc,
   134  	kidNames map[keybase1.KID]string,
   135  	parents map[keybase1.KID]keybase1.KID) error {
   136  	// Import the KID to validate it.
   137  	key, err := libkb.ImportKeypairFromKID(publicKey.Base.Kid)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	if publicKey.Base.IsSibkey {
   142  		addVerifyingKey(kbfscrypto.MakeVerifyingKey(key.GetKID()))
   143  	} else {
   144  		addCryptPublicKey(kbfscrypto.MakeCryptPublicKey(key.GetKID()))
   145  	}
   146  	if publicKey.DeviceDescription != "" {
   147  		kidNames[publicKey.Base.Kid] = publicKey.DeviceDescription
   148  	}
   149  
   150  	if publicKey.Parent != nil {
   151  		parents[publicKey.Base.Kid] = *publicKey.Parent
   152  	}
   153  	return nil
   154  }
   155  
   156  // updateKIDNamesFromParents sets the name of each KID without a name
   157  // that has a a parent with a name, to that parent's name.
   158  func updateKIDNamesFromParents(kidNames map[keybase1.KID]string,
   159  	parents map[keybase1.KID]keybase1.KID) {
   160  	for kid, parent := range parents {
   161  		if _, ok := kidNames[kid]; ok {
   162  			continue
   163  		}
   164  		if parentName, ok := kidNames[parent]; ok {
   165  			kidNames[kid] = parentName
   166  		}
   167  	}
   168  }
   169  
   170  func filterKeys(keys map[keybase1.KID]keybase1.PublicKeyV2NaCl) (
   171  	verifyingKeys []kbfscrypto.VerifyingKey,
   172  	cryptPublicKeys []kbfscrypto.CryptPublicKey,
   173  	kidNames map[keybase1.KID]string, err error) {
   174  	kidNames = make(map[keybase1.KID]string, len(keys))
   175  	parents := make(map[keybase1.KID]keybase1.KID, len(keys))
   176  
   177  	addVerifyingKey := func(key kbfscrypto.VerifyingKey) {
   178  		verifyingKeys = append(verifyingKeys, key)
   179  	}
   180  	addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) {
   181  		cryptPublicKeys = append(cryptPublicKeys, key)
   182  	}
   183  
   184  	for _, publicKey := range keys {
   185  		if publicKey.Base.Revocation != nil {
   186  			continue
   187  		}
   188  
   189  		err := processKey(publicKey, addVerifyingKey, addCryptPublicKey,
   190  			kidNames, parents)
   191  		if err != nil {
   192  			return nil, nil, nil, err
   193  		}
   194  	}
   195  	updateKIDNamesFromParents(kidNames, parents)
   196  	return verifyingKeys, cryptPublicKeys, kidNames, nil
   197  }
   198  
   199  func (k *KeybaseServiceBase) filterRevokedKeys(
   200  	ctx context.Context,
   201  	uid keybase1.UID,
   202  	keys map[keybase1.KID]keybase1.PublicKeyV2NaCl,
   203  	reset *keybase1.ResetSummary) (
   204  	map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo,
   205  	map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo,
   206  	map[keybase1.KID]string, error) {
   207  	verifyingKeys := make(map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo)
   208  	cryptPublicKeys := make(
   209  		map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo)
   210  	var kidNames = map[keybase1.KID]string{}
   211  	var parents = map[keybase1.KID]keybase1.KID{}
   212  
   213  	for _, key := range keys {
   214  		var info idutil.RevokedKeyInfo
   215  		switch {
   216  		case key.Base.Revocation != nil:
   217  			info.Time = key.Base.Revocation.Time
   218  			info.MerkleRoot = key.Base.Revocation.PrevMerkleRootSigned
   219  			// If we don't have a prev seqno, then we already have the
   220  			// best merkle data we're going to get.
   221  			info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0)
   222  			info.SetSigChainLocation(key.Base.Revocation.SigChainLocation)
   223  		case reset != nil:
   224  			info.Time = keybase1.ToTime(keybase1.FromUnixTime(reset.Ctime))
   225  			info.MerkleRoot.Seqno = reset.MerkleRoot.Seqno
   226  			info.MerkleRoot.HashMeta = reset.MerkleRoot.HashMeta
   227  			// If we don't have a prev seqno, then we already have the
   228  			// best merkle data we're going to get.
   229  			info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0)
   230  			info.SetResetInfo(reset.ResetSeqno, true)
   231  		default:
   232  			// Not revoked.
   233  			continue
   234  		}
   235  
   236  		addVerifyingKey := func(key kbfscrypto.VerifyingKey) {
   237  			verifyingKeys[key] = info
   238  		}
   239  		addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) {
   240  			cryptPublicKeys[key] = info
   241  		}
   242  		err := processKey(key, addVerifyingKey, addCryptPublicKey,
   243  			kidNames, parents)
   244  		if err != nil {
   245  			return nil, nil, nil, err
   246  		}
   247  	}
   248  	updateKIDNamesFromParents(kidNames, parents)
   249  	return verifyingKeys, cryptPublicKeys, kidNames, nil
   250  
   251  }
   252  
   253  func (k *KeybaseServiceBase) getCachedCurrentSession() idutil.SessionInfo {
   254  	k.sessionCacheLock.RLock()
   255  	defer k.sessionCacheLock.RUnlock()
   256  	return k.cachedCurrentSession
   257  }
   258  
   259  func (k *KeybaseServiceBase) setCachedCurrentSession(s idutil.SessionInfo) {
   260  	k.sessionCacheLock.Lock()
   261  	defer k.sessionCacheLock.Unlock()
   262  	k.cachedCurrentSession = s
   263  }
   264  
   265  func (k *KeybaseServiceBase) getCachedUserInfo(
   266  	uid keybase1.UID) idutil.UserInfo {
   267  	k.userCacheLock.RLock()
   268  	defer k.userCacheLock.RUnlock()
   269  	return k.userCache[uid]
   270  }
   271  
   272  func (k *KeybaseServiceBase) setCachedUserInfo(
   273  	uid keybase1.UID, info idutil.UserInfo) {
   274  	k.userCacheLock.Lock()
   275  	defer k.userCacheLock.Unlock()
   276  	if info.Name == kbname.NormalizedUsername("") {
   277  		delete(k.userCache, uid)
   278  	} else {
   279  		k.userCache[uid] = info
   280  	}
   281  }
   282  
   283  func (k *KeybaseServiceBase) getCachedTeamInfo(
   284  	tid keybase1.TeamID) idutil.TeamInfo {
   285  	k.teamCacheLock.RLock()
   286  	defer k.teamCacheLock.RUnlock()
   287  	return k.teamCache[tid]
   288  }
   289  
   290  func (k *KeybaseServiceBase) setCachedTeamInfo(
   291  	tid keybase1.TeamID, info idutil.TeamInfo) {
   292  	k.teamCacheLock.Lock()
   293  	defer k.teamCacheLock.Unlock()
   294  	if info.Name == kbname.NormalizedUsername("") {
   295  		delete(k.teamCache, tid)
   296  		delete(k.notWriterCache, tid)
   297  	} else {
   298  		k.teamCache[tid] = info
   299  	}
   300  }
   301  
   302  func (k *KeybaseServiceBase) getCachedNotWriter(
   303  	tid keybase1.TeamID, uid keybase1.UID) (notWriter bool) {
   304  	// Full write lock because of the delete-after-expiration code
   305  	// below.
   306  	k.teamCacheLock.Lock()
   307  	defer k.teamCacheLock.Unlock()
   308  	cachedTime, notWriter := k.notWriterCache[tid][uid]
   309  	if !notWriter {
   310  		return false
   311  	}
   312  
   313  	if k.config.Clock().Now().Sub(cachedTime) > cacheNotWriterExpiration {
   314  		delete(k.notWriterCache[tid], uid)
   315  		return false
   316  	}
   317  	return true
   318  }
   319  
   320  func (k *KeybaseServiceBase) setCachedNotWriter(
   321  	tid keybase1.TeamID, uid keybase1.UID) {
   322  	k.teamCacheLock.Lock()
   323  	defer k.teamCacheLock.Unlock()
   324  	teamMap := k.notWriterCache[tid]
   325  	if teamMap == nil {
   326  		teamMap = make(map[keybase1.UID]time.Time)
   327  		k.notWriterCache[tid] = teamMap
   328  	}
   329  	teamMap[uid] = k.config.Clock().Now()
   330  }
   331  
   332  // ClearCaches implements the KeybaseService interface for
   333  // KeybaseServiceBase.
   334  func (k *KeybaseServiceBase) ClearCaches(ctx context.Context) {
   335  	k.log.CDebugf(ctx, "Clearing KBFS-side user and team caches")
   336  
   337  	k.setCachedCurrentSession(idutil.SessionInfo{})
   338  	func() {
   339  		k.userCacheLock.Lock()
   340  		defer k.userCacheLock.Unlock()
   341  		k.userCache = make(map[keybase1.UID]idutil.UserInfo)
   342  	}()
   343  	k.teamCacheLock.Lock()
   344  	defer k.teamCacheLock.Unlock()
   345  	k.teamCache = make(map[keybase1.TeamID]idutil.TeamInfo)
   346  	k.notWriterCache = make(map[keybase1.TeamID]map[keybase1.UID]time.Time)
   347  }
   348  
   349  // LoggedIn implements keybase1.NotifySessionInterface.
   350  func (k *KeybaseServiceBase) LoggedIn(ctx context.Context, arg keybase1.LoggedInArg) error {
   351  	k.log.CDebugf(ctx, "Current session logged in: %s, signedUp: %t", arg.Username, arg.SignedUp)
   352  	// Since we don't have the whole session, just clear the cache and
   353  	// repopulate it.  The `CurrentSession` call executes the "logged
   354  	// in" flow.
   355  	k.setCachedCurrentSession(idutil.SessionInfo{})
   356  	const sessionID = 0
   357  	_, err := k.CurrentSession(ctx, sessionID)
   358  	if err != nil {
   359  		k.log.CDebugf(ctx, "Getting current session failed when %s is logged "+
   360  			"in, so pretending user has logged out: %v",
   361  			arg.Username, err)
   362  		if k.config != nil {
   363  			serviceLoggedOut(ctx, k.config)
   364  		}
   365  		return nil
   366  	}
   367  
   368  	return nil
   369  }
   370  
   371  // LoggedOut implements keybase1.NotifySessionInterface.
   372  func (k *KeybaseServiceBase) LoggedOut(ctx context.Context) error {
   373  	k.log.CDebugf(ctx, "Current session logged out")
   374  	k.setCachedCurrentSession(idutil.SessionInfo{})
   375  	if k.config != nil {
   376  		serviceLoggedOut(ctx, k.config)
   377  	}
   378  	return nil
   379  }
   380  
   381  // KeyfamilyChanged implements keybase1.NotifyKeyfamilyInterface.
   382  func (k *KeybaseServiceBase) KeyfamilyChanged(ctx context.Context,
   383  	uid keybase1.UID) error {
   384  	k.log.CDebugf(ctx, "Key family for user %s changed", uid)
   385  	k.setCachedUserInfo(uid, idutil.UserInfo{})
   386  
   387  	if k.getCachedCurrentSession().UID == uid {
   388  		mdServer := k.config.MDServer()
   389  		if mdServer != nil {
   390  			// Ignore any errors for now, we don't want to block this
   391  			// notification and it's not worth spawning a goroutine for.
   392  			mdServer.CheckForRekeys(context.Background())
   393  		}
   394  	}
   395  
   396  	return nil
   397  }
   398  
   399  // ReachabilityChanged implements keybase1.ReachabiltyInterface.
   400  func (k *KeybaseServiceBase) ReachabilityChanged(ctx context.Context,
   401  	reachability keybase1.Reachability) error {
   402  	k.log.CDebugf(ctx, "CheckReachability invoked: %v", reachability)
   403  	if reachability.Reachable == keybase1.Reachable_YES {
   404  		k.config.KBFSOps().PushConnectionStatusChange(GregorServiceName, nil)
   405  	} else {
   406  		k.config.KBFSOps().PushConnectionStatusChange(
   407  			GregorServiceName, errDisconnected{})
   408  	}
   409  	mdServer := k.config.MDServer()
   410  	if mdServer != nil {
   411  		mdServer.CheckReachability(ctx)
   412  	}
   413  	return nil
   414  }
   415  
   416  // StartReachability implements keybase1.ReachabilityInterface.
   417  func (k *KeybaseServiceBase) StartReachability(ctx context.Context) (res keybase1.Reachability, err error) {
   418  	return k.CheckReachability(ctx)
   419  }
   420  
   421  // CheckReachability implements keybase1.ReachabilityInterface.
   422  func (k *KeybaseServiceBase) CheckReachability(ctx context.Context) (res keybase1.Reachability, err error) {
   423  	res.Reachable = keybase1.Reachable_NO
   424  	mdServer := k.config.MDServer()
   425  	if mdServer != nil && mdServer.IsConnected() {
   426  		res.Reachable = keybase1.Reachable_YES
   427  	}
   428  	return res, nil
   429  }
   430  
   431  // PaperKeyCached implements keybase1.NotifyPaperKeyInterface.
   432  func (k *KeybaseServiceBase) PaperKeyCached(ctx context.Context,
   433  	arg keybase1.PaperKeyCachedArg) error {
   434  	k.log.CDebugf(ctx, "Paper key for %s cached", arg.Uid)
   435  
   436  	if k.getCachedCurrentSession().UID == arg.Uid {
   437  		err := k.config.KBFSOps().KickoffAllOutstandingRekeys()
   438  		if err != nil {
   439  			// Ignore and log errors here. For now the only way it could error
   440  			// is when the method is called on a folderBranchOps which is a
   441  			// developer mistake and not recoverable from code.
   442  			k.log.CDebugf(ctx,
   443  				"Calling KickoffAllOutstandingRekeys error: %s", err)
   444  		}
   445  		// Ignore any errors for now, we don't want to block this
   446  		// notification and it's not worth spawning a goroutine for.
   447  		mdServer := k.config.MDServer()
   448  		if mdServer != nil {
   449  			mdServer.CheckForRekeys(context.Background())
   450  		}
   451  	}
   452  
   453  	return nil
   454  }
   455  
   456  // ClientOutOfDate implements keybase1.NotifySessionInterface.
   457  func (k *KeybaseServiceBase) ClientOutOfDate(ctx context.Context,
   458  	arg keybase1.ClientOutOfDateArg) error {
   459  	k.log.CDebugf(ctx, "Client out of date: %v", arg)
   460  	return nil
   461  }
   462  
   463  // RootAuditError implements keybase1.NotifyAuditInterface.
   464  func (k *KeybaseServiceBase) RootAuditError(ctx context.Context,
   465  	arg keybase1.RootAuditErrorArg) error {
   466  	k.log.CDebugf(ctx, "Merkle tree audit error: %v", arg.Message)
   467  	return nil
   468  }
   469  
   470  // ConvertIdentifyError converts a errors during identify into KBFS errors
   471  func ConvertIdentifyError(assertion string, err error) error {
   472  	switch err.(type) {
   473  	case libkb.NotFoundError:
   474  		return idutil.NoSuchUserError{Input: assertion}
   475  	case libkb.ResolutionError:
   476  		return idutil.NoSuchUserError{Input: assertion}
   477  	}
   478  	return err
   479  }
   480  
   481  // Resolve implements the KeybaseService interface for KeybaseServiceBase.
   482  func (k *KeybaseServiceBase) Resolve(
   483  	ctx context.Context, assertion string,
   484  	offline keybase1.OfflineAvailability) (
   485  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   486  	res, err := k.identifyClient.Resolve3(
   487  		ctx, keybase1.Resolve3Arg{
   488  			Assertion: assertion,
   489  			Oa:        offline,
   490  		})
   491  	if err != nil {
   492  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""),
   493  			ConvertIdentifyError(assertion, err)
   494  	}
   495  	return kbname.NewNormalizedUsername(res.Name), res.Id, nil
   496  }
   497  
   498  // Identify implements the KeybaseService interface for KeybaseServiceBase.
   499  func (k *KeybaseServiceBase) Identify(
   500  	ctx context.Context, assertion, reason string,
   501  	offline keybase1.OfflineAvailability) (
   502  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   503  	// setting UseDelegateUI to true here will cause daemon to use
   504  	// registered identify ui providers instead of terminal if any
   505  	// are available.  If not, then it will use the terminal UI.
   506  	arg := keybase1.IdentifyLiteArg{
   507  		Assertion:     assertion,
   508  		UseDelegateUI: true,
   509  		Reason:        keybase1.IdentifyReason{Reason: reason},
   510  		// No need to go back and forth with the UI until the service
   511  		// knows for sure there's a need for a dialogue.
   512  		CanSuppressUI: true,
   513  		Oa:            offline,
   514  	}
   515  
   516  	ei := tlfhandle.GetExtendedIdentify(ctx)
   517  	arg.IdentifyBehavior = ei.Behavior
   518  
   519  	res, err := k.identifyClient.IdentifyLite(ctx, arg)
   520  	// IdentifyLite still returns keybase1.UserPlusKeys data (sans
   521  	// keys), even if it gives a NoSigChainError or a UserDeletedError,
   522  	// and in KBFS it's fine if the user doesn't have a full sigchain
   523  	// (e.g., it's just like the sharing before signup case, except
   524  	// the user already has a UID).  Both types of users are based
   525  	// entirely on server trust anyway.
   526  	switch err.(type) {
   527  	case nil:
   528  	case libkb.NoSigChainError, libkb.UserDeletedError:
   529  		ei.OnError(ctx)
   530  		// But if the username is blame, just return it, since the
   531  		// returned username would be useless and confusing.
   532  		if res.Ul.Name == "" {
   533  			return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   534  		}
   535  		k.log.CDebugf(ctx,
   536  			"Ignoring error (%s) for user %s with no sigchain; "+
   537  				"error type=%T", err, res.Ul.Name, err)
   538  	default:
   539  		// If the caller is waiting for breaks, let them know we got an error.
   540  		ei.OnError(ctx)
   541  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""),
   542  			ConvertIdentifyError(assertion, err)
   543  	}
   544  
   545  	// This is required for every identify call. The userBreak
   546  	// function will take care of checking if res.TrackBreaks is nil
   547  	// or not.
   548  	name := kbname.NormalizedUsername(res.Ul.Name)
   549  	if res.Ul.Id.IsUser() {
   550  		asUser, err := res.Ul.Id.AsUser()
   551  		if err != nil {
   552  			return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   553  		}
   554  		ei.UserBreak(ctx, name, asUser, res.TrackBreaks)
   555  	} else if !res.Ul.Id.IsNil() {
   556  		ei.TeamBreak(ctx, res.Ul.Id.AsTeamOrBust(), res.TrackBreaks)
   557  	}
   558  
   559  	return name, res.Ul.Id, nil
   560  }
   561  
   562  // NormalizeSocialAssertion implements the KeybaseService interface for
   563  // KeybaseServiceBase.
   564  func (k *KeybaseServiceBase) NormalizeSocialAssertion(
   565  	ctx context.Context, assertion string) (keybase1.SocialAssertion, error) {
   566  	return k.identifyClient.NormalizeSocialAssertion(ctx, assertion)
   567  }
   568  
   569  // ResolveIdentifyImplicitTeam implements the KeybaseService interface
   570  // for KeybaseServiceBase.
   571  func (k *KeybaseServiceBase) ResolveIdentifyImplicitTeam(
   572  	ctx context.Context, assertions, suffix string, tlfType tlf.Type,
   573  	doIdentifies bool, reason string,
   574  	offline keybase1.OfflineAvailability) (idutil.ImplicitTeamInfo, error) {
   575  	if tlfType != tlf.Private && tlfType != tlf.Public {
   576  		return idutil.ImplicitTeamInfo{}, fmt.Errorf(
   577  			"Invalid implicit team TLF type: %s", tlfType)
   578  	}
   579  
   580  	arg := keybase1.ResolveIdentifyImplicitTeamArg{
   581  		Assertions:   assertions,
   582  		Suffix:       suffix,
   583  		DoIdentifies: doIdentifies,
   584  		Reason:       keybase1.IdentifyReason{Reason: reason},
   585  		Create:       true,
   586  		IsPublic:     tlfType == tlf.Public,
   587  		Oa:           offline,
   588  	}
   589  
   590  	ei := tlfhandle.GetExtendedIdentify(ctx)
   591  	arg.IdentifyBehavior = ei.Behavior
   592  
   593  	res, err := k.identifyClient.ResolveIdentifyImplicitTeam(ctx, arg)
   594  	if err != nil {
   595  		return idutil.ImplicitTeamInfo{}, ConvertIdentifyError(assertions, err)
   596  	}
   597  	if strings.Contains(res.DisplayName, "_implicit_team_") {
   598  		k.log.CWarningf(
   599  			ctx, "Got display name %s for assertions %s",
   600  			res.DisplayName, assertions)
   601  	}
   602  	name := kbname.NormalizedUsername(res.DisplayName)
   603  
   604  	// Exactly one break callback is required for every identify call.
   605  	if doIdentifies {
   606  		if len(res.TrackBreaks) > 0 {
   607  			// Iterate the map to get one entry, then break.
   608  			for userVer, breaks := range res.TrackBreaks {
   609  				// TODO: resolve the UID into a username so we don't have to
   610  				// pass in the full display name here?
   611  				ei.UserBreak(ctx, name, userVer.Uid, &breaks)
   612  				break
   613  			}
   614  		} else {
   615  			ei.TeamBreak(ctx, keybase1.TeamID(""), nil)
   616  		}
   617  	}
   618  
   619  	iteamInfo := idutil.ImplicitTeamInfo{
   620  		Name: name,
   621  		TID:  res.TeamID,
   622  	}
   623  	if res.FolderID != "" {
   624  		iteamInfo.TlfID, err = tlf.ParseID(res.FolderID.String())
   625  		if err != nil {
   626  			return idutil.ImplicitTeamInfo{}, err
   627  		}
   628  	}
   629  
   630  	return iteamInfo, nil
   631  }
   632  
   633  // ResolveImplicitTeamByID implements the KeybaseService interface for
   634  // KeybaseServiceBase.
   635  func (k *KeybaseServiceBase) ResolveImplicitTeamByID(
   636  	ctx context.Context, teamID keybase1.TeamID) (name string, err error) {
   637  	arg := keybase1.ResolveImplicitTeamArg{
   638  		Id: teamID,
   639  	}
   640  
   641  	res, err := k.identifyClient.ResolveImplicitTeam(ctx, arg)
   642  	if err != nil {
   643  		return "", err
   644  	}
   645  	return res.Name, nil
   646  }
   647  
   648  func (k *KeybaseServiceBase) checkForRevokedVerifyingKey(
   649  	ctx context.Context, currUserInfo idutil.UserInfo, kid keybase1.KID) (
   650  	newUserInfo idutil.UserInfo, exists bool, err error) {
   651  	newUserInfo = currUserInfo
   652  	for key, info := range currUserInfo.RevokedVerifyingKeys {
   653  		if !key.KID().Equal(kid) {
   654  			continue
   655  		}
   656  		exists = true
   657  		if info.FilledInMerkle() {
   658  			break
   659  		}
   660  
   661  		k.log.CDebugf(ctx, "Filling in merkle info for user %s, revoked key %s",
   662  			currUserInfo.UID, kid)
   663  
   664  		// If possible, ask the service to give us the first merkle
   665  		// root that covers this revoke. Some older device revokes
   666  		// didn't yet include a prev field, so we can't refine the
   667  		// merkle root in those cases, and will be relying only on
   668  		// server trust.
   669  		if info.MerkleRoot.Seqno > 0 {
   670  			var res keybase1.NextMerkleRootRes
   671  			resetSeqno, isReset := info.ResetInfo()
   672  			if isReset {
   673  				res, err = k.userClient.FindNextMerkleRootAfterReset(ctx,
   674  					keybase1.FindNextMerkleRootAfterResetArg{
   675  						Uid:        currUserInfo.UID,
   676  						ResetSeqno: resetSeqno,
   677  						Prev: keybase1.ResetMerkleRoot{
   678  							Seqno:    info.MerkleRoot.Seqno,
   679  							HashMeta: info.MerkleRoot.HashMeta,
   680  						},
   681  					})
   682  			} else {
   683  				res, err = k.userClient.FindNextMerkleRootAfterRevoke(ctx,
   684  					keybase1.FindNextMerkleRootAfterRevokeArg{
   685  						Uid:  currUserInfo.UID,
   686  						Kid:  kid,
   687  						Loc:  info.SigChainLocation(),
   688  						Prev: info.MerkleRoot,
   689  					})
   690  			}
   691  			if m, ok := err.(libkb.MerkleClientError); ok && m.IsOldTree() { // nolint
   692  				k.log.CDebugf(ctx, "Merkle root is too old for checking "+
   693  					"the revoked key: %+v", err)
   694  				info.MerkleRoot.Seqno = 0
   695  			} else if err != nil {
   696  				return idutil.UserInfo{}, false, err
   697  			} else if res.Res != nil {
   698  				info.MerkleRoot = *res.Res
   699  			}
   700  		}
   701  		info.SetFilledInMerkle(true)
   702  		newUserInfo = currUserInfo.DeepCopy()
   703  		newUserInfo.RevokedVerifyingKeys[key] = info
   704  		k.setCachedUserInfo(newUserInfo.UID, newUserInfo)
   705  		break
   706  	}
   707  
   708  	return newUserInfo, exists, nil
   709  }
   710  
   711  // LoadUserPlusKeys implements the KeybaseService interface for
   712  // KeybaseServiceBase.
   713  func (k *KeybaseServiceBase) LoadUserPlusKeys(
   714  	ctx context.Context, uid keybase1.UID, pollForKID keybase1.KID,
   715  	offline keybase1.OfflineAvailability) (idutil.UserInfo, error) {
   716  	cachedUserInfo := k.getCachedUserInfo(uid)
   717  	if cachedUserInfo.Name != kbname.NormalizedUsername("") {
   718  		if pollForKID == keybase1.KID("") {
   719  			return cachedUserInfo, nil
   720  		}
   721  		// Skip the cache if pollForKID isn't present in
   722  		// `VerifyingKeys` or one of the revoked verifying keys.
   723  		for _, key := range cachedUserInfo.VerifyingKeys {
   724  			if key.KID().Equal(pollForKID) {
   725  				return cachedUserInfo, nil
   726  			}
   727  		}
   728  
   729  		// Check if the key is revoked, and fill in the merkle info in
   730  		// that case.
   731  		cachedUserInfo, exists, err := k.checkForRevokedVerifyingKey(
   732  			ctx, cachedUserInfo, pollForKID)
   733  		if err != nil {
   734  			return idutil.UserInfo{}, err
   735  		}
   736  		if exists {
   737  			return cachedUserInfo, nil
   738  		}
   739  	}
   740  
   741  	arg := keybase1.LoadUserPlusKeysV2Arg{
   742  		Uid:        uid,
   743  		PollForKID: pollForKID,
   744  		Oa:         offline,
   745  	}
   746  	res, err := k.userClient.LoadUserPlusKeysV2(ctx, arg)
   747  	if err != nil {
   748  		return idutil.UserInfo{}, err
   749  	}
   750  
   751  	userInfo, err := k.processUserPlusKeys(ctx, res)
   752  	if err != nil {
   753  		return idutil.UserInfo{}, err
   754  	}
   755  
   756  	if pollForKID != keybase1.KID("") {
   757  		// Fill in merkle info if we were explicitly trying to load a
   758  		// revoked key.
   759  		userInfo, _, err = k.checkForRevokedVerifyingKey(
   760  			ctx, userInfo, pollForKID)
   761  		if err != nil {
   762  			return idutil.UserInfo{}, err
   763  		}
   764  	}
   765  	return userInfo, nil
   766  }
   767  
   768  func (k *KeybaseServiceBase) getLastWriterInfo(
   769  	ctx context.Context, teamInfo idutil.TeamInfo, tlfType tlf.Type,
   770  	user keybase1.UID, verifyingKey kbfscrypto.VerifyingKey) (
   771  	idutil.TeamInfo, error) {
   772  	if _, ok := teamInfo.LastWriters[verifyingKey]; ok {
   773  		// Already cached, nothing to do.
   774  		return teamInfo, nil
   775  	}
   776  
   777  	res, err := k.teamsClient.FindNextMerkleRootAfterTeamRemovalBySigningKey(
   778  		ctx, keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg{
   779  			Uid:        user,
   780  			SigningKey: verifyingKey.KID(),
   781  			Team:       teamInfo.TID,
   782  			IsPublic:   tlfType == tlf.Public,
   783  		})
   784  	if err != nil {
   785  		return idutil.TeamInfo{}, err
   786  	}
   787  
   788  	// Copy any old data to avoid races.
   789  	newLastWriters := make(
   790  		map[kbfscrypto.VerifyingKey]keybase1.MerkleRootV2,
   791  		len(teamInfo.LastWriters)+1)
   792  	for k, v := range teamInfo.LastWriters {
   793  		newLastWriters[k] = v
   794  	}
   795  	newLastWriters[verifyingKey] = *res.Res
   796  	teamInfo.LastWriters = newLastWriters
   797  	return teamInfo, nil
   798  }
   799  
   800  var allowedLoadTeamRoles = map[keybase1.TeamRole]bool{
   801  	keybase1.TeamRole_NONE:   true,
   802  	keybase1.TeamRole_WRITER: true,
   803  	keybase1.TeamRole_READER: true,
   804  }
   805  
   806  // LoadTeamPlusKeys implements the KeybaseService interface for
   807  // KeybaseServiceBase.
   808  func (k *KeybaseServiceBase) LoadTeamPlusKeys(
   809  	ctx context.Context, tid keybase1.TeamID, tlfType tlf.Type,
   810  	desiredKeyGen kbfsmd.KeyGen, desiredUser keybase1.UserVersion,
   811  	desiredKey kbfscrypto.VerifyingKey, desiredRole keybase1.TeamRole,
   812  	offline keybase1.OfflineAvailability) (idutil.TeamInfo, error) {
   813  	if !allowedLoadTeamRoles[desiredRole] {
   814  		panic(fmt.Sprintf("Disallowed team role: %v", desiredRole))
   815  	}
   816  
   817  	cachedTeamInfo := k.getCachedTeamInfo(tid)
   818  	if cachedTeamInfo.Name != kbname.NormalizedUsername("") {
   819  		// If the cached team info doesn't satisfy our desires, don't
   820  		// use it.
   821  		satisfiesDesires := true
   822  		if desiredKeyGen >= kbfsmd.FirstValidKeyGen {
   823  			// If `desiredKeyGen` is at most as large as the keygen in
   824  			// the cached latest team info, then our cached info
   825  			// satisfies our desires.
   826  			satisfiesDesires = desiredKeyGen <= cachedTeamInfo.LatestKeyGen
   827  		}
   828  
   829  		if satisfiesDesires && desiredUser.Uid.Exists() {
   830  			// If the user is in the writer map, that satisfies none, reader
   831  			// or writer desires.
   832  			satisfiesDesires = cachedTeamInfo.Writers[desiredUser.Uid]
   833  			if !satisfiesDesires {
   834  				if desiredRole == keybase1.TeamRole_NONE ||
   835  					desiredRole == keybase1.TeamRole_READER {
   836  					// If the user isn't a writer, but the desired
   837  					// role is a reader, we need to check the reader
   838  					// map explicitly.
   839  					satisfiesDesires = cachedTeamInfo.Readers[desiredUser.Uid]
   840  				} else {
   841  					if !desiredKey.IsNil() {
   842  						// If the desired role was at least a writer, but
   843  						// the user isn't currently a writer, see if they
   844  						// ever were.
   845  						var err error
   846  						cachedTeamInfo, err = k.getLastWriterInfo(
   847  							ctx, cachedTeamInfo, tlfType, desiredUser.Uid,
   848  							desiredKey)
   849  						if err != nil {
   850  							return idutil.TeamInfo{}, err
   851  						}
   852  						k.setCachedTeamInfo(tid, cachedTeamInfo)
   853  					}
   854  
   855  					// If we have recently learned that the user is
   856  					// not a writer (e.g., of a public folder), we
   857  					// should rely on that cached info to avoid
   858  					// looking that up too often.
   859  					satisfiesDesires = k.getCachedNotWriter(
   860  						tid, desiredUser.Uid)
   861  				}
   862  			}
   863  		}
   864  
   865  		if satisfiesDesires {
   866  			return cachedTeamInfo, nil
   867  		}
   868  	}
   869  
   870  	arg := keybase1.LoadTeamPlusApplicationKeysArg{
   871  		Id:              tid,
   872  		Application:     keybase1.TeamApplication_KBFS,
   873  		IncludeKBFSKeys: true,
   874  		Oa:              offline,
   875  	}
   876  
   877  	if desiredKeyGen >= kbfsmd.FirstValidKeyGen {
   878  		arg.Refreshers.NeedApplicationsAtGenerationsWithKBFS =
   879  			map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{
   880  				keybase1.PerTeamKeyGeneration(desiredKeyGen): {
   881  					keybase1.TeamApplication_KBFS,
   882  				},
   883  			}
   884  	}
   885  
   886  	if desiredUser.Uid.Exists() && desiredKey.IsNil() {
   887  		arg.Refreshers.WantMembers = append(
   888  			arg.Refreshers.WantMembers, desiredUser)
   889  		arg.Refreshers.WantMembersRole = desiredRole
   890  	}
   891  
   892  	res, err := k.teamsClient.LoadTeamPlusApplicationKeys(ctx, arg)
   893  	if err != nil {
   894  		return idutil.TeamInfo{}, err
   895  	}
   896  
   897  	if tid != res.Id {
   898  		return idutil.TeamInfo{}, fmt.Errorf(
   899  			"TID doesn't match: %s vs %s", tid, res.Id)
   900  	}
   901  
   902  	info := idutil.TeamInfo{
   903  		Name:      kbname.NormalizedUsername(res.Name),
   904  		TID:       res.Id,
   905  		CryptKeys: make(map[kbfsmd.KeyGen]kbfscrypto.TLFCryptKey),
   906  		Writers:   make(map[keybase1.UID]bool),
   907  		Readers:   make(map[keybase1.UID]bool),
   908  	}
   909  	for _, key := range res.ApplicationKeys {
   910  		keyGen := kbfsmd.KeyGen(key.KeyGeneration)
   911  		info.CryptKeys[keyGen] =
   912  			kbfscrypto.MakeTLFCryptKey(key.Key)
   913  		if keyGen > info.LatestKeyGen {
   914  			info.LatestKeyGen = keyGen
   915  		}
   916  	}
   917  
   918  	for _, user := range res.Writers {
   919  		info.Writers[user.Uid] = true
   920  	}
   921  	for _, user := range res.OnlyReaders {
   922  		info.Readers[user.Uid] = true
   923  	}
   924  
   925  	// For subteams, get the root team ID.
   926  	if tid.IsSubTeam() {
   927  		rootID, err := k.teamsClient.GetTeamRootID(ctx, tid)
   928  		if err != nil {
   929  			return idutil.TeamInfo{}, err
   930  		}
   931  		info.RootID = rootID
   932  	}
   933  
   934  	// Fill in `LastWriters`, only if needed.
   935  	if desiredUser.Uid.Exists() && desiredRole == keybase1.TeamRole_WRITER &&
   936  		!info.Writers[desiredUser.Uid] && !desiredKey.IsNil() {
   937  		info, err = k.getLastWriterInfo(
   938  			ctx, info, tlfType, desiredUser.Uid, desiredKey)
   939  		if err != nil {
   940  			return idutil.TeamInfo{}, err
   941  		}
   942  	}
   943  
   944  	k.setCachedTeamInfo(tid, info)
   945  
   946  	if desiredUser.Uid.Exists() && !info.Writers[desiredUser.Uid] &&
   947  		!(desiredRole == keybase1.TeamRole_NONE ||
   948  			desiredRole == keybase1.TeamRole_READER) {
   949  		// Remember that this user was not a writer for a short
   950  		// amount of time, to avoid repeated lookups for writers
   951  		// in a public folder (for example).
   952  		k.setCachedNotWriter(tid, desiredUser.Uid)
   953  	}
   954  
   955  	return info, nil
   956  }
   957  
   958  // CreateTeamTLF implements the KeybaseService interface for
   959  // KeybaseServiceBase.
   960  func (k *KeybaseServiceBase) CreateTeamTLF(
   961  	ctx context.Context, teamID keybase1.TeamID, tlfID tlf.ID) (err error) {
   962  	return k.kbfsClient.CreateTLF(ctx, keybase1.CreateTLFArg{
   963  		TeamID: teamID,
   964  		TlfID:  keybase1.TLFID(tlfID.String()),
   965  	})
   966  }
   967  
   968  // GetTeamSettings implements the KeybaseService interface for
   969  // KeybaseServiceBase.
   970  func (k *KeybaseServiceBase) GetTeamSettings(
   971  	ctx context.Context, teamID keybase1.TeamID,
   972  	offline keybase1.OfflineAvailability) (
   973  	keybase1.KBFSTeamSettings, error) {
   974  	// TODO: get invalidations from the server and cache the settings?
   975  	return k.kbfsClient.GetKBFSTeamSettings(
   976  		ctx, keybase1.GetKBFSTeamSettingsArg{
   977  			TeamID: teamID,
   978  			Oa:     offline,
   979  		})
   980  }
   981  
   982  func (k *KeybaseServiceBase) getCurrentMerkleRoot(ctx context.Context) (
   983  	keybase1.MerkleRootV2, time.Time, error) {
   984  	const merkleFreshnessMs = int(time.Second * 60 / time.Millisecond)
   985  	res, err := k.merkleClient.GetCurrentMerkleRoot(ctx, merkleFreshnessMs)
   986  	if err != nil {
   987  		return keybase1.MerkleRootV2{}, time.Time{}, err
   988  	}
   989  
   990  	return res.Root, keybase1.FromTime(res.UpdateTime), nil
   991  }
   992  
   993  // GetCurrentMerkleRoot implements the KeybaseService interface for
   994  // KeybaseServiceBase.
   995  func (k *KeybaseServiceBase) GetCurrentMerkleRoot(ctx context.Context) (
   996  	keybase1.MerkleRootV2, time.Time, error) {
   997  	// Refresh the cached value in the background if the cached value
   998  	// is older than 30s; if our cached value is more than 60s old,
   999  	// block.
  1000  	_, root, rootTime, err := k.merkleRoot.Get(
  1001  		ctx, 30*time.Second, 60*time.Second)
  1002  	return root, rootTime, err
  1003  }
  1004  
  1005  // VerifyMerkleRoot implements the KBPKI interface for KeybaseServiceBase.
  1006  func (k *KeybaseServiceBase) VerifyMerkleRoot(
  1007  	ctx context.Context, root keybase1.MerkleRootV2,
  1008  	kbfsRoot keybase1.KBFSRoot) error {
  1009  	return k.merkleClient.VerifyMerkleRootAndKBFS(ctx,
  1010  		keybase1.VerifyMerkleRootAndKBFSArg{
  1011  			Root:             root,
  1012  			ExpectedKBFSRoot: kbfsRoot,
  1013  		})
  1014  }
  1015  
  1016  func (k *KeybaseServiceBase) processUserPlusKeys(
  1017  	ctx context.Context, upk keybase1.UserPlusKeysV2AllIncarnations) (
  1018  	idutil.UserInfo, error) {
  1019  	verifyingKeys, cryptPublicKeys, kidNames, err := filterKeys(
  1020  		upk.Current.DeviceKeys)
  1021  	if err != nil {
  1022  		return idutil.UserInfo{}, err
  1023  	}
  1024  
  1025  	revokedVerifyingKeys, revokedCryptPublicKeys, revokedKidNames, err :=
  1026  		k.filterRevokedKeys(
  1027  			ctx, upk.Current.Uid, upk.Current.DeviceKeys, upk.Current.Reset)
  1028  	if err != nil {
  1029  		return idutil.UserInfo{}, err
  1030  	}
  1031  
  1032  	if len(revokedKidNames) > 0 {
  1033  		for k, v := range revokedKidNames {
  1034  			kidNames[k] = v
  1035  		}
  1036  	}
  1037  
  1038  	for _, incarnation := range upk.PastIncarnations {
  1039  		revokedVerifyingKeysPast, revokedCryptPublicKeysPast,
  1040  			revokedKidNames, err :=
  1041  			k.filterRevokedKeys(
  1042  				ctx, incarnation.Uid, incarnation.DeviceKeys, incarnation.Reset)
  1043  		if err != nil {
  1044  			return idutil.UserInfo{}, err
  1045  		}
  1046  
  1047  		if len(revokedKidNames) > 0 {
  1048  			for k, v := range revokedKidNames {
  1049  				kidNames[k] = v
  1050  			}
  1051  		}
  1052  
  1053  		for k, v := range revokedVerifyingKeysPast {
  1054  			revokedVerifyingKeys[k] = v
  1055  		}
  1056  		for k, v := range revokedCryptPublicKeysPast {
  1057  			revokedCryptPublicKeys[k] = v
  1058  		}
  1059  	}
  1060  
  1061  	u := idutil.UserInfo{
  1062  		Name: kbname.NewNormalizedUsername(
  1063  			upk.Current.Username),
  1064  		UID:                    upk.Current.Uid,
  1065  		VerifyingKeys:          verifyingKeys,
  1066  		CryptPublicKeys:        cryptPublicKeys,
  1067  		KIDNames:               kidNames,
  1068  		EldestSeqno:            upk.Current.EldestSeqno,
  1069  		RevokedVerifyingKeys:   revokedVerifyingKeys,
  1070  		RevokedCryptPublicKeys: revokedCryptPublicKeys,
  1071  	}
  1072  
  1073  	k.setCachedUserInfo(upk.Current.Uid, u)
  1074  	return u, nil
  1075  }
  1076  
  1077  func (k *KeybaseServiceBase) getCachedCurrentSessionOrInProgressCh() (
  1078  	cachedSession idutil.SessionInfo, inProgressCh chan struct{}, doRPC bool) {
  1079  	k.sessionCacheLock.Lock()
  1080  	defer k.sessionCacheLock.Unlock()
  1081  
  1082  	if k.cachedCurrentSession != (idutil.SessionInfo{}) {
  1083  		return k.cachedCurrentSession, nil, false
  1084  	}
  1085  
  1086  	// If someone already started the RPC, wait for them (and release
  1087  	// the lock).
  1088  	if k.sessionInProgressCh != nil {
  1089  		return idutil.SessionInfo{}, k.sessionInProgressCh, false
  1090  	}
  1091  
  1092  	k.sessionInProgressCh = make(chan struct{})
  1093  	return idutil.SessionInfo{}, k.sessionInProgressCh, true
  1094  }
  1095  
  1096  func (k *KeybaseServiceBase) getCurrentSession(
  1097  	ctx context.Context, sessionID int) (idutil.SessionInfo, bool, error) {
  1098  	var cachedCurrentSession idutil.SessionInfo
  1099  	var inProgressCh chan struct{}
  1100  	doRPC := false
  1101  	// Loop until either we have the session info, or until we are the
  1102  	// sole goroutine that needs to make the RPC.  Avoid holding the
  1103  	// session cache lock during the RPC, since that can result in a
  1104  	// deadlock if the RPC results in a call to `ClearCaches()`.
  1105  	for !doRPC {
  1106  		cachedCurrentSession, inProgressCh, doRPC =
  1107  			k.getCachedCurrentSessionOrInProgressCh()
  1108  		if cachedCurrentSession != (idutil.SessionInfo{}) {
  1109  			return cachedCurrentSession, false, nil
  1110  		}
  1111  
  1112  		if !doRPC {
  1113  			// Wait for another goroutine to finish the RPC.
  1114  			select {
  1115  			case <-inProgressCh:
  1116  			case <-ctx.Done():
  1117  				return idutil.SessionInfo{}, false, ctx.Err()
  1118  			}
  1119  		}
  1120  	}
  1121  
  1122  	var s idutil.SessionInfo
  1123  	// Close and clear the in-progress channel, even on an error.
  1124  	defer func() {
  1125  		k.sessionCacheLock.Lock()
  1126  		defer k.sessionCacheLock.Unlock()
  1127  		k.cachedCurrentSession = s
  1128  		close(k.sessionInProgressCh)
  1129  		k.sessionInProgressCh = nil
  1130  	}()
  1131  
  1132  	res, err := k.sessionClient.CurrentSession(ctx, sessionID)
  1133  	if err != nil {
  1134  		if _, ok := err.(libkb.NoSessionError); ok {
  1135  			// Use an error with a proper OS error code attached to it.
  1136  			err = idutil.NoCurrentSessionError{}
  1137  		}
  1138  		return idutil.SessionInfo{}, false, err
  1139  	}
  1140  	s, err = idutil.SessionInfoFromProtocol(res)
  1141  	if err != nil {
  1142  		return idutil.SessionInfo{}, false, err
  1143  	}
  1144  
  1145  	k.log.CDebugf(
  1146  		ctx, "new session with username %s, uid %s, crypt public key %s, and verifying key %s",
  1147  		s.Name, s.UID, s.CryptPublicKey, s.VerifyingKey)
  1148  	return s, true, nil
  1149  }
  1150  
  1151  // CurrentSession implements the KeybaseService interface for KeybaseServiceBase.
  1152  func (k *KeybaseServiceBase) CurrentSession(
  1153  	ctx context.Context, sessionID int) (
  1154  	idutil.SessionInfo, error) {
  1155  	ctx = CtxWithRandomIDReplayable(
  1156  		ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log)
  1157  
  1158  	s, newSession, err := k.getCurrentSession(ctx, sessionID)
  1159  	if err != nil {
  1160  		return idutil.SessionInfo{}, err
  1161  	}
  1162  
  1163  	if newSession && k.config != nil {
  1164  		// Don't hold the lock while calling `serviceLoggedIn`.
  1165  		_ = serviceLoggedIn(ctx, k.config, s, TLFJournalBackgroundWorkEnabled)
  1166  	}
  1167  
  1168  	return s, nil
  1169  }
  1170  
  1171  // FavoriteAdd implements the KeybaseService interface for KeybaseServiceBase.
  1172  func (k *KeybaseServiceBase) FavoriteAdd(ctx context.Context, folder keybase1.FolderHandle) error {
  1173  	return k.favoriteClient.FavoriteAdd(ctx, keybase1.FavoriteAddArg{Folder: folder})
  1174  }
  1175  
  1176  // FavoriteDelete implements the KeybaseService interface for KeybaseServiceBase.
  1177  func (k *KeybaseServiceBase) FavoriteDelete(ctx context.Context, folder keybase1.FolderHandle) error {
  1178  	return k.favoriteClient.FavoriteIgnore(ctx,
  1179  		keybase1.FavoriteIgnoreArg{Folder: folder})
  1180  }
  1181  
  1182  // FavoriteList implements the KeybaseService interface for KeybaseServiceBase.
  1183  func (k *KeybaseServiceBase) FavoriteList(ctx context.Context,
  1184  	sessionID int) (keybase1.FavoritesResult, error) {
  1185  	return k.favoriteClient.GetFavorites(ctx, sessionID)
  1186  }
  1187  
  1188  // EncryptFavorites encrypts cached favorites to store on disk.
  1189  func (k *KeybaseServiceBase) EncryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) {
  1190  	return k.kbfsClient.EncryptFavorites(ctx, dataToEncrypt)
  1191  }
  1192  
  1193  // DecryptFavorites decrypts cached favorites stored on disk.
  1194  func (k *KeybaseServiceBase) DecryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) {
  1195  	return k.kbfsClient.DecryptFavorites(ctx, dataToEncrypt)
  1196  }
  1197  
  1198  // NotifyOnlineStatusChanged implements the KeybaseService interface for
  1199  // KeybaseServiceBase.
  1200  func (k *KeybaseServiceBase) NotifyOnlineStatusChanged(ctx context.Context,
  1201  	online bool) error {
  1202  	k.log.CDebugf(ctx, "Sending notification for onlineStatus: online=%v", online)
  1203  	return k.kbfsClient.FSOnlineStatusChangedEvent(ctx, online)
  1204  }
  1205  
  1206  // Notify implements the KeybaseService interface for KeybaseServiceBase.
  1207  func (k *KeybaseServiceBase) Notify(ctx context.Context, notification *keybase1.FSNotification) error {
  1208  	return k.kbfsClient.FSEvent(ctx, *notification)
  1209  }
  1210  
  1211  // NotifyPathUpdated implements the KeybaseService interface for
  1212  // KeybaseServiceBase.
  1213  func (k *KeybaseServiceBase) NotifyPathUpdated(
  1214  	ctx context.Context, path string) error {
  1215  	return k.kbfsClient.FSPathUpdate(ctx, path)
  1216  }
  1217  
  1218  // NotifySyncStatus implements the KeybaseService interface for
  1219  // KeybaseServiceBase.
  1220  func (k *KeybaseServiceBase) NotifySyncStatus(ctx context.Context,
  1221  	status *keybase1.FSPathSyncStatus) error {
  1222  	return k.kbfsClient.FSSyncEvent(ctx, *status)
  1223  }
  1224  
  1225  // NotifyOverallSyncStatus implements the KeybaseService interface for
  1226  // KeybaseServiceBase.
  1227  func (k *KeybaseServiceBase) NotifyOverallSyncStatus(
  1228  	ctx context.Context, status keybase1.FolderSyncStatus) error {
  1229  	return k.kbfsClient.FSOverallSyncEvent(ctx, status)
  1230  }
  1231  
  1232  // NotifyFavoritesChanged implements the KeybaseService interface for
  1233  // KeybaseServiceBase.
  1234  func (k *KeybaseServiceBase) NotifyFavoritesChanged(ctx context.Context) error {
  1235  	return k.kbfsClient.FSFavoritesChangedEvent(ctx)
  1236  }
  1237  
  1238  // OnPathChange implements the SubscriptionNotifier interface.
  1239  func (k *KeybaseServiceBase) OnPathChange(
  1240  	clientID SubscriptionManagerClientID,
  1241  	subscriptionIDs []SubscriptionID, path string,
  1242  	topics []keybase1.PathSubscriptionTopic) {
  1243  	subscriptionIDStrings := make([]string, 0, len(subscriptionIDs))
  1244  	for _, sid := range subscriptionIDs {
  1245  		subscriptionIDStrings = append(subscriptionIDStrings, string(sid))
  1246  	}
  1247  	err := k.kbfsClient.FSSubscriptionNotifyPathEvent(
  1248  		context.Background(), keybase1.FSSubscriptionNotifyPathEventArg{
  1249  			ClientID:        string(clientID),
  1250  			SubscriptionIDs: subscriptionIDStrings,
  1251  			Path:            path,
  1252  			Topics:          topics,
  1253  		})
  1254  	if err != nil {
  1255  		k.log.CDebugf(
  1256  			context.TODO(), "Couldn't send path change notification: %+v", err)
  1257  	}
  1258  }
  1259  
  1260  // OnNonPathChange implements the SubscriptionNotifier interface.
  1261  func (k *KeybaseServiceBase) OnNonPathChange(
  1262  	clientID SubscriptionManagerClientID,
  1263  	subscriptionIDs []SubscriptionID, topic keybase1.SubscriptionTopic) {
  1264  	subscriptionIDStrings := make([]string, 0, len(subscriptionIDs))
  1265  	for _, sid := range subscriptionIDs {
  1266  		subscriptionIDStrings = append(subscriptionIDStrings, string(sid))
  1267  	}
  1268  	err := k.kbfsClient.FSSubscriptionNotifyEvent(context.Background(),
  1269  		keybase1.FSSubscriptionNotifyEventArg{
  1270  			ClientID:        string(clientID),
  1271  			SubscriptionIDs: subscriptionIDStrings,
  1272  			Topic:           topic,
  1273  		})
  1274  	if err != nil {
  1275  		k.log.CDebugf(
  1276  			context.TODO(),
  1277  			"Couldn't send non-path change notification: %+v", err)
  1278  	}
  1279  }
  1280  
  1281  // FlushUserFromLocalCache implements the KeybaseService interface for
  1282  // KeybaseServiceBase.
  1283  func (k *KeybaseServiceBase) FlushUserFromLocalCache(ctx context.Context,
  1284  	uid keybase1.UID) {
  1285  	k.log.CDebugf(ctx, "Flushing cache for user %s", uid)
  1286  	k.setCachedUserInfo(uid, idutil.UserInfo{})
  1287  }
  1288  
  1289  // CtxKeybaseServiceTagKey is the type used for unique context tags
  1290  // used while servicing incoming keybase requests.
  1291  type CtxKeybaseServiceTagKey int
  1292  
  1293  const (
  1294  	// CtxKeybaseServiceIDKey is the type of the tag for unique
  1295  	// operation IDs used while servicing incoming keybase requests.
  1296  	CtxKeybaseServiceIDKey CtxKeybaseServiceTagKey = iota
  1297  )
  1298  
  1299  // CtxKeybaseServiceOpID is the display name for the unique operation
  1300  // enqueued rekey ID tag.
  1301  const CtxKeybaseServiceOpID = "KSID"
  1302  
  1303  // FSEditListRequest implements keybase1.NotifyFSRequestInterface for
  1304  // KeybaseServiceBase.
  1305  func (k *KeybaseServiceBase) FSEditListRequest(ctx context.Context,
  1306  	req keybase1.FSEditListRequest) (err error) {
  1307  	ctx = CtxWithRandomIDReplayable(ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID,
  1308  		k.log)
  1309  	k.log.CDebugf(ctx, "Edit list request for %s (public: %t)",
  1310  		req.Folder.Name, !req.Folder.Private)
  1311  	tlfHandle, err := getHandleFromFolderName(
  1312  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, req.Folder.Name,
  1313  		!req.Folder.Private)
  1314  	if err != nil {
  1315  		return err
  1316  	}
  1317  
  1318  	rootNode, _, err := k.config.KBFSOps().
  1319  		GetOrCreateRootNode(ctx, tlfHandle, data.MasterBranch)
  1320  	if err != nil {
  1321  		return err
  1322  	}
  1323  	history, err := k.config.KBFSOps().GetEditHistory(ctx,
  1324  		rootNode.GetFolderBranch())
  1325  	if err != nil {
  1326  		return err
  1327  	}
  1328  
  1329  	// TODO(KBFS-2996) Convert the edits to an RPC response.
  1330  	resp := keybase1.FSEditListArg{
  1331  		RequestID: req.RequestID,
  1332  		Edits:     history,
  1333  	}
  1334  
  1335  	k.log.CDebugf(ctx, "Sending edit history response with %d writer clusters",
  1336  		len(resp.Edits.History))
  1337  	return k.kbfsClient.FSEditList(ctx, resp)
  1338  }
  1339  
  1340  // FSSyncStatusRequest implements keybase1.NotifyFSRequestInterface for
  1341  // KeybaseServiceBase.
  1342  func (k *KeybaseServiceBase) FSSyncStatusRequest(ctx context.Context,
  1343  	req keybase1.FSSyncStatusRequest) (err error) {
  1344  	k.log.CDebugf(ctx, "Got sync status request: %v", req)
  1345  
  1346  	resp := keybase1.FSSyncStatusArg{RequestID: req.RequestID}
  1347  
  1348  	// For now, just return the number of syncing bytes.
  1349  	jManager, err := GetJournalManager(k.config)
  1350  	if err == nil {
  1351  		status, _ := jManager.Status(ctx)
  1352  		resp.Status.TotalSyncingBytes = status.UnflushedBytes
  1353  		k.log.CDebugf(ctx, "Sending sync status response with %d syncing bytes",
  1354  			status.UnflushedBytes)
  1355  	} else {
  1356  		k.log.CDebugf(ctx, "No journal server, sending empty response")
  1357  	}
  1358  
  1359  	return k.kbfsClient.FSSyncStatus(ctx, resp)
  1360  }
  1361  
  1362  // TeamChangedByID implements keybase1.NotifyTeamInterface for
  1363  // KeybaseServiceBase.
  1364  func (k *KeybaseServiceBase) TeamChangedByID(ctx context.Context,
  1365  	arg keybase1.TeamChangedByIDArg) error {
  1366  	k.log.CDebugf(ctx, "Flushing cache for team %s "+
  1367  		"(membershipChange=%t, keyRotated=%t, renamed=%t)",
  1368  		arg.TeamID, arg.Changes.MembershipChanged,
  1369  		arg.Changes.KeyRotated, arg.Changes.Renamed)
  1370  	k.setCachedTeamInfo(arg.TeamID, idutil.TeamInfo{})
  1371  
  1372  	if arg.Changes.Renamed {
  1373  		k.config.KBFSOps().TeamNameChanged(ctx, arg.TeamID)
  1374  	}
  1375  	return nil
  1376  }
  1377  
  1378  // TeamChangedByName implements keybase1.NotifyTeamInterface for
  1379  // KeybaseServiceBase.
  1380  func (k *KeybaseServiceBase) TeamChangedByName(ctx context.Context,
  1381  	arg keybase1.TeamChangedByNameArg) error {
  1382  	// ignore
  1383  	return nil
  1384  }
  1385  
  1386  // TeamDeleted implements keybase1.NotifyTeamInterface for
  1387  // KeybaseServiceBase.
  1388  func (k *KeybaseServiceBase) TeamDeleted(ctx context.Context,
  1389  	teamID keybase1.TeamID) error {
  1390  	return nil
  1391  }
  1392  
  1393  // TeamExit implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1394  func (k *KeybaseDaemonRPC) TeamExit(context.Context, keybase1.TeamID) error {
  1395  	return nil
  1396  }
  1397  
  1398  // TeamRoleMapChanged implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1399  func (k *KeybaseDaemonRPC) TeamRoleMapChanged(context.Context, keybase1.UserTeamVersion) error {
  1400  	return nil
  1401  }
  1402  
  1403  // NewlyAddedToTeam implements keybase1.NotifyTeamInterface for
  1404  // KeybaseServiceBase.
  1405  func (k *KeybaseDaemonRPC) NewlyAddedToTeam(context.Context, keybase1.TeamID) error {
  1406  	return nil
  1407  }
  1408  
  1409  // TeamMetadataUpdate implements keybase1.NotifyTeamInterface for
  1410  // KeybaseServiceBase.
  1411  func (k *KeybaseDaemonRPC) TeamMetadataUpdate(context.Context) error {
  1412  	return nil
  1413  }
  1414  
  1415  // TeamAbandoned implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1416  func (k *KeybaseDaemonRPC) TeamAbandoned(
  1417  	ctx context.Context, tid keybase1.TeamID) error {
  1418  	k.log.CDebugf(ctx, "Implicit team %s abandoned", tid)
  1419  	k.setCachedTeamInfo(tid, idutil.TeamInfo{})
  1420  	k.config.KBFSOps().TeamAbandoned(ctx, tid)
  1421  	return nil
  1422  }
  1423  
  1424  // AvatarUpdated implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1425  func (k *KeybaseDaemonRPC) AvatarUpdated(ctx context.Context,
  1426  	arg keybase1.AvatarUpdatedArg) error {
  1427  	return nil
  1428  }
  1429  
  1430  // TeamTreeMembershipsPartial implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1431  func (k *KeybaseDaemonRPC) TeamTreeMembershipsPartial(context.Context,
  1432  	keybase1.TeamTreeMembership) error {
  1433  	return nil
  1434  }
  1435  
  1436  // TeamTreeMembershipsDone implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1437  func (k *KeybaseDaemonRPC) TeamTreeMembershipsDone(context.Context,
  1438  	keybase1.TeamTreeMembershipsDoneResult) error {
  1439  	return nil
  1440  }
  1441  
  1442  // StartMigration implements keybase1.ImplicitTeamMigrationInterface for
  1443  // KeybaseServiceBase.
  1444  func (k *KeybaseServiceBase) StartMigration(ctx context.Context,
  1445  	folder keybase1.Folder) (err error) {
  1446  	mdServer := k.config.MDServer()
  1447  	if mdServer == nil {
  1448  		return errors.New("no mdserver")
  1449  	}
  1450  	// Making a favorite here to reuse the code that converts from
  1451  	// `keybase1.FolderType` into `tlf.Type`.
  1452  	fav := favorites.NewFolderFromProtocol(folder)
  1453  	handle, err := GetHandleFromFolderNameAndType(
  1454  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type)
  1455  	if err != nil {
  1456  		return err
  1457  	}
  1458  	// Before taking the lock, first make sure this device can handle
  1459  	// the migration.
  1460  	tlfID := handle.TlfID()
  1461  	err = k.config.KBFSOps().CheckMigrationPerms(ctx, tlfID)
  1462  	if err != nil {
  1463  		k.log.CDebugf(ctx, "This device cannot migrate %s: %+v", tlfID, err)
  1464  		return err
  1465  	}
  1466  	return k.config.MDServer().StartImplicitTeamMigration(ctx, tlfID)
  1467  }
  1468  
  1469  // FinalizeMigration implements keybase1.ImplicitTeamMigrationInterface for
  1470  // KeybaseServiceBase.
  1471  func (k *KeybaseServiceBase) FinalizeMigration(ctx context.Context,
  1472  	folder keybase1.Folder) (err error) {
  1473  	fav := favorites.NewFolderFromProtocol(folder)
  1474  	handle, err := GetHandleFromFolderNameAndType(
  1475  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type)
  1476  	if err != nil {
  1477  		return err
  1478  	}
  1479  	if handle.TypeForKeying() == tlf.TeamKeying {
  1480  		// Clear the cache for this implicit team, to ensure we get
  1481  		// all the latest key generations for the team info during the
  1482  		// migration.
  1483  		id := handle.FirstResolvedWriter()
  1484  		if id.IsTeamOrSubteam() {
  1485  			tid, err := id.AsTeam()
  1486  			if err != nil {
  1487  				return err
  1488  			}
  1489  			k.log.CDebugf(ctx, "Clearing team info for tid=%s, handle=%s",
  1490  				tid, handle.GetCanonicalPath())
  1491  			k.setCachedTeamInfo(tid, idutil.TeamInfo{})
  1492  		}
  1493  	}
  1494  	return k.config.KBFSOps().MigrateToImplicitTeam(ctx, handle.TlfID())
  1495  }
  1496  
  1497  // GetTLFCryptKeys implements the TlfKeysInterface interface for
  1498  // KeybaseServiceBase.
  1499  func (k *KeybaseServiceBase) GetTLFCryptKeys(ctx context.Context,
  1500  	query keybase1.TLFQuery) (res keybase1.GetTLFCryptKeysRes, err error) {
  1501  	if ctx, err = tlfhandle.MakeExtendedIdentify(
  1502  		CtxWithRandomIDReplayable(ctx,
  1503  			CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log),
  1504  		query.IdentifyBehavior,
  1505  	); err != nil {
  1506  		return keybase1.GetTLFCryptKeysRes{}, err
  1507  	}
  1508  
  1509  	tlfHandle, err := getHandleFromFolderName(
  1510  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName, false)
  1511  	if err != nil {
  1512  		return res, err
  1513  	}
  1514  
  1515  	res.NameIDBreaks.CanonicalName = keybase1.CanonicalTlfName(
  1516  		tlfHandle.GetCanonicalName())
  1517  
  1518  	keys, id, err := k.config.KBFSOps().GetTLFCryptKeys(ctx, tlfHandle)
  1519  	if err != nil {
  1520  		return res, err
  1521  	}
  1522  	res.NameIDBreaks.TlfID = keybase1.TLFID(id.String())
  1523  
  1524  	for i, key := range keys {
  1525  		res.CryptKeys = append(res.CryptKeys, keybase1.CryptKey{
  1526  			KeyGeneration: int(kbfsmd.FirstValidKeyGen) + i,
  1527  			Key:           keybase1.Bytes32(key.Data()),
  1528  		})
  1529  	}
  1530  
  1531  	if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
  1532  		res.NameIDBreaks.Breaks = tlfhandle.GetExtendedIdentify(ctx).
  1533  			GetTlfBreakAndClose()
  1534  	}
  1535  
  1536  	return res, nil
  1537  }
  1538  
  1539  // GetPublicCanonicalTLFNameAndID implements the TlfKeysInterface interface for
  1540  // KeybaseServiceBase.
  1541  func (k *KeybaseServiceBase) GetPublicCanonicalTLFNameAndID(
  1542  	ctx context.Context, query keybase1.TLFQuery) (
  1543  	res keybase1.CanonicalTLFNameAndIDWithBreaks, err error) {
  1544  	if ctx, err = tlfhandle.MakeExtendedIdentify(
  1545  		CtxWithRandomIDReplayable(ctx,
  1546  			CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log),
  1547  		query.IdentifyBehavior,
  1548  	); err != nil {
  1549  		return keybase1.CanonicalTLFNameAndIDWithBreaks{}, err
  1550  	}
  1551  
  1552  	tlfHandle, err := getHandleFromFolderName(
  1553  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName,
  1554  		true /* public */)
  1555  	if err != nil {
  1556  		return res, err
  1557  	}
  1558  
  1559  	res.CanonicalName = keybase1.CanonicalTlfName(
  1560  		tlfHandle.GetCanonicalName())
  1561  
  1562  	id, err := k.config.KBFSOps().GetTLFID(ctx, tlfHandle)
  1563  	if err != nil {
  1564  		return res, err
  1565  	}
  1566  	res.TlfID = keybase1.TLFID(id.String())
  1567  
  1568  	if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
  1569  		res.Breaks = tlfhandle.GetExtendedIdentify(ctx).GetTlfBreakAndClose()
  1570  	}
  1571  
  1572  	return res, nil
  1573  }
  1574  
  1575  // EstablishMountDir asks the service for the current mount path
  1576  func (k *KeybaseServiceBase) EstablishMountDir(ctx context.Context) (
  1577  	string, error) {
  1578  	dir, err := k.kbfsMountClient.GetCurrentMountDir(ctx)
  1579  	if err != nil {
  1580  		k.log.CInfof(ctx, "GetCurrentMountDir fails - ", err)
  1581  		return "", err
  1582  	}
  1583  	if dir == "" {
  1584  		dirs, err := k.kbfsMountClient.GetAllAvailableMountDirs(ctx)
  1585  		if err != nil {
  1586  			k.log.CInfof(ctx, "GetAllAvailableMountDirs fails - ", err)
  1587  			return "", err
  1588  		}
  1589  		dir, err = chooseDefaultMount(ctx, dirs, k.log)
  1590  		if err != nil {
  1591  			k.log.CInfof(ctx, "chooseDefaultMount fails - ", err)
  1592  			return "", err
  1593  		}
  1594  		err2 := k.kbfsMountClient.SetCurrentMountDir(ctx, dir)
  1595  		if err2 != nil {
  1596  			k.log.CInfof(ctx, "SetCurrentMountDir fails - ", err2)
  1597  		}
  1598  		// Continue mounting even if we can't save the mount
  1599  		k.log.CDebugf(ctx, "Choosing mountdir %s from %v", dir, dirs)
  1600  	}
  1601  	return dir, err
  1602  }
  1603  
  1604  // PutGitMetadata implements the KeybaseService interface for
  1605  // KeybaseServiceBase.
  1606  func (k *KeybaseServiceBase) PutGitMetadata(
  1607  	ctx context.Context, folder keybase1.FolderHandle, repoID keybase1.RepoID,
  1608  	metadata keybase1.GitLocalMetadata) error {
  1609  	return k.gitClient.PutGitMetadata(ctx, keybase1.PutGitMetadataArg{
  1610  		Folder:   folder,
  1611  		RepoID:   repoID,
  1612  		Metadata: metadata,
  1613  	})
  1614  }
  1615  
  1616  // GetKVStoreClient implements the KeybaseService interface for
  1617  // KeybaseServiceBase.
  1618  func (k *KeybaseServiceBase) GetKVStoreClient() keybase1.KvstoreInterface {
  1619  	return k.kvstoreClient
  1620  }
  1621  
  1622  // GetKeybaseDaemonRawClient implements the KeybaseService interface for
  1623  // KeybaseServiceBase.
  1624  func (k *KeybaseServiceBase) GetKeybaseDaemonRawClient() rpc.GenericClient {
  1625  	return nil
  1626  }