github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/keybase_daemon_local.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  	"sync"
    10  
    11  	"golang.org/x/net/context"
    12  
    13  	"github.com/keybase/client/go/kbfs/idutil"
    14  	"github.com/keybase/client/go/kbfs/kbfscodec"
    15  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    16  	"github.com/keybase/client/go/kbfs/ldbutils"
    17  	kbname "github.com/keybase/client/go/kbun"
    18  	"github.com/keybase/client/go/protocol/keybase1"
    19  	"github.com/syndtr/goleveldb/leveldb"
    20  	"github.com/syndtr/goleveldb/leveldb/util"
    21  )
    22  
    23  type favoriteStore interface {
    24  	FavoriteAdd(uid keybase1.UID, folder keybase1.FolderHandle) error
    25  	FavoriteDelete(uid keybase1.UID, folder keybase1.FolderHandle) error
    26  	FavoriteList(uid keybase1.UID) ([]keybase1.Folder, error)
    27  
    28  	Shutdown()
    29  }
    30  
    31  type diskFavoriteClient struct {
    32  	favoriteDb *leveldb.DB
    33  	codec      kbfscodec.Codec
    34  }
    35  
    36  var _ favoriteStore = diskFavoriteClient{}
    37  
    38  func (c diskFavoriteClient) favkey(
    39  	uid keybase1.UID, folder keybase1.FolderHandle) []byte {
    40  	return []byte(fmt.Sprintf("%s:%s", uid, folder.ToString()))
    41  }
    42  
    43  func (c diskFavoriteClient) FavoriteAdd(
    44  	uid keybase1.UID, folder keybase1.FolderHandle) error {
    45  	enc, err := c.codec.Encode(folder)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	return c.favoriteDb.Put(c.favkey(uid, folder), enc, nil)
    51  }
    52  
    53  func (c diskFavoriteClient) FavoriteDelete(
    54  	uid keybase1.UID, folder keybase1.FolderHandle) error {
    55  	return c.favoriteDb.Delete(c.favkey(uid, folder), nil)
    56  }
    57  
    58  func (c diskFavoriteClient) FavoriteList(uid keybase1.UID) (
    59  	[]keybase1.Folder, error) {
    60  	iter := c.favoriteDb.NewIterator(util.BytesPrefix([]byte(uid+":")), nil)
    61  	defer iter.Release()
    62  	var folders []keybase1.Folder
    63  	for iter.Next() {
    64  		var folder keybase1.Folder
    65  		if err := c.codec.Decode(iter.Value(), &folder); err != nil {
    66  			return nil, err
    67  		}
    68  		folders = append(folders, folder)
    69  	}
    70  	if err := iter.Error(); err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	return folders, nil
    75  }
    76  
    77  func (c diskFavoriteClient) Shutdown() {
    78  	c.favoriteDb.Close()
    79  }
    80  
    81  type memoryFavoriteClient struct {
    82  	favorites map[keybase1.UID]map[string]keybase1.FolderHandle
    83  }
    84  
    85  var _ favoriteStore = memoryFavoriteClient{}
    86  
    87  func (c memoryFavoriteClient) FavoriteAdd(
    88  	uid keybase1.UID, folder keybase1.FolderHandle) error {
    89  	if c.favorites[uid] == nil {
    90  		c.favorites[uid] = make(map[string]keybase1.FolderHandle)
    91  	}
    92  	c.favorites[uid][folder.ToString()] = folder
    93  	return nil
    94  }
    95  
    96  func (c memoryFavoriteClient) FavoriteDelete(
    97  	uid keybase1.UID, folder keybase1.FolderHandle) error {
    98  	if c.favorites[uid] != nil {
    99  		delete(c.favorites[uid], folder.ToString())
   100  	}
   101  	return nil
   102  }
   103  
   104  func (c memoryFavoriteClient) FavoriteList(
   105  	uid keybase1.UID) ([]keybase1.Folder, error) {
   106  	folders := make([]keybase1.Folder, len(c.favorites[uid]))
   107  	i := 0
   108  	for _, v := range c.favorites[uid] {
   109  		folders[i] = keybase1.Folder{
   110  			Name:       v.Name,
   111  			FolderType: v.FolderType,
   112  		}
   113  		i++
   114  	}
   115  	return folders, nil
   116  }
   117  
   118  func (c memoryFavoriteClient) Shutdown() {}
   119  
   120  // KeybaseDaemonLocal implements KeybaseDaemon using an in-memory user
   121  // and session store, and a given favorite store.
   122  type KeybaseDaemonLocal struct {
   123  	*idutil.DaemonLocal
   124  
   125  	// lock protects everything below.
   126  	lock          sync.Mutex
   127  	favoriteStore favoriteStore
   128  }
   129  
   130  var _ KeybaseService = &KeybaseDaemonLocal{}
   131  
   132  type makeKeysFunc func(kbname.NormalizedUsername, int) (
   133  	kbfscrypto.CryptPublicKey, kbfscrypto.VerifyingKey)
   134  
   135  func (k *KeybaseDaemonLocal) addDeviceForTesting(uid keybase1.UID,
   136  	makeKeys makeKeysFunc) (int, error) {
   137  	k.lock.Lock()
   138  	defer k.lock.Unlock()
   139  
   140  	user, err := k.GetLocalUser(uid)
   141  	if err != nil {
   142  		return 0, fmt.Errorf("No such user %s: %v", uid, err)
   143  	}
   144  	user = user.DeepCopy()
   145  
   146  	index := len(user.VerifyingKeys)
   147  	newCryptPublicKey, newVerifyingKey := makeKeys(user.Name, index)
   148  	user.VerifyingKeys = append(user.VerifyingKeys, newVerifyingKey)
   149  	user.CryptPublicKeys = append(user.CryptPublicKeys, newCryptPublicKey)
   150  
   151  	k.SetLocalUser(uid, user)
   152  	return index, nil
   153  }
   154  
   155  func (k *KeybaseDaemonLocal) revokeDeviceForTesting(clock Clock,
   156  	uid keybase1.UID, index int) error {
   157  	k.lock.Lock()
   158  	defer k.lock.Unlock()
   159  
   160  	user, err := k.GetLocalUser(uid)
   161  	if err != nil {
   162  		return fmt.Errorf("No such user %s: %v", uid, err)
   163  	}
   164  	user = user.DeepCopy()
   165  
   166  	session, err := k.CurrentSession(context.TODO(), 0)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	if index >= len(user.VerifyingKeys) ||
   172  		(session.UID == uid && index == user.CurrentCryptPublicKeyIndex) {
   173  		return fmt.Errorf("Can't revoke index %d", index)
   174  	}
   175  
   176  	if user.RevokedVerifyingKeys == nil {
   177  		user.RevokedVerifyingKeys =
   178  			make(map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo)
   179  	}
   180  	if user.RevokedCryptPublicKeys == nil {
   181  		user.RevokedCryptPublicKeys =
   182  			make(map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo)
   183  	}
   184  
   185  	kbtime := keybase1.ToTime(clock.Now())
   186  	info := idutil.RevokedKeyInfo{
   187  		Time: kbtime,
   188  		MerkleRoot: keybase1.MerkleRootV2{
   189  			Seqno: 1,
   190  		},
   191  	}
   192  	user.RevokedVerifyingKeys[user.VerifyingKeys[index]] = info
   193  	user.RevokedCryptPublicKeys[user.CryptPublicKeys[index]] = info
   194  
   195  	user.VerifyingKeys = append(user.VerifyingKeys[:index],
   196  		user.VerifyingKeys[index+1:]...)
   197  	user.CryptPublicKeys = append(user.CryptPublicKeys[:index],
   198  		user.CryptPublicKeys[index+1:]...)
   199  
   200  	if session.UID == uid && index < user.CurrentCryptPublicKeyIndex {
   201  		user.CurrentCryptPublicKeyIndex--
   202  	}
   203  	if session.UID == uid && index < user.CurrentVerifyingKeyIndex {
   204  		user.CurrentVerifyingKeyIndex--
   205  	}
   206  
   207  	k.SetLocalUser(uid, user)
   208  	return nil
   209  }
   210  
   211  func (k *KeybaseDaemonLocal) switchDeviceForTesting(uid keybase1.UID,
   212  	index int) error {
   213  	k.lock.Lock()
   214  	defer k.lock.Unlock()
   215  
   216  	user, err := k.GetLocalUser(uid)
   217  	if err != nil {
   218  		return fmt.Errorf("No such user %s: %v", uid, err)
   219  	}
   220  
   221  	if index >= len(user.CryptPublicKeys) {
   222  		return fmt.Errorf("Wrong crypt public key index: %d", index)
   223  	}
   224  	user.CurrentCryptPublicKeyIndex = index
   225  
   226  	if index >= len(user.VerifyingKeys) {
   227  		return fmt.Errorf("Wrong verifying key index: %d", index)
   228  	}
   229  	user.CurrentVerifyingKeyIndex = index
   230  
   231  	k.SetLocalUser(uid, user)
   232  	return nil
   233  }
   234  
   235  func (k *KeybaseDaemonLocal) addTeamWriterForTest(
   236  	tid keybase1.TeamID, uid keybase1.UID) error {
   237  	k.lock.Lock()
   238  	defer k.lock.Unlock()
   239  	teamName, isImplicit, err := k.DaemonLocal.AddTeamWriterForTest(tid, uid)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	if !isImplicit {
   244  		f := keybase1.FolderHandle{
   245  			Name:       string(teamName),
   246  			FolderType: keybase1.FolderType_TEAM,
   247  		}
   248  		err := k.favoriteStore.FavoriteAdd(uid, f)
   249  		if err != nil {
   250  			return err
   251  		}
   252  	}
   253  	return nil
   254  }
   255  
   256  func (k *KeybaseDaemonLocal) removeTeamWriterForTest(
   257  	tid keybase1.TeamID, uid keybase1.UID) error {
   258  	k.lock.Lock()
   259  	defer k.lock.Unlock()
   260  	teamName, err := k.DaemonLocal.RemoveTeamWriterForTest(tid, uid)
   261  	if err != nil {
   262  		return err
   263  	}
   264  	f := keybase1.FolderHandle{
   265  		Name:       string(teamName),
   266  		FolderType: keybase1.FolderType_TEAM,
   267  	}
   268  	return k.favoriteStore.FavoriteDelete(uid, f)
   269  }
   270  
   271  func (k *KeybaseDaemonLocal) addTeamReaderForTest(
   272  	tid keybase1.TeamID, uid keybase1.UID) error {
   273  	k.lock.Lock()
   274  	defer k.lock.Unlock()
   275  	teamName, err := k.DaemonLocal.AddTeamReaderForTest(tid, uid)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	f := keybase1.FolderHandle{
   280  		Name:       string(teamName),
   281  		FolderType: keybase1.FolderType_TEAM,
   282  	}
   283  	return k.favoriteStore.FavoriteAdd(uid, f)
   284  }
   285  
   286  func (k *KeybaseDaemonLocal) addTeamsForTestLocked(teams []idutil.TeamInfo) {
   287  	k.DaemonLocal.AddTeamsForTest(teams)
   288  	for _, t := range teams {
   289  		f := keybase1.FolderHandle{
   290  			Name:       string(t.Name),
   291  			FolderType: keybase1.FolderType_TEAM,
   292  		}
   293  		for u := range t.Writers {
   294  			_ = k.favoriteStore.FavoriteAdd(u, f)
   295  		}
   296  		for u := range t.Readers {
   297  			_ = k.favoriteStore.FavoriteAdd(u, f)
   298  		}
   299  	}
   300  }
   301  
   302  func (k *KeybaseDaemonLocal) addTeamsForTest(teams []idutil.TeamInfo) {
   303  	k.lock.Lock()
   304  	defer k.lock.Unlock()
   305  	k.addTeamsForTestLocked(teams)
   306  }
   307  
   308  // FavoriteAdd implements KeybaseDaemon for KeybaseDaemonLocal.
   309  func (k *KeybaseDaemonLocal) FavoriteAdd(
   310  	ctx context.Context, folder keybase1.FolderHandle) error {
   311  	if err := checkContext(ctx); err != nil {
   312  		return err
   313  	}
   314  
   315  	k.lock.Lock()
   316  	defer k.lock.Unlock()
   317  	session, err := k.CurrentSession(ctx, 0)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	return k.favoriteStore.FavoriteAdd(session.UID, folder)
   322  }
   323  
   324  // FavoriteDelete implements KeybaseDaemon for KeybaseDaemonLocal.
   325  func (k *KeybaseDaemonLocal) FavoriteDelete(
   326  	ctx context.Context, folder keybase1.FolderHandle) error {
   327  	if err := checkContext(ctx); err != nil {
   328  		return err
   329  	}
   330  
   331  	k.lock.Lock()
   332  	defer k.lock.Unlock()
   333  	session, err := k.CurrentSession(ctx, 0)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	return k.favoriteStore.FavoriteDelete(session.UID, folder)
   338  }
   339  
   340  // FavoriteList implements KeybaseDaemon for KeybaseDaemonLocal.
   341  func (k *KeybaseDaemonLocal) FavoriteList(
   342  	ctx context.Context, sessionID int) (keybase1.FavoritesResult, error) {
   343  	if err := checkContext(ctx); err != nil {
   344  		return keybase1.FavoritesResult{}, err
   345  	}
   346  
   347  	k.lock.Lock()
   348  	defer k.lock.Unlock()
   349  
   350  	session, err := k.CurrentSession(ctx, 0)
   351  	if err != nil {
   352  		return keybase1.FavoritesResult{}, err
   353  	}
   354  
   355  	// This is only used for testing, so it's okay to only have favorites here.
   356  	favs, err := k.favoriteStore.FavoriteList(session.UID)
   357  	if err != nil {
   358  		return keybase1.FavoritesResult{}, err
   359  	}
   360  	return keybase1.FavoritesResult{
   361  		FavoriteFolders: favs,
   362  		IgnoredFolders:  []keybase1.Folder{},
   363  		NewFolders:      []keybase1.Folder{},
   364  	}, nil
   365  }
   366  
   367  // EncryptFavorites implements KeybaseService for KeybaseDaemonLocal
   368  func (k *KeybaseDaemonLocal) EncryptFavorites(ctx context.Context,
   369  	dataToEncrypt []byte) ([]byte, error) {
   370  	return nil, checkContext(ctx)
   371  }
   372  
   373  // DecryptFavorites implements KeybaseService for KeybaseDaemonLocal
   374  func (k *KeybaseDaemonLocal) DecryptFavorites(ctx context.Context,
   375  	dataToDecrypt []byte) ([]byte, error) {
   376  	return nil, checkContext(ctx)
   377  }
   378  
   379  // NotifyOnlineStatusChanged implements KeybaseDaemon for KeybaseDeamonLocal.
   380  func (k *KeybaseDaemonLocal) NotifyOnlineStatusChanged(ctx context.Context, online bool) error {
   381  	return checkContext(ctx)
   382  }
   383  
   384  // NotifyFavoritesChanged implements KeybaseDaemon for KeybaseDeamonLocal.
   385  func (k *KeybaseDaemonLocal) NotifyFavoritesChanged(ctx context.Context) error {
   386  	return checkContext(ctx)
   387  }
   388  
   389  // Notify implements KeybaseDaemon for KeybaseDeamonLocal.
   390  func (k *KeybaseDaemonLocal) Notify(ctx context.Context, notification *keybase1.FSNotification) error {
   391  	return checkContext(ctx)
   392  }
   393  
   394  // NotifyPathUpdated implements KeybaseDaemon for KeybaseDeamonLocal.
   395  func (k *KeybaseDaemonLocal) NotifyPathUpdated(
   396  	ctx context.Context, _ string) error {
   397  	return checkContext(ctx)
   398  }
   399  
   400  // NotifySyncStatus implements KeybaseDaemon for KeybaseDeamonLocal.
   401  func (k *KeybaseDaemonLocal) NotifySyncStatus(ctx context.Context,
   402  	_ *keybase1.FSPathSyncStatus) error {
   403  	return checkContext(ctx)
   404  }
   405  
   406  // NotifyOverallSyncStatus implements KeybaseDaemon for KeybaseDeamonLocal.
   407  func (k *KeybaseDaemonLocal) NotifyOverallSyncStatus(
   408  	ctx context.Context, _ keybase1.FolderSyncStatus) error {
   409  	return checkContext(ctx)
   410  }
   411  
   412  // FlushUserFromLocalCache implements the KeybaseDaemon interface for
   413  // KeybaseDaemonLocal.
   414  func (k *KeybaseDaemonLocal) FlushUserFromLocalCache(ctx context.Context,
   415  	uid keybase1.UID) {
   416  	// Do nothing.
   417  }
   418  
   419  // ClearCaches implements the KeybaseDaemon interface for
   420  // KeybaseDaemonLocal.
   421  func (k *KeybaseDaemonLocal) ClearCaches(_ context.Context) {
   422  	// Do nothing.
   423  }
   424  
   425  // EstablishMountDir implements the KeybaseDaemon interface for KeybaseDaemonLocal.
   426  func (k *KeybaseDaemonLocal) EstablishMountDir(ctx context.Context) (string, error) {
   427  	return "", nil
   428  }
   429  
   430  // PutGitMetadata implements the KeybaseService interface for
   431  // KeybaseDaemonLocal.
   432  func (k *KeybaseDaemonLocal) PutGitMetadata(
   433  	ctx context.Context, folder keybase1.FolderHandle, repoID keybase1.RepoID,
   434  	metadata keybase1.GitLocalMetadata) error {
   435  	return nil
   436  }
   437  
   438  // OnPathChange implements the SubscriptionNotifier interface.
   439  func (k *KeybaseDaemonLocal) OnPathChange(
   440  	clientID SubscriptionManagerClientID,
   441  	subscriptionIDs []SubscriptionID, path string, topics []keybase1.PathSubscriptionTopic) {
   442  }
   443  
   444  // OnNonPathChange implements the SubscriptionNotifier interface.
   445  func (k *KeybaseDaemonLocal) OnNonPathChange(
   446  	clientID SubscriptionManagerClientID,
   447  	subscriptionIDs []SubscriptionID, topic keybase1.SubscriptionTopic) {
   448  }
   449  
   450  // GetKVStoreClient implements the KeybaseService interface.
   451  func (k *KeybaseDaemonLocal) GetKVStoreClient() keybase1.KvstoreInterface {
   452  	return nil
   453  }
   454  
   455  // Shutdown implements KeybaseDaemon for KeybaseDaemonLocal.
   456  func (k *KeybaseDaemonLocal) Shutdown() {
   457  	k.favoriteStore.Shutdown()
   458  }
   459  
   460  func newKeybaseDaemonLocal(
   461  	codec kbfscodec.Codec, currentUID keybase1.UID, users []idutil.LocalUser,
   462  	teams []idutil.TeamInfo, favoriteStore favoriteStore) *KeybaseDaemonLocal {
   463  	k := &KeybaseDaemonLocal{
   464  		DaemonLocal:   idutil.NewDaemonLocal(currentUID, users, teams, codec),
   465  		favoriteStore: favoriteStore,
   466  	}
   467  	return k
   468  }
   469  
   470  // NewKeybaseDaemonDisk constructs a KeybaseDaemonLocal object given a
   471  // set of possible users, and one user that should be "logged in".
   472  // Any storage (e.g. the favorites) persists to disk.
   473  func NewKeybaseDaemonDisk(currentUID keybase1.UID, users []idutil.LocalUser,
   474  	teams []idutil.TeamInfo, favDBFile string, codec kbfscodec.Codec) (
   475  	*KeybaseDaemonLocal, error) {
   476  	favoriteDb, err := leveldb.OpenFile(favDBFile, ldbutils.LeveldbOptions(nil))
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  	favoriteStore := diskFavoriteClient{favoriteDb, codec}
   481  	return newKeybaseDaemonLocal(
   482  		codec, currentUID, users, teams, favoriteStore), nil
   483  }
   484  
   485  // NewKeybaseDaemonMemory constructs a KeybaseDaemonLocal object given
   486  // a set of possible users, and one user that should be "logged in".
   487  // Any storage (e.g. the favorites) is kept in memory only.
   488  func NewKeybaseDaemonMemory(currentUID keybase1.UID,
   489  	users []idutil.LocalUser, teams []idutil.TeamInfo,
   490  	codec kbfscodec.Codec) *KeybaseDaemonLocal {
   491  	favoriteStore := memoryFavoriteClient{
   492  		favorites: make(map[keybase1.UID]map[string]keybase1.FolderHandle),
   493  	}
   494  	return newKeybaseDaemonLocal(codec, currentUID, users, teams, favoriteStore)
   495  }