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