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 }