github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/idutil/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 idutil 6 7 import ( 8 "fmt" 9 "sync" 10 "time" 11 12 "golang.org/x/net/context" 13 14 "github.com/keybase/client/go/externals" 15 "github.com/keybase/client/go/kbfs/kbfscodec" 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 kbname "github.com/keybase/client/go/kbun" 20 "github.com/keybase/client/go/protocol/keybase1" 21 "github.com/pkg/errors" 22 ) 23 24 func checkContext(ctx context.Context) error { 25 select { 26 case <-ctx.Done(): 27 return errors.WithStack(ctx.Err()) 28 default: 29 return nil 30 } 31 } 32 33 type localUserMap map[keybase1.UID]LocalUser 34 35 func (m localUserMap) getLocalUser(uid keybase1.UID) (LocalUser, error) { 36 user, ok := m[uid] 37 if !ok { 38 return LocalUser{}, NoSuchUserError{uid.String()} 39 } 40 return user, nil 41 } 42 43 type localTeamMap map[keybase1.TeamID]TeamInfo 44 45 func (m localTeamMap) getLocalTeam(tid keybase1.TeamID) (TeamInfo, error) { 46 team, ok := m[tid] 47 if !ok { 48 return TeamInfo{}, NoSuchTeamError{tid.String()} 49 } 50 return team, nil 51 } 52 53 type localTeamSettingsMap map[keybase1.TeamID]keybase1.KBFSTeamSettings 54 55 type localImplicitTeamMap map[keybase1.TeamID]ImplicitTeamInfo 56 57 // DaemonLocal implements KeybaseDaemon using an in-memory user 58 // and session store, and a given favorite store. 59 type DaemonLocal struct { 60 codec kbfscodec.Codec 61 62 // lock protects everything below. 63 lock sync.Mutex 64 localUsers localUserMap 65 localTeams localTeamMap 66 localTeamSettings localTeamSettingsMap 67 localImplicitTeams localImplicitTeamMap 68 currentUID keybase1.UID 69 asserts map[string]keybase1.UserOrTeamID 70 implicitAsserts map[string]keybase1.TeamID 71 merkleRoot keybase1.MerkleRootV2 72 merkleTime time.Time 73 } 74 75 // SetCurrentUID sets the current UID. 76 func (dl *DaemonLocal) SetCurrentUID(uid keybase1.UID) { 77 dl.lock.Lock() 78 defer dl.lock.Unlock() 79 // TODO: Send out notifications. 80 dl.currentUID = uid 81 } 82 83 func (dl *DaemonLocal) assertionToIDLocked(ctx context.Context, 84 assertion string) (id keybase1.UserOrTeamID, err error) { 85 expr, err := externals.AssertionParseAndOnlyStatic(ctx, assertion) 86 if err != nil { 87 return keybase1.UserOrTeamID(""), err 88 } 89 urls := expr.CollectUrls(nil) 90 if len(urls) == 0 { 91 return keybase1.UserOrTeamID(""), errors.New("No assertion URLs") 92 } 93 94 for _, url := range urls { 95 var currID keybase1.UserOrTeamID 96 switch { 97 case url.IsUID(): 98 currID = url.ToUID().AsUserOrTeam() 99 case url.IsTeamID(): 100 currID = url.ToTeamID().AsUserOrTeam() 101 default: 102 key, val := url.ToKeyValuePair() 103 a := fmt.Sprintf("%s@%s", val, key) 104 if url.IsKeybase() && key != "team" { 105 a = val 106 } 107 var ok bool 108 currID, ok = dl.asserts[a] 109 if !ok { 110 return keybase1.UserOrTeamID(""), NoSuchUserError{a} 111 } 112 } 113 if id != keybase1.UserOrTeamID("") && currID != id { 114 return keybase1.UserOrTeamID(""), 115 errors.New("AND assertions resolve to different UIDs") 116 } 117 id = currID 118 } 119 return id, nil 120 } 121 122 // Resolve implements the KeybaseService interface for DaemonLocal. 123 func (dl *DaemonLocal) Resolve( 124 ctx context.Context, assertion string, _ keybase1.OfflineAvailability) ( 125 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 126 if err := checkContext(ctx); err != nil { 127 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 128 } 129 130 dl.lock.Lock() 131 defer dl.lock.Unlock() 132 id, err := dl.assertionToIDLocked(ctx, assertion) 133 if err != nil { 134 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 135 } 136 137 if id.IsUser() { 138 u, err := dl.localUsers.getLocalUser(id.AsUserOrBust()) 139 if err != nil { 140 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 141 } 142 return u.Name, id, nil 143 } 144 145 // Otherwise it's a team 146 ti, err := dl.localTeams.getLocalTeam(id.AsTeamOrBust()) 147 if err != nil { 148 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 149 } 150 151 _, ok := dl.localImplicitTeams[id.AsTeamOrBust()] 152 if ok { 153 // An implicit team exists, so Resolve shouldn't work. The 154 // caller should use `ResolveImplicitTeamByID` instead. 155 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), 156 fmt.Errorf("Team ID %s is an implicit team", id) 157 } 158 159 return ti.Name, id, nil 160 } 161 162 // Identify implements the KeybaseService interface for DaemonLocal. 163 func (dl *DaemonLocal) Identify( 164 ctx context.Context, assertion, _ string, 165 offline keybase1.OfflineAvailability) ( 166 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 167 // The local daemon doesn't need to distinguish resolves from 168 // identifies. 169 return dl.Resolve(ctx, assertion, offline) 170 } 171 172 // NormalizeSocialAssertion implements the KeybaseService interface 173 // for DaemonLocal. 174 func (dl *DaemonLocal) NormalizeSocialAssertion( 175 ctx context.Context, assertion string) (keybase1.SocialAssertion, error) { 176 socialAssertion, isSocialAssertion := externals.NormalizeSocialAssertionStatic(ctx, assertion) 177 if !isSocialAssertion { 178 return keybase1.SocialAssertion{}, fmt.Errorf("Invalid social assertion") 179 } 180 return socialAssertion, nil 181 } 182 183 func (dl *DaemonLocal) resolveForImplicitTeam( 184 ctx context.Context, name string, r []kbname.NormalizedUsername, 185 ur []keybase1.SocialAssertion, 186 resolvedIDs map[kbname.NormalizedUsername]keybase1.UserOrTeamID) ( 187 []kbname.NormalizedUsername, []keybase1.SocialAssertion, error) { 188 id, err := dl.assertionToIDLocked(ctx, name) 189 if err == nil { 190 u, err := dl.localUsers.getLocalUser(id.AsUserOrBust()) 191 if err != nil { 192 return nil, nil, err 193 } 194 r = append(r, u.Name) 195 resolvedIDs[u.Name] = id 196 } else { 197 a, ok := externals.NormalizeSocialAssertionStatic(ctx, name) 198 if !ok { 199 return nil, nil, fmt.Errorf("Bad assertion: %s", name) 200 } 201 ur = append(ur, a) 202 } 203 return r, ur, nil 204 } 205 206 // ResolveIdentifyImplicitTeam implements the KeybaseService interface 207 // for DaemonLocal. 208 func (dl *DaemonLocal) ResolveIdentifyImplicitTeam( 209 ctx context.Context, assertions, suffix string, tlfType tlf.Type, 210 doIdentifies bool, reason string, _ keybase1.OfflineAvailability) ( 211 ImplicitTeamInfo, error) { 212 if err := checkContext(ctx); err != nil { 213 return ImplicitTeamInfo{}, err 214 } 215 216 if tlfType != tlf.Private && tlfType != tlf.Public { 217 return ImplicitTeamInfo{}, fmt.Errorf( 218 "Invalid implicit team TLF type: %s", tlfType) 219 } 220 221 dl.lock.Lock() 222 defer dl.lock.Unlock() 223 224 // Canonicalize the name. 225 writerNames, readerNames, _, err := 226 SplitAndNormalizeTLFName(assertions, tlfType) 227 if err != nil { 228 return ImplicitTeamInfo{}, err 229 } 230 var writers, readers []kbname.NormalizedUsername 231 var unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion 232 resolvedIDs := make(map[kbname.NormalizedUsername]keybase1.UserOrTeamID) 233 for _, w := range writerNames { 234 writers, unresolvedWriters, err = dl.resolveForImplicitTeam( 235 ctx, w, writers, unresolvedWriters, resolvedIDs) 236 if err != nil { 237 return ImplicitTeamInfo{}, err 238 } 239 } 240 for _, r := range readerNames { 241 readers, unresolvedReaders, err = dl.resolveForImplicitTeam( 242 ctx, r, readers, unresolvedReaders, resolvedIDs) 243 if err != nil { 244 return ImplicitTeamInfo{}, err 245 } 246 } 247 248 var extensions []tlf.HandleExtension 249 if len(suffix) != 0 { 250 extensions, err = tlf.ParseHandleExtensionSuffix(suffix) 251 if err != nil { 252 return ImplicitTeamInfo{}, err 253 } 254 } 255 name := tlf.MakeCanonicalName( 256 writers, unresolvedWriters, readers, unresolvedReaders, extensions) 257 258 key := fmt.Sprintf("%s:%s", tlfType.String(), name) 259 tid, ok := dl.implicitAsserts[key] 260 if ok { 261 return dl.localImplicitTeams[tid], nil 262 } 263 264 // If the implicit team doesn't exist, always create it. 265 266 // Need to make the team info as well, so get the list of user 267 // names and resolve them. Auto-generate an implicit team name. 268 implicitName := kbname.NormalizedUsername( 269 fmt.Sprintf("_implicit_%d", len(dl.localTeams))) 270 teams := makeLocalTeams( 271 []kbname.NormalizedUsername{implicitName}, len(dl.localTeams), tlfType) 272 info := teams[0] 273 info.Writers = make(map[keybase1.UID]bool, len(writerNames)) 274 for _, w := range writers { 275 id, ok := resolvedIDs[w] 276 if !ok { 277 return ImplicitTeamInfo{}, fmt.Errorf("No resolved writer %s", w) 278 } 279 info.Writers[id.AsUserOrBust()] = true 280 } 281 if len(readerNames) > 0 { 282 info.Readers = make(map[keybase1.UID]bool, len(readerNames)) 283 for _, r := range readers { 284 id, ok := resolvedIDs[r] 285 if !ok { 286 return ImplicitTeamInfo{}, fmt.Errorf( 287 "No resolved reader %s", r) 288 289 } 290 info.Readers[id.AsUserOrBust()] = true 291 } 292 } 293 // Unresolved users don't need to go in the team info, they're 294 // irrelvant until they're resolved. TODO: add resolved users 295 // into existing teams they should be on. 296 297 tid = teams[0].TID 298 dl.implicitAsserts[key] = tid 299 dl.localTeams[tid] = info 300 301 asUserName := kbname.NormalizedUsername(name) 302 iteamInfo := ImplicitTeamInfo{ 303 // TODO: use the "preferred" canonical format here by listing 304 // the logged-in user first? 305 Name: asUserName, 306 TID: tid, 307 } 308 dl.localImplicitTeams[tid] = iteamInfo 309 return iteamInfo, nil 310 } 311 312 // ResolveImplicitTeamByID implements the KeybaseService interface 313 // for DaemonLocal. 314 func (dl *DaemonLocal) ResolveImplicitTeamByID( 315 ctx context.Context, teamID keybase1.TeamID) (name string, err error) { 316 if err := checkContext(ctx); err != nil { 317 return "", err 318 } 319 320 dl.lock.Lock() 321 defer dl.lock.Unlock() 322 323 info, ok := dl.localImplicitTeams[teamID] 324 if !ok { 325 return "", NoSuchTeamError{teamID.String()} 326 } 327 return info.Name.String(), nil 328 } 329 330 // LoadUserPlusKeys implements the KeybaseService interface for 331 // DaemonLocal. 332 func (dl *DaemonLocal) LoadUserPlusKeys( 333 ctx context.Context, uid keybase1.UID, _ keybase1.KID, 334 _ keybase1.OfflineAvailability) (UserInfo, error) { 335 if err := checkContext(ctx); err != nil { 336 return UserInfo{}, err 337 } 338 339 dl.lock.Lock() 340 defer dl.lock.Unlock() 341 u, err := dl.localUsers.getLocalUser(uid) 342 if err != nil { 343 return UserInfo{}, err 344 } 345 346 var infoCopy UserInfo 347 if err := kbfscodec.Update(dl.codec, &infoCopy, u.UserInfo); err != nil { 348 return UserInfo{}, err 349 } 350 return infoCopy, nil 351 } 352 353 // LoadTeamPlusKeys implements the KeybaseService interface for 354 // DaemonLocal. 355 func (dl *DaemonLocal) LoadTeamPlusKeys( 356 ctx context.Context, tid keybase1.TeamID, _ tlf.Type, _ kbfsmd.KeyGen, 357 _ keybase1.UserVersion, _ kbfscrypto.VerifyingKey, 358 _ keybase1.TeamRole, _ keybase1.OfflineAvailability) (TeamInfo, error) { 359 if err := checkContext(ctx); err != nil { 360 return TeamInfo{}, err 361 } 362 363 dl.lock.Lock() 364 defer dl.lock.Unlock() 365 t, err := dl.localTeams.getLocalTeam(tid) 366 if err != nil { 367 return TeamInfo{}, err 368 } 369 370 // Copy the info since it contains a map that might be mutated. 371 var infoCopy TeamInfo 372 if err := kbfscodec.Update(dl.codec, &infoCopy, t); err != nil { 373 return TeamInfo{}, err 374 } 375 return infoCopy, nil 376 } 377 378 // CreateTeamTLF implements the KeybaseService interface for 379 // DaemonLocal. 380 func (dl *DaemonLocal) CreateTeamTLF( 381 ctx context.Context, teamID keybase1.TeamID, tlfID tlf.ID) (err error) { 382 if err := checkContext(ctx); err != nil { 383 return err 384 } 385 386 // TODO: add check to make sure the private/public suffix of the 387 // team ID matches that of the tlf ID. 388 dl.lock.Lock() 389 defer dl.lock.Unlock() 390 iteamInfo, isImplicit := dl.localImplicitTeams[teamID] 391 if isImplicit { 392 iteamInfo.TlfID = tlfID 393 dl.localImplicitTeams[teamID] = iteamInfo 394 } 395 _, isRegularTeam := dl.localTeams[teamID] 396 if !isImplicit && !isRegularTeam { 397 return NoSuchTeamError{teamID.String()} 398 } 399 dl.localTeamSettings[teamID] = keybase1.KBFSTeamSettings{ 400 TlfID: keybase1.TLFID(tlfID.String())} 401 return nil 402 } 403 404 // GetTeamSettings implements the KeybaseService interface for 405 // DaemonLocal. 406 func (dl *DaemonLocal) GetTeamSettings( 407 ctx context.Context, teamID keybase1.TeamID, 408 _ keybase1.OfflineAvailability) ( 409 settings keybase1.KBFSTeamSettings, err error) { 410 if err := checkContext(ctx); err != nil { 411 return keybase1.KBFSTeamSettings{}, err 412 } 413 414 dl.lock.Lock() 415 defer dl.lock.Unlock() 416 return dl.localTeamSettings[teamID], nil 417 } 418 419 // GetCurrentMerkleRoot implements the MerkleRootGetter interface for 420 // DaemonLocal. 421 func (dl *DaemonLocal) GetCurrentMerkleRoot(ctx context.Context) ( 422 keybase1.MerkleRootV2, time.Time, error) { 423 if err := checkContext(ctx); err != nil { 424 return keybase1.MerkleRootV2{}, time.Time{}, err 425 } 426 427 dl.lock.Lock() 428 defer dl.lock.Unlock() 429 return dl.merkleRoot, dl.merkleTime, nil 430 } 431 432 // VerifyMerkleRoot implements the MerkleRootGetter interface for 433 // DaemonLocal. 434 func (dl *DaemonLocal) VerifyMerkleRoot( 435 _ context.Context, _ keybase1.MerkleRootV2, _ keybase1.KBFSRoot) error { 436 return nil 437 } 438 439 // SetCurrentMerkleRoot is a helper function, useful for tests, to set 440 // the current Merkle root. 441 func (dl *DaemonLocal) SetCurrentMerkleRoot( 442 root keybase1.MerkleRootV2, rootTime time.Time) { 443 dl.lock.Lock() 444 defer dl.lock.Unlock() 445 dl.merkleRoot = root 446 dl.merkleTime = rootTime 447 } 448 449 // CurrentSession implements the KeybaseService interface for 450 // DaemonLocal. 451 func (dl *DaemonLocal) CurrentSession(ctx context.Context, sessionID int) ( 452 SessionInfo, error) { 453 if err := checkContext(ctx); err != nil { 454 return SessionInfo{}, err 455 } 456 457 dl.lock.Lock() 458 defer dl.lock.Unlock() 459 u, err := dl.localUsers.getLocalUser(dl.currentUID) 460 if err != nil { 461 return SessionInfo{}, err 462 } 463 return SessionInfo{ 464 Name: u.Name, 465 UID: u.UID, 466 CryptPublicKey: u.GetCurrentCryptPublicKey(), 467 VerifyingKey: u.GetCurrentVerifyingKey(), 468 }, nil 469 } 470 471 // AddNewAssertionForTest makes newAssertion, which should be a single 472 // assertion that doesn't already resolve to anything, resolve to the 473 // same UID as oldAssertion, which should be an arbitrary assertion 474 // that does already resolve to something. It returns the UID of the 475 // user associated with the given assertions. 476 func (dl *DaemonLocal) AddNewAssertionForTest( 477 oldAssertion, newAssertion string) (keybase1.UID, error) { 478 dl.lock.Lock() 479 defer dl.lock.Unlock() 480 id, err := dl.assertionToIDLocked(context.Background(), oldAssertion) 481 if err != nil { 482 return keybase1.UID(""), err 483 } 484 uid := id.AsUserOrBust() 485 486 lu, err := dl.localUsers.getLocalUser(uid) 487 if err != nil { 488 return keybase1.UID(""), err 489 } 490 lu.Asserts = append(lu.Asserts, newAssertion) 491 dl.asserts[newAssertion] = id 492 dl.localUsers[uid] = lu 493 return uid, nil 494 } 495 496 // AddNewAssertionForTestOrBust is like AddNewAssertionForTest, but 497 // panics if there's an error. 498 func (dl *DaemonLocal) AddNewAssertionForTestOrBust( 499 oldAssertion, newAssertion string) keybase1.UID { 500 uid, err := dl.AddNewAssertionForTest(oldAssertion, newAssertion) 501 if err != nil { 502 panic(err) 503 } 504 return uid 505 } 506 507 // ChangeTeamNameForTest updates the name of an existing team. 508 func (dl *DaemonLocal) ChangeTeamNameForTest( 509 oldName, newName string) (keybase1.TeamID, error) { 510 dl.lock.Lock() 511 defer dl.lock.Unlock() 512 oldAssert := oldName + "@team" 513 newAssert := newName + "@team" 514 515 id, ok := dl.asserts[oldAssert] 516 if !ok { 517 return keybase1.TeamID(""), 518 fmt.Errorf("No such old team name: %s", oldName) 519 } 520 tid, err := id.AsTeam() 521 if err != nil { 522 return keybase1.TeamID(""), err 523 } 524 525 team, ok := dl.localTeams[tid] 526 if !ok { 527 return keybase1.TeamID(""), 528 fmt.Errorf("No such old team name: %s/%s", oldName, tid) 529 } 530 team.Name = kbname.NormalizedUsername(newName) 531 dl.localTeams[tid] = team 532 533 dl.asserts[newAssert] = id 534 delete(dl.asserts, oldAssert) 535 return tid, nil 536 } 537 538 // RemoveAssertionForTest removes the given assertion. Should only be 539 // used by tests. 540 func (dl *DaemonLocal) RemoveAssertionForTest(assertion string) { 541 dl.lock.Lock() 542 defer dl.lock.Unlock() 543 delete(dl.asserts, assertion) 544 } 545 546 // AddTeamWriterForTest adds a writer to a team. Should only be used 547 // by tests. 548 func (dl *DaemonLocal) AddTeamWriterForTest( 549 tid keybase1.TeamID, uid keybase1.UID) ( 550 username kbname.NormalizedUsername, isImplicit bool, err error) { 551 dl.lock.Lock() 552 defer dl.lock.Unlock() 553 t, err := dl.localTeams.getLocalTeam(tid) 554 if err != nil { 555 return "", false, err 556 } 557 558 if t.Writers == nil { 559 t.Writers = make(map[keybase1.UID]bool) 560 } 561 t.Writers[uid] = true 562 delete(t.Readers, uid) 563 dl.localTeams[tid] = t 564 _, isImplicit = dl.localImplicitTeams[tid] 565 return t.Name, isImplicit, nil 566 } 567 568 // RemoveTeamWriterForTest removes a writer from a team. Should only 569 // be used by tests. 570 func (dl *DaemonLocal) RemoveTeamWriterForTest( 571 tid keybase1.TeamID, uid keybase1.UID) (kbname.NormalizedUsername, error) { 572 dl.lock.Lock() 573 defer dl.lock.Unlock() 574 t, err := dl.localTeams.getLocalTeam(tid) 575 if err != nil { 576 return "", err 577 } 578 579 if _, ok := t.Writers[uid]; ok { 580 u, err := dl.localUsers.getLocalUser(uid) 581 if err != nil { 582 return "", err 583 } 584 if t.LastWriters == nil { 585 t.LastWriters = make( 586 map[kbfscrypto.VerifyingKey]keybase1.MerkleRootV2) 587 } 588 for _, key := range u.VerifyingKeys { 589 t.LastWriters[key] = dl.merkleRoot 590 } 591 } 592 dl.merkleRoot.Seqno++ 593 594 delete(t.Writers, uid) 595 delete(t.Readers, uid) 596 597 dl.localTeams[tid] = t 598 return t.Name, nil 599 } 600 601 // AddTeamReaderForTest adds a reader to a team. Should only be used 602 // by tests. 603 func (dl *DaemonLocal) AddTeamReaderForTest( 604 tid keybase1.TeamID, uid keybase1.UID) (kbname.NormalizedUsername, error) { 605 dl.lock.Lock() 606 defer dl.lock.Unlock() 607 t, err := dl.localTeams.getLocalTeam(tid) 608 if err != nil { 609 return "", err 610 } 611 612 if t.Writers[uid] { 613 // Being a writer already implies being a reader. 614 return "", nil 615 } 616 617 if t.Readers == nil { 618 t.Readers = make(map[keybase1.UID]bool) 619 } 620 t.Readers[uid] = true 621 dl.localTeams[tid] = t 622 return t.Name, nil 623 } 624 625 // AddTeamKeyForTest adds a key to a team. Should only be used by 626 // tests. 627 func (dl *DaemonLocal) AddTeamKeyForTest( 628 tid keybase1.TeamID, newKeyGen kbfsmd.KeyGen, 629 newKey kbfscrypto.TLFCryptKey) error { 630 dl.lock.Lock() 631 defer dl.lock.Unlock() 632 t, err := dl.localTeams.getLocalTeam(tid) 633 if err != nil { 634 return err 635 } 636 637 t.CryptKeys[newKeyGen] = newKey 638 if newKeyGen > t.LatestKeyGen { 639 t.LatestKeyGen = newKeyGen 640 // Only need to save back to the map if we've modified a 641 // non-reference type like the latest key gen. 642 dl.localTeams[tid] = t 643 } 644 return nil 645 } 646 647 func (dl *DaemonLocal) addTeamsForTestLocked(teams []TeamInfo) { 648 for _, t := range teams { 649 dl.localTeams[t.TID] = t 650 dl.asserts[string(t.Name)+"@team"] = t.TID.AsUserOrTeam() 651 } 652 } 653 654 // AddTeamsForTest adds teams to this daemon. Should only be used by 655 // tests. 656 func (dl *DaemonLocal) AddTeamsForTest(teams []TeamInfo) { 657 dl.lock.Lock() 658 defer dl.lock.Unlock() 659 dl.addTeamsForTestLocked(teams) 660 } 661 662 // GetLocalUser returns the `LocalUser` object for the given UID. 663 func (dl *DaemonLocal) GetLocalUser(uid keybase1.UID) (LocalUser, error) { 664 dl.lock.Lock() 665 defer dl.lock.Unlock() 666 return dl.localUsers.getLocalUser(uid) 667 } 668 669 // SetLocalUser sets the `LocalUser` object for the given UID. 670 func (dl *DaemonLocal) SetLocalUser(uid keybase1.UID, user LocalUser) { 671 dl.lock.Lock() 672 defer dl.lock.Unlock() 673 dl.localUsers[uid] = user 674 } 675 676 // GetIDForAssertion returns the UID associated with the given 677 // assertion. 678 func (dl *DaemonLocal) GetIDForAssertion(assertion string) ( 679 keybase1.UserOrTeamID, bool) { 680 dl.lock.Lock() 681 defer dl.lock.Unlock() 682 id, ok := dl.asserts[assertion] 683 return id, ok 684 } 685 686 // GetLocalUsers returns all of the users tracked by this daemon. 687 func (dl *DaemonLocal) GetLocalUsers() (res []LocalUser) { 688 dl.lock.Lock() 689 defer dl.lock.Unlock() 690 for _, u := range dl.localUsers { 691 res = append(res, u) 692 } 693 return res 694 } 695 696 // NewDaemonLocal constructs a new DaemonLocal, initialized to contain 697 // the given users and teams. 698 func NewDaemonLocal( 699 currentUID keybase1.UID, users []LocalUser, 700 teams []TeamInfo, codec kbfscodec.Codec) *DaemonLocal { 701 localUserMap := make(localUserMap) 702 asserts := make(map[string]keybase1.UserOrTeamID) 703 for _, u := range users { 704 localUserMap[u.UID] = u 705 for _, a := range u.Asserts { 706 asserts[a] = u.UID.AsUserOrTeam() 707 } 708 asserts[string(u.Name)] = u.UID.AsUserOrTeam() 709 } 710 dl := &DaemonLocal{ 711 codec: codec, 712 localUsers: localUserMap, 713 localTeams: make(localTeamMap), 714 localTeamSettings: make(localTeamSettingsMap), 715 localImplicitTeams: make(localImplicitTeamMap), 716 asserts: asserts, 717 implicitAsserts: make(map[string]keybase1.TeamID), 718 currentUID: currentUID, 719 // TODO: let test fill in valid merkle root. 720 } 721 dl.AddTeamsForTest(teams) 722 return dl 723 }