diff --git a/go/libkb/active_device.go b/go/libkb/active_device.go index df0549dbca3e..c5ba188fa204 100644 --- a/go/libkb/active_device.go +++ b/go/libkb/active_device.go @@ -33,7 +33,7 @@ func (a *ActiveDevice) Dump(m MetaContext, prefix string) { m.Debug("%sUsername (via env): %s", prefix, a.Username(m)) m.Debug("%sDeviceID: %s", prefix, a.deviceID) m.Debug("%sDeviceName: %s", prefix, a.deviceName) - m.Debug("%sDeviceCtime: %s", prefix, a.deviceCtime) + m.Debug("%sDeviceCtime: %s", prefix, keybase1.FormatTime(a.deviceCtime)) if a.signingKey != nil { m.Debug("%sSigKey: %s", prefix, a.signingKey.GetKID()) } diff --git a/go/libkb/all_provisioned_usernames.go b/go/libkb/all_provisioned_usernames.go index 4e03cc8d0f05..e961b229f089 100644 --- a/go/libkb/all_provisioned_usernames.go +++ b/go/libkb/all_provisioned_usernames.go @@ -16,7 +16,7 @@ func getUsernameIfProvisioned(m MetaContext, uc UserConfig) (ret NormalizedUsern m.Debug("- user was likely reset (%s)", err) return ret, nil case KeyRevokedError: - m.Debug("- device was revoked (s)", err) + m.Debug("- device was revoked (%s)", err) return ret, nil case UserDeletedError: m.Debug(" - user was deleted (%s)", err) diff --git a/go/service/random_pw_test.go b/go/service/random_pw_test.go index a8ae4bc1f6ac..71a632200647 100644 --- a/go/service/random_pw_test.go +++ b/go/service/random_pw_test.go @@ -81,6 +81,16 @@ func (r *errorAPIMock) GetDecode(mctx libkb.MetaContext, arg libkb.APIArg, w lib return r.realAPI.GetDecode(mctx, arg, w) } +func (r errorAPIMock) Get(mctx libkb.MetaContext, arg libkb.APIArg) (*libkb.APIRes, error) { + if arg.Endpoint == "user/has_random_pw" { + r.callCount++ + if r.shouldTimeout { + return nil, errors.New("timeout or something") + } + } + return r.realAPI.Get(mctx, arg) +} + func TestCanLogoutTimeout(t *testing.T) { tc := libkb.SetupTest(t, "randompw", 3) defer tc.Cleanup() @@ -165,3 +175,51 @@ func TestCanLogoutTimeout(t *testing.T) { require.Contains(t, err.Error(), "timeout or something") require.Equal(t, 1, fakeAPI.callCount) } + +func TestCanLogoutWhenRevoked(t *testing.T) { + tc := libkb.SetupTest(t, "randompw", 3) + defer tc.Cleanup() + + user, err := kbtest.CreateAndSignupFakeUserRandomPW("rpw", tc.G) + require.NoError(t, err) + + userHandler := NewUserHandler(nil, tc.G, nil, nil) + ret, err := userHandler.CanLogout(context.Background(), 0) + require.NoError(t, err) + require.False(t, ret.CanLogout) + + // Provision second device + tc2 := libkb.SetupTest(t, "randompw2", 3) + defer tc2.Cleanup() + kbtest.ProvisionNewDeviceKex(&tc, &tc2, user, libkb.DeviceTypeDesktop) + + // Should still see "can't logout" on second device (also populate + // HasRandomPW cache). + userHandler2 := NewUserHandler(nil, tc2.G, nil, nil) + ret, err = userHandler2.CanLogout(context.Background(), 0) + require.NoError(t, err) + require.False(t, ret.CanLogout) + + // Revoke device 2 + revokeEng := engine.NewRevokeDeviceEngine(tc.G, engine.RevokeDeviceEngineArgs{ + ID: tc2.G.ActiveDevice.DeviceID(), + }) + uis := libkb.UIs{ + SecretUI: &libkb.TestSecretUI{}, + LogUI: tc.G.UI.GetLogUI(), + } + m := libkb.NewMetaContextForTest(tc).WithUIs(uis) + err = engine.RunEngine2(m, revokeEng) + require.NoError(t, err) + + // Try CanLogout from device 2. Should detect that we are revoked and let + // us log out. + ret, err = userHandler2.CanLogout(context.Background(), 0) + require.NoError(t, err) + require.True(t, ret.CanLogout) + + // Device 1 still can't logout. + ret, err = userHandler.CanLogout(context.Background(), 0) + require.NoError(t, err) + require.False(t, ret.CanLogout) +} diff --git a/go/service/user.go b/go/service/user.go index f9fff5954d63..c86d25eea2ba 100644 --- a/go/service/user.go +++ b/go/service/user.go @@ -572,7 +572,7 @@ func (h *UserHandler) LoadHasRandomPw(ctx context.Context, arg keybase1.LoadHasR m = m.WithLogTag("HASRPW") defer m.TraceTimed(fmt.Sprintf("UserHandler#LoadHasRandomPw(forceRepoll=%t)", arg.ForceRepoll), func() error { return err })() - meUID := m.G().ActiveDevice.UID() + meUID := m.ActiveDevice().UID() cacheKey := libkb.DbKey{ Typ: libkb.DBHasRandomPW, Key: meUID.String(), @@ -645,6 +645,20 @@ func (h *UserHandler) CanLogout(ctx context.Context, sessionID int) (res keybase return res, nil } + if err := libkb.CheckCurrentUIDDeviceID(libkb.NewMetaContext(ctx, h.G())); err != nil { + switch err.(type) { + case libkb.DeviceNotFoundError, libkb.UserNotFoundError, + libkb.KeyRevokedError, libkb.NoDeviceError, libkb.NoUIDError: + h.G().Log.CDebugf(ctx, "CanLogout: allowing logout because of CheckCurrentUIDDeviceID returning: %s", err.Error()) + return keybase1.CanLogoutRes{CanLogout: true}, nil + default: + // Unexpected error like network connectivity issue, fall through. + // Even if we are offline here, we may be able to get cached value + // `false` from LoadHasRandomPw and be allowed to log out. + h.G().Log.CDebugf(ctx, "CanLogout: CheckCurrentUIDDeviceID returned: %q, falling through", err.Error()) + } + } + hasRandomPW, err := h.LoadHasRandomPw(ctx, keybase1.LoadHasRandomPwArg{ SessionID: sessionID, ForceRepoll: false,