func (k *KeybaseDaemonLocal) assertionToUIDLocked(ctx context.Context, assertion string) (uid keybase1.UID, err error) { expr, err := libkb.AssertionParseAndOnly(assertion) if err != nil { return keybase1.UID(""), err } urls := expr.CollectUrls(nil) if len(urls) == 0 { return keybase1.UID(""), errors.New("No assertion URLs") } for _, url := range urls { var currUID keybase1.UID if url.IsUID() { currUID = url.ToUID() } else { key, val := url.ToKeyValuePair() a := fmt.Sprintf("%s@%s", val, key) if url.IsKeybase() { a = val } var ok bool currUID, ok = k.asserts[a] if !ok { return keybase1.UID(""), NoSuchUserError{a} } } if uid != keybase1.UID("") && currUID != uid { return keybase1.UID(""), errors.New("AND assertions resolve to different UIDs") } uid = currUID } return uid, nil }
func testIdentifyResolve2(t *testing.T, g *libkb.GlobalContext) { cli, err := client.GetIdentifyClient(g) if err != nil { t.Fatalf("failed to get new identifyclient: %v", err) } if _, err := cli.Resolve(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil { t.Fatalf("Resovle failed: %v\n", err) } // We don't want to hit the cache, since the previous lookup never hit the // server. For Resolve2, we have to, since we need a username. So test that // here. if res, err := cli.Resolve2(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil { t.Fatalf("Resovle failed: %v\n", err) } else if res.Username != "t_tracy" { t.Fatalf("Wrong username: %s != 't_tracy", res.Username) } if res, err := cli.Resolve2(context.TODO(), "t_tracy@rooter"); err != nil { t.Fatalf("Resolve2 failed: %v\n", err) } else if res.Username != "t_tracy" { t.Fatalf("Wrong name: %s != 't_tracy", res.Username) } else if !res.Uid.Equal(keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")) { t.Fatalf("Wrong uid for tracy: %s\n", res.Uid) } if _, err := cli.Resolve2(context.TODO(), "foobag@rooter"); err == nil { t.Fatalf("expected an error on a bad resolve, but got none") } else if _, ok := err.(libkb.ResolutionError); !ok { t.Fatalf("Wrong error: wanted type %T but got (%v, %T)", libkb.ResolutionError{}, err, err) } }
func testLoadAllPublicKeysUnverified(t *testing.T, g *libkb.GlobalContext) { cli, err := client.GetUserClient(g) if err != nil { t.Fatalf("failed to get user client: %s", err) } // t_rosetta arg := keybase1.LoadAllPublicKeysUnverifiedArg{Uid: keybase1.UID("b8939251cb3d367e68587acb33a64d19")} res, err := cli.LoadAllPublicKeysUnverified(context.TODO(), arg) if err != nil { t.Fatalf("failed to make load keys call: %s", err) } if len(res) != 3 { t.Fatalf("wrong amount of keys loaded: %d != %d", len(res), 3) } keys := map[keybase1.KID]bool{ keybase1.KID("0101fe1183765f256289427d6943cd8bab3b5fe095bcdd27f031ed298da523efd3120a"): true, keybase1.KID("0101b5839c4ccaa9d03b3016b9aa73a7e3eafb067f9c86c07a6f2f79cb8558b1c97f0a"): true, keybase1.KID("0101188ee7e63ccbd05af498772ab2975ee29df773240d17dde09aecf6c132a5a9a60a"): true, } for _, key := range res { if _, ok := keys[key.KID]; !ok { t.Fatalf("unknown key in response: %s", key.KID) } } }
func (ra resolvableAssertion) resolve(ctx context.Context) ( nameUIDPair, keybase1.SocialAssertion, error) { if ra.assertion == PublicUIDName { return nameUIDPair{}, keybase1.SocialAssertion{}, fmt.Errorf("Invalid name %s", ra.assertion) } name, uid, err := ra.resolver.Resolve(ctx, ra.assertion) if err == nil && ra.mustBeUser != keybase1.UID("") && ra.mustBeUser != uid { // Force an unresolved assertion sinced the forced user doesn't match err = NoSuchUserError{ra.assertion} } switch err := err.(type) { default: return nameUIDPair{}, keybase1.SocialAssertion{}, err case nil: return nameUIDPair{ name: name, uid: uid, }, keybase1.SocialAssertion{}, nil case NoSuchUserError: if !ra.sharingBeforeSignupEnabled { return nameUIDPair{}, keybase1.SocialAssertion{}, err } socialAssertion, ok := libkb.NormalizeSocialAssertion(ra.assertion) if !ok { return nameUIDPair{}, keybase1.SocialAssertion{}, err } return nameUIDPair{}, socialAssertion, nil } }
func genUID(t *testing.T) keybase1.UID { uid := make([]byte, 8) if _, err := rand.Read(uid); err != nil { t.Fatalf("rand failed: %v\n", err) } return keybase1.UID(hex.EncodeToString(uid)) }
func (c *fakeKeybaseClient) Call(ctx context.Context, s string, args interface{}, res interface{}) error { switch s { case "keybase.1.session.currentSession": *res.(*keybase1.Session) = keybase1.Session{ Uid: c.session.UID, Username: "******", Token: c.session.Token, DeviceSubkeyKid: c.session.CryptPublicKey.kid, DeviceSibkeyKid: c.session.VerifyingKey.kid, } c.currentSessionCalled = true return nil case "keybase.1.identify.identify2": arg := args.([]interface{})[0].(keybase1.Identify2Arg) uidStr := strings.TrimPrefix(arg.UserAssertion, "uid:") if len(uidStr) == len(arg.UserAssertion) { return fmt.Errorf("Non-uid assertion %s", arg.UserAssertion) } uid := keybase1.UID(uidStr) userInfo, ok := c.users[uid] if !ok { return fmt.Errorf("Could not find user info for UID %s", uid) } *res.(*keybase1.Identify2Res) = keybase1.Identify2Res{ Upk: keybase1.UserPlusKeys{ Uid: uid, Username: string(userInfo.Name), }, } c.identifyCalled = true return nil case "keybase.1.user.loadUserPlusKeys": arg := args.([]interface{})[0].(keybase1.LoadUserPlusKeysArg) userInfo, ok := c.users[arg.Uid] if !ok { return fmt.Errorf("Could not find user info for UID %s", arg.Uid) } *res.(*keybase1.UserPlusKeys) = keybase1.UserPlusKeys{ Uid: arg.Uid, Username: string(userInfo.Name), } c.loadUserPlusKeysCalled = true return nil default: return fmt.Errorf("Unknown call: %s %v %v", s, args, res) } }
// Resolve implements the KeybaseDaemon interface for KeybaseDaemonRPC. func (k *KeybaseDaemonRPC) Resolve(ctx context.Context, assertion string) ( libkb.NormalizedUsername, keybase1.UID, error) { user, err := k.identifyClient.Resolve2(ctx, assertion) if err != nil { return libkb.NormalizedUsername(""), keybase1.UID(""), convertIdentifyError(assertion, err) } return libkb.NewNormalizedUsername(user.Username), user.Uid, nil }
// GetCurrentUID implements the KBPKI interface for KBPKIClient. func (k *KBPKIClient) GetCurrentUID(ctx context.Context) (keybase1.UID, error) { s, err := k.session(ctx) if err != nil { // TODO: something more intelligent; maybe just shut down // unless we want anonymous browsing of public data return keybase1.UID(""), err } return s.UID, nil }
// Resolve implements KeybaseDaemon for KeybaseDaemonLocal. func (k KeybaseDaemonLocal) Resolve(ctx context.Context, assertion string) ( keybase1.UID, error) { uid, ok := k.asserts[assertion] if !ok { return keybase1.UID(""), NoSuchUserError{assertion} } return uid, nil }
// addNewAssertionForTest makes newAssertion, which should be a single // assertion that doesn't already resolve to anything, resolve to the // same UID as oldAssertion, which should be an arbitrary assertion // that does already resolve to something. It returns the UID of the // user associated with the given assertions. func (k *KeybaseDaemonLocal) addNewAssertionForTest( oldAssertion, newAssertion string) (keybase1.UID, error) { k.lock.Lock() defer k.lock.Unlock() uid, err := k.assertionToUIDLocked(context.Background(), oldAssertion) if err != nil { return keybase1.UID(""), err } lu, err := k.localUsers.getLocalUser(uid) if err != nil { return keybase1.UID(""), err } lu.Asserts = append(lu.Asserts, newAssertion) k.asserts[newAssertion] = uid k.localUsers[uid] = lu return uid, nil }
// GetCurrentUserInfo implements the KBPKI interface for KBPKIClient. func (k *KBPKIClient) GetCurrentUserInfo(ctx context.Context) ( libkb.NormalizedUsername, keybase1.UID, error) { s, err := k.session(ctx) if err != nil { // TODO: something more intelligent; maybe just shut down // unless we want anonymous browsing of public data return libkb.NormalizedUsername(""), keybase1.UID(""), err } return s.Name, s.UID, nil }
func TestKBPKIClientIdentify(t *testing.T) { c, _, _ := makeTestKBPKIClient(t) u, err := c.Identify(context.Background(), "test_name1", "") if err != nil { t.Fatal(err) } if u.UID == keybase1.UID("") { t.Fatal("empty user") } }
// Resolve implements KeybaseDaemon for KeybaseDaemonLocal. func (k *KeybaseDaemonLocal) Resolve(ctx context.Context, assertion string) ( libkb.NormalizedUsername, keybase1.UID, error) { k.lock.Lock() defer k.lock.Unlock() uid, err := k.assertionToUIDLocked(ctx, assertion) if err != nil { return libkb.NormalizedUsername(""), keybase1.UID(""), err } return k.localUsers[uid].Name, uid, nil }
// CurrentSession implements the KeybaseDaemon interface for KeybaseDaemonRPC. func (k *KeybaseDaemonRPC) CurrentSession(ctx context.Context, sessionID int) ( SessionInfo, error) { cachedCurrentSession := k.getCachedCurrentSession() if cachedCurrentSession != (SessionInfo{}) { return cachedCurrentSession, nil } res, err := k.sessionClient.CurrentSession(ctx, sessionID) if err != nil { if ncs := (NoCurrentSessionError{}); err.Error() == NoCurrentSessionExpectedError { // Use an error with a proper OS error code attached to // it. TODO: move ErrNoSession from client/go/service to // client/go/libkb, so we can use types for the check // above. err = ncs } return SessionInfo{}, err } // Import the KIDs to validate them. deviceSubkey, err := libkb.ImportKeypairFromKID(res.DeviceSubkeyKid) if err != nil { return SessionInfo{}, err } deviceSibkey, err := libkb.ImportKeypairFromKID(res.DeviceSibkeyKid) if err != nil { return SessionInfo{}, err } cryptPublicKey := MakeCryptPublicKey(deviceSubkey.GetKID()) verifyingKey := MakeVerifyingKey(deviceSibkey.GetKID()) s := SessionInfo{ Name: libkb.NewNormalizedUsername(res.Username), UID: keybase1.UID(res.Uid), Token: res.Token, CryptPublicKey: cryptPublicKey, VerifyingKey: verifyingKey, } k.log.CDebugf( ctx, "new session with username %s, uid %s, crypt public key %s, and verifying key %s", s.Name, s.UID, s.CryptPublicKey, s.VerifyingKey) k.setCachedCurrentSession(s) return s, nil }
func (e *ListTrackingEngine) runTable(trackList TrackList) error { for _, link := range trackList { uid, err := link.GetTrackedUID() if err != nil { return err } entry := keybase1.UserSummary{ Username: link.ToDisplayString(), SigIDDisplay: link.GetSigID().ToDisplayString(true), TrackTime: keybase1.ToTime(link.GetCTime()), Uid: keybase1.UID(uid), } entry.Proofs.PublicKeys = e.linkPGPKeys(link) entry.Proofs.Social = e.linkSocialProofs(link) entry.Proofs.Web = e.linkWebProofs(link) e.tableResult = append(e.tableResult, entry) } return nil }
// CurrentSession implements the KeybaseDaemon interface for KeybaseDaemonRPC. func (k *KeybaseDaemonRPC) CurrentSession(ctx context.Context, sessionID int) ( SessionInfo, error) { cachedCurrentSession := k.getCachedCurrentSession() if cachedCurrentSession != (SessionInfo{}) { return cachedCurrentSession, nil } res, err := k.sessionClient.CurrentSession(ctx, sessionID) if err != nil { if ncs := (NoCurrentSessionError{}); err.Error() == ncs.Error() { // Use an error with a proper OS error code attached to it. err = ncs } return SessionInfo{}, err } // Import the KIDs to validate them. deviceSubkey, err := libkb.ImportKeypairFromKID(res.DeviceSubkeyKid) if err != nil { return SessionInfo{}, err } deviceSibkey, err := libkb.ImportKeypairFromKID(res.DeviceSibkeyKid) if err != nil { return SessionInfo{}, err } cryptPublicKey := MakeCryptPublicKey(deviceSubkey.GetKID()) verifyingKey := MakeVerifyingKey(deviceSibkey.GetKID()) s := SessionInfo{ UID: keybase1.UID(res.Uid), Token: res.Token, CryptPublicKey: cryptPublicKey, VerifyingKey: verifyingKey, } k.log.CDebugf( ctx, "new session with uid %s, crypt public key %s, and verifying key %s", s.UID, s.CryptPublicKey, s.VerifyingKey) k.setCachedCurrentSession(s) return s, nil }
// Test that the session cache works and is invalidated as expected. func TestKeybaseDaemonSessionCache(t *testing.T) { k := MakeLocalUserCryptPublicKeyOrBust( libkb.NormalizedUsername("fake username")) v := MakeLocalUserVerifyingKeyOrBust( libkb.NormalizedUsername("fake username")) session := SessionInfo{ UID: keybase1.UID("fake uid"), Token: "fake token", CryptPublicKey: k, VerifyingKey: v, } client := &fakeKeybaseClient{session: session} c := newKeybaseDaemonRPCWithClient( nil, client, logger.NewTestLogger(t)) // Should fill cache. testCurrentSession(t, client, c, session, expectCall) // Should be cached. testCurrentSession(t, client, c, session, expectCached) // Should invalidate cache. err := c.LoggedOut(context.Background()) if err != nil { t.Fatal(err) } // Should fill cache again. testCurrentSession(t, client, c, session, expectCall) // Should be cached again. testCurrentSession(t, client, c, session, expectCached) // Should invalidate cache. c.OnDisconnected(UsingExistingConnection) // Should fill cache again. testCurrentSession(t, client, c, session, expectCall) }
func (t Tracker) GetUID() keybase1.UID { return keybase1.UID(t.Tracker) }
func TestTrackingNotifications(t *testing.T) { tc := setupTest(t, "signup") tc2 := cloneContext(tc) tc5 := cloneContext(tc) libkb.G.LocalDb = nil // Hack the various portions of the service that aren't // properly contextified. defer tc.Cleanup() stopCh := make(chan error) svc := service.NewService(tc.G, false) startCh := svc.GetStartChannel() go func() { err := svc.Run() if err != nil { t.Logf("Running the service produced an error: %v", err) } stopCh <- err }() userInfo := randomUser("sgnup") tui := trackingUI{ signupUI: signupUI{ info: userInfo, Contextified: libkb.NewContextified(tc2.G), }, } tc2.G.SetUI(&tui) signup := client.NewCmdSignupRunner(tc2.G) signup.SetTest() <-startCh if err := signup.Run(); err != nil { t.Fatal(err) } tc2.G.Log.Debug("Login State: %v", tc2.G.LoginState()) nh := newTrackingNotifyHandler() // Launch the server that will listen for tracking notifications. launchServer := func(nh *trackingNotifyHandler) error { cli, xp, err := client.GetRPCClientWithContext(tc5.G) if err != nil { return err } srv := rpc.NewServer(xp, nil) if err = srv.Register(keybase1.NotifyTrackingProtocol(nh)); err != nil { return err } ncli := keybase1.NotifyCtlClient{Cli: cli} if err = ncli.SetNotifications(context.TODO(), keybase1.NotificationChannels{ Tracking: true, }); err != nil { return err } return nil } // Actually launch it in the background go func() { err := launchServer(nh) if err != nil { nh.errCh <- err } }() // Have our test user track t_alice. trackCmd := client.NewCmdTrackRunner(tc2.G) trackCmd.SetUser("t_alice") trackCmd.SetOptions(keybase1.TrackOptions{BypassConfirm: true}) err := trackCmd.Run() if err != nil { t.Fatal(err) } // Do a check for new tracking statements that should fire off a // notification. Currently the track command above does not fetch the new // chain link from the server, so this call is required. It's possible that // TrackEngine (or our signature caching code) might change in the future, // making this call unnecessary. checkTrackingCmd := client.NewCmdCheckTrackingRunner(tc2.G) err = checkTrackingCmd.Run() if err != nil { t.Fatal(err) } // Wait to get a notification back as we expect. // NOTE: If this test ever starts deadlocking here, it's possible that // we've changed how we cache signatures that we make on the local client, // in such a way that the fetch done by CheckTracking above doesn't find // any "isOwnNewLinkFromServer" links. If so, one way to fix this test // would be to blow away the local db before calling CheckTracking. tc.G.Log.Debug("Waiting for two tracking notifications.") for i := 0; i < 2; i++ { select { case err := <-nh.errCh: t.Fatalf("Error before notify: %v", err) case arg := <-nh.trackingCh: tAliceUID := keybase1.UID("295a7eea607af32040647123732bc819") tc.G.Log.Debug("Got tracking changed notification (%#v)", arg) if "t_alice" == arg.Username { if !tAliceUID.Equal(arg.Uid) { t.Fatalf("Bad UID back: %s != %s", tAliceUID, arg.Uid) } } else if userInfo.username == arg.Username { if !tc.G.Env.GetUID().Equal(arg.Uid) { t.Fatalf("Bad UID back: %s != %s", tc.G.Env.GetUID(), arg.Uid) } } else { t.Fatalf("Bad username back: %s != %s || %s", arg.Username, "t_alice", userInfo.username) } } } if err := client.CtlServiceStop(tc2.G); err != nil { t.Fatal(err) } // If the server failed, it's also an error if err := <-stopCh; err != nil { t.Fatal(err) } }
return &resolveTestClock{now: time.Now()} } func newTestResolverCache(g *GlobalContext) (*Resolver, *resolveTestClock) { clock := newResolveTestClock() res := NewResolver(g) res.EnableCaching() res.nowFunc = func() time.Time { return clock.now } return res, clock } func (r *resolveTestClock) tick(d time.Duration) { r.now = r.now.Add(d) } var tracyUID = keybase1.UID("eb72f49f2dde6429e5d78003dae0c919") func TestResolveSimple(t *testing.T) { tc := SetupTest(t, "resolveSimple", 1) r, clock := newTestResolverCache(tc.G) goodResolve := func(s string) { res := r.Resolve(s) if err := res.GetError(); err != nil { t.Fatal(err) } if res.GetUID() != tracyUID { t.Fatalf("Got wrong UID; wanted %s but got %s", tracyUID, res.GetUID()) } } goodResolve("eb72f49f2dde6429e5d78003dae0c919@uid")
// Test that the user cache works and is invalidated as expected. func TestKeybaseDaemonUserCache(t *testing.T) { uid1 := keybase1.UID("uid1") uid2 := keybase1.UID("uid2") name1 := libkb.NewNormalizedUsername("name1") name2 := libkb.NewNormalizedUsername("name2") users := map[keybase1.UID]UserInfo{ uid1: {Name: name1}, uid2: {Name: name2}, } client := &fakeKeybaseClient{users: users} c := newKeybaseDaemonRPCWithClient( nil, client, logger.NewTestLogger(t)) // Should fill cache. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) // Should be cached. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) // Should fill cache. testIdentify(t, client, c, uid2, name2, expectCall) // Should be cached. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should not be cached. testIdentify(t, client, c, uid2, name2, expectCall) // Should invalidate cache for uid1. err := c.UserChanged(context.Background(), uid1) if err != nil { t.Fatal(err) } // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) // Should be cached again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) // Should still be cached. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should invalidate cache for uid2. err = c.UserChanged(context.Background(), uid2) if err != nil { t.Fatal(err) } // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) // Should be cached again. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should invalidate cache for all users. c.OnDisconnected(UsingExistingConnection) // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) }
// Test that the user cache works and is invalidated as expected. func TestKeybaseDaemonUserCache(t *testing.T) { uid1 := keybase1.UID("uid1") uid2 := keybase1.UID("uid2") name1 := libkb.NewNormalizedUsername("name1") name2 := libkb.NewNormalizedUsername("name2") users := map[keybase1.UID]UserInfo{ uid1: {Name: name1}, uid2: {Name: name2}, } client := &fakeKeybaseClient{users: users} c := newKeybaseDaemonRPCWithClient( nil, client, logger.NewTestLogger(t)) // Should fill cache. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) // Should be cached. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) // Should fill cache. testIdentify(t, client, c, uid2, name2, expectCall) // Should be cached. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should not be cached. testIdentify(t, client, c, uid2, name2, expectCall) // Should invalidate cache for uid1. err := c.UserChanged(context.Background(), uid1) require.NoError(t, err) // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) // Should be cached again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) // Should still be cached. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should invalidate cache for uid2. err = c.UserChanged(context.Background(), uid2) require.NoError(t, err) // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) // Should be cached again. testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) // Should invalidate cache for all users. c.OnDisconnected(context.Background(), rpc.UsingExistingConnection) // Should fill cache again. testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) // Test that CheckForRekey gets called only if the logged-in user // changes. session := SessionInfo{ UID: uid1, } c.setCachedCurrentSession(session) ctr := NewSafeTestReporter(t) mockCtrl := gomock.NewController(ctr) config := NewConfigMock(mockCtrl, ctr) c.config = config defer func() { config.ctr.CheckForFailures() mockCtrl.Finish() }() errChan := make(chan error, 1) config.mockMdserv.EXPECT().CheckForRekeys(gomock.Any()).Do( func(ctx context.Context) { errChan <- nil }).Return(errChan) err = c.UserChanged(context.Background(), uid1) <-errChan // This one shouldn't trigger CheckForRekeys; if it does, the mock // controller will catch it during Finish. err = c.UserChanged(context.Background(), uid2) }
// ResolveAgain tries to resolve any unresolved assertions in the // given handle and returns a new handle with the results. As an // optimization, if h contains no unresolved assertions, it just // returns itself. func (h *TlfHandle) ResolveAgain(ctx context.Context, resolver resolver) ( *TlfHandle, error) { return h.ResolveAgainForUser(ctx, resolver, keybase1.UID("")) }
run() // A new slow-path hit if !i.fastStats.eq(0, 0, 0, 0) || !i.slowStats.eq(3, 1, 1, 0) { t.Fatalf("bad cache stats %+v %+v", i.fastStats, i.slowStats) } } func TestResolveAndIdentify2WithUIDWithAssertions(t *testing.T) { tc := SetupEngineTest(t, "Identify2WithUIDWithAssertion") i := newIdentify2WithUIDTester(tc.G) tc.G.ProofCheckerFactory = i arg := &keybase1.Identify2Arg{ UserAssertion: "tacovontaco@twitter+t_tracy@rooter", } eng := NewResolveThenIdentify2(tc.G, arg) eng.testArgs = &Identify2WithUIDTestArgs{ noMe: true, } ctx := Context{IdentifyUI: i} err := eng.Run(&ctx) if err != nil { t.Fatal(err) } <-i.startCh <-i.finishCh } var tracyUID = keybase1.UID("eb72f49f2dde6429e5d78003dae0c919") var trackingUID = keybase1.UID("92b3b3dbe457059f28c9f74e8e6b9419") var trackingServerReply = `{"seqno":3,"payload_hash":"c3ffe390e9c9dabdd5f7253b81e0a38fad2c17589a9c7fcd967958418055140a","sig_id":"4ec10665ad163d0aa419ce4eab8ff661429c9a3a32cd4978fdb8c6b5c6d047620f","sig_id_short":"TsEGZa0WPQqkGc5Oq4_2YUKcmjoyzUl4_bjG","kid":"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a","sig":"-----BEGIN PGP MESSAGE-----\nVersion: Keybase OpenPGP v2.0.49\nComment: https://keybase.io/crypto\n\n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\nHzt0Q3P9OxPLmqeOPX3RVSPpG2YdtWQ=\n=h5Bq\n-----END PGP MESSAGE-----","payload_json":"{\"body\":{\"client\":{\"name\":\"keybase.io web\"},\"key\":{\"eldest_kid\":\"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a\",\"fingerprint\":\"a889587e1ce7bd7edbe3eeb8ef8fd8f7b31c4a2f\",\"host\":\"keybase.io\",\"key_id\":\"ef8fd8f7b31c4a2f\",\"kid\":\"0101f3b2f0e8c9d1f099db64cac366c6a9c1da63da624127b2f66a056acfa36834fe0a\",\"uid\":\"92b3b3dbe457059f28c9f74e8e6b9419\",\"username\":\"tracy_friend1\"},\"track\":{\"basics\":{\"id_version\":14,\"last_id_change\":1449514728,\"username\":\"t_tracy\"},\"id\":\"eb72f49f2dde6429e5d78003dae0c919\",\"key\":{\"key_fingerprint\":\"\",\"kid\":\"01209bd2e255235529cf45877767ad8687d85200518adc74595d058750e2f7ab7b000a\"},\"pgp_keys\":[{\"key_fingerprint\":\"4ff50d580914427227bb14c821029e2c7cf0d488\",\"kid\":\"0101ee69b1566428109eb7548d9a9d7267d48933daa4614fa743cedbeac618ab66dd0a\"}],\"remote_proofs\":[{\"ctime\":1449512840,\"curr\":\"f09c84ccadf8817aea944526638e9a4c034c9200dd68b5a3292c7f69d980390d\",\"etime\":1954088840,\"prev\":\"909f6aa65b050ec5582515cad43aeb1f9279ee21db955cff309abe4692b7e11a\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"twitter\",\"username\":\"tacovontaco\"},\"proof_type\":2,\"state\":1},\"seqno\":5,\"sig_id\":\"67570e971c5b8881cf07179d1872a83042be4285ba897a8f12dc3e419cade80b0f\",\"sig_type\":2},{\"ctime\":1449512883,\"curr\":\"8ad8ce94c9d23d260750294905877ef92adf4e7736198909fcbe7e27d6dfb463\",\"etime\":1954088883,\"prev\":\"f09c84ccadf8817aea944526638e9a4c034c9200dd68b5a3292c7f69d980390d\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"github\",\"username\":\"tacoplusplus\"},\"proof_type\":3,\"state\":1},\"seqno\":6,\"sig_id\":\"bfe76a25acf046f7477350291cdd178e1f0026a49f85733d97c122ba4e4a000f0f\",\"sig_type\":2},{\"ctime\":1449512914,\"curr\":\"ea5bee1701e7ec7c8dfd71421bd2ab6fb0fa2af473412c664fa49d35c34078ea\",\"etime\":1954088914,\"prev\":\"8ad8ce94c9d23d260750294905877ef92adf4e7736198909fcbe7e27d6dfb463\",\"remote_key_proof\":{\"check_data_json\":{\"name\":\"rooter\",\"username\":\"t_tracy\"},\"proof_type\":100001,\"state\":1},\"seqno\":7,\"sig_id\":\"0c467de321795b777aa10916eb9aa8153bffa5163b5079600db7d50ca00a77410f\",\"sig_type\":2},{\"ctime\":1449514687,\"curr\":\"bfd3462a2193fa7946f7f31e5074cfc4ac95400680273deb520078a6a4f5cbf5\",\"etime\":1954090687,\"prev\":\"9ae84f56c0c62dc91206363b9f5609245f94199d58a4a3c0bee7d4bb91c47de7\",\"remote_key_proof\":{\"check_data_json\":{\"hostname\":\"keybase.io\",\"protocol\":\"https:\"},\"proof_type\":1000,\"state\":1},\"seqno\":9,\"sig_id\":\"92eeea3db99cb519409765c17ea32a82ce8b86bbacd8f366e8e8930f1faea20b0f\",\"sig_type\":2}],\"seq_tail\":{\"payload_hash\":\"bfd3462a2193fa7946f7f31e5074cfc4ac95400680273deb520078a6a4f5cbf5\",\"seqno\":9,\"sig_id\":\"92eeea3db99cb519409765c17ea32a82ce8b86bbacd8f366e8e8930f1faea20b0f\"}},\"type\":\"track\",\"version\":1},\"ctime\":1449514785,\"expire_in\":157680000,\"prev\":\"a4f76660341a087d69238f5a25e98d8b3d038224457107bad91ffdfbd82d84d9\",\"seqno\":3,\"tag\":\"signature\"}","sig_type":3,"ctime":1449514785,"etime":1607194785,"rtime":null,"sig_status":0,"prev":"a4f76660341a087d69238f5a25e98d8b3d038224457107bad91ffdfbd82d84d9","proof_id":null,"proof_type":null,"proof_text_check":null,"proof_text_full":null,"check_data_json":null,"remote_id":null,"api_url":null,"human_url":null,"proof_state":null,"proof_status":null,"retry_count":null,"hard_fail_count":null,"last_check":null,"last_success":null,"version":null,"fingerprint":"a889587e1ce7bd7edbe3eeb8ef8fd8f7b31c4a2f"}`
// ParseTlfHandle parses a TlfHandle from an encoded string. See // TlfHandle.GetCanonicalName() for the opposite direction. // // Some errors that may be returned and can be specially handled: // // TlfNameNotCanonical: Returned when the given name is not canonical // -- another name to try (which itself may not be canonical) is in // the error. Usually, you want to treat this as a symlink to the name // to try. // // NoSuchNameError: Returned when public is set and the given folder // has no public folder. func ParseTlfHandle( ctx context.Context, kbpki KBPKI, name string, public bool, sharingBeforeSignupEnabled bool) (*TlfHandle, error) { // Before parsing the tlf handle (which results in identify // calls that cause tracker popups), first see if there's any // quick normalization of usernames we can do. For example, // this avoids an identify in the case of "HEAD" which might // just be a shell trying to look for a git repo rather than a // real user lookup for "head" (KBFS-531). Note that the name // might still contain assertions, which will result in // another alias in a subsequent lookup. writerNames, readerNames, conflictSuffix, err := splitAndNormalizeTLFName(name, public) if err != nil { return nil, err } hasPublic := len(readerNames) == 0 if public && !hasPublic { // No public folder exists for this folder. return nil, NoSuchNameError{Name: name} } normalizedName, err := normalizeNamesInTLF(writerNames, readerNames, conflictSuffix) if err != nil { return nil, err } if normalizedName != name { return nil, TlfNameNotCanonical{name, normalizedName} } writers := make([]resolvableUser, len(writerNames)) for i, w := range writerNames { writers[i] = resolvableAssertion{sharingBeforeSignupEnabled, kbpki, w, keybase1.UID("")} } readers := make([]resolvableUser, len(readerNames)) for i, r := range readerNames { readers[i] = resolvableAssertion{sharingBeforeSignupEnabled, kbpki, r, keybase1.UID("")} } var conflictInfo *ConflictInfo if len(conflictSuffix) != 0 { conflictInfo, err = ParseConflictInfo(conflictSuffix) if err != nil { return nil, err } } h, err := makeTlfHandleHelper(ctx, public, writers, readers, conflictInfo) if err != nil { return nil, err } if !public { currentUsername, currentUID, err := kbpki.GetCurrentUserInfo(ctx) if err != nil { return nil, err } canRead := false for _, uid := range append(h.Writers, h.Readers...) { if uid == currentUID { canRead = true break } } if !canRead { return nil, ReadAccessError{currentUsername, h.GetCanonicalName(), public} } } if string(h.GetCanonicalName()) == name { // Name is already canonical (i.e., all usernames and // no assertions) so we can delay the identify until // the node is actually used. return h, nil } // Otherwise, identify before returning the canonical name. err = identifyHandle(ctx, kbpki, kbpki, h) if err != nil { return nil, err } return nil, TlfNameNotCanonical{name, string(h.GetCanonicalName())} }