github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/keybase_daemon_rpc_test.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 "strings" 10 "testing" 11 "time" 12 13 "github.com/golang/mock/gomock" 14 "github.com/keybase/client/go/kbfs/idutil" 15 "github.com/keybase/client/go/kbfs/kbfscrypto" 16 "github.com/keybase/client/go/kbfs/test/clocktest" 17 "github.com/keybase/client/go/kbfs/tlf" 18 kbname "github.com/keybase/client/go/kbun" 19 "github.com/keybase/client/go/logger" 20 "github.com/keybase/client/go/protocol/keybase1" 21 "github.com/keybase/go-framed-msgpack-rpc/rpc" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "golang.org/x/net/context" 25 ) 26 27 // If we cancel the RPC before the RPC returns, the call should error quickly. 28 func TestKeybaseDaemonRPCIdentifyCanceled(t *testing.T) { 29 serverConn, conn := rpc.MakeConnectionForTest(t) 30 daemon := newKeybaseDaemonRPCWithClient( 31 nil, 32 conn.GetClient(), 33 logger.NewTestLogger(t)) 34 35 f := func(ctx context.Context) error { 36 _, _, err := daemon.Identify( 37 ctx, "", "", keybase1.OfflineAvailability_NONE) 38 return err 39 } 40 testRPCWithCanceledContext(t, serverConn, f) 41 } 42 43 // If we cancel the RPC before the RPC returns, the call should error quickly. 44 func TestKeybaseDaemonRPCGetCurrentSessionCanceled(t *testing.T) { 45 serverConn, conn := rpc.MakeConnectionForTest(t) 46 daemon := newKeybaseDaemonRPCWithClient( 47 nil, 48 conn.GetClient(), 49 logger.NewTestLogger(t)) 50 51 f := func(ctx context.Context) error { 52 _, err := daemon.CurrentSession(ctx, 0) 53 return err 54 } 55 testRPCWithCanceledContext(t, serverConn, f) 56 } 57 58 // TODO: Add tests for Favorite* methods, too. 59 60 type fakeKeybaseClient struct { 61 session idutil.SessionInfo 62 users map[keybase1.UID]idutil.UserInfo 63 currentSessionCalled bool 64 identifyCalled bool 65 loadUserPlusKeysCalled bool 66 loadAllPublicKeysUnverified bool 67 editResponse keybase1.FSEditListArg 68 } 69 70 var _ rpc.GenericClient = (*fakeKeybaseClient)(nil) 71 72 func (c *fakeKeybaseClient) Call(ctx context.Context, s string, args interface{}, 73 res interface{}, _ time.Duration) error { 74 return c.call(ctx, s, args, res) 75 } 76 77 func (c *fakeKeybaseClient) CallCompressed(ctx context.Context, s string, args interface{}, 78 res interface{}, _ rpc.CompressionType, _ time.Duration) error { 79 return c.call(ctx, s, args, res) 80 } 81 82 func (c *fakeKeybaseClient) call(ctx context.Context, s string, args interface{}, res interface{}) error { 83 switch s { 84 case "keybase.1.session.currentSession": 85 *res.(*keybase1.Session) = keybase1.Session{ 86 Uid: c.session.UID, 87 Username: "fake username", 88 DeviceSubkeyKid: c.session.CryptPublicKey.KID(), 89 DeviceSibkeyKid: c.session.VerifyingKey.KID(), 90 } 91 92 c.currentSessionCalled = true 93 return nil 94 95 case "keybase.1.identify.identifyLite": 96 arg := args.([]interface{})[0].(keybase1.IdentifyLiteArg) 97 uidStr := strings.TrimPrefix(arg.Assertion, "uid:") 98 if len(uidStr) == len(arg.Assertion) { 99 return fmt.Errorf("Non-uid assertion %s", arg.Assertion) 100 } 101 102 uid := keybase1.UID(uidStr) 103 userInfo, ok := c.users[uid] 104 if !ok { 105 return fmt.Errorf("Could not find user info for UID %s", uid) 106 } 107 108 *res.(*keybase1.IdentifyLiteRes) = keybase1.IdentifyLiteRes{ 109 Ul: keybase1.UserOrTeamLite{ 110 Id: uid.AsUserOrTeam(), 111 Name: string(userInfo.Name), 112 }, 113 } 114 115 c.identifyCalled = true 116 return nil 117 118 case "keybase.1.user.loadUserPlusKeysV2": 119 arg := args.([]interface{})[0].(keybase1.LoadUserPlusKeysV2Arg) 120 121 userInfo, ok := c.users[arg.Uid] 122 if !ok { 123 return fmt.Errorf("Could not find user info for UID %s", arg.Uid) 124 } 125 126 *res.(*keybase1.UserPlusKeysV2AllIncarnations) = 127 keybase1.UserPlusKeysV2AllIncarnations{ 128 Current: keybase1.UserPlusKeysV2{ 129 Uid: arg.Uid, 130 Username: string(userInfo.Name), 131 }, 132 } 133 134 c.loadUserPlusKeysCalled = true 135 return nil 136 137 case "keybase.1.user.loadAllPublicKeysUnverified": 138 pk := keybase1.PublicKey{ 139 KID: kbfscrypto.MakeFakeVerifyingKeyOrBust("foo").KID(), 140 } 141 *res.(*[]keybase1.PublicKey) = []keybase1.PublicKey{pk} 142 143 c.loadAllPublicKeysUnverified = true 144 return nil 145 146 case "keybase.1.kbfs.FSEditList": 147 c.editResponse = args.([]interface{})[0].(keybase1.FSEditListArg) 148 return nil 149 150 default: 151 return fmt.Errorf("Unknown call: %s %v %v", s, args, res) 152 } 153 } 154 155 func (c *fakeKeybaseClient) Notify(_ context.Context, s string, args interface{}, timeout time.Duration) error { 156 return fmt.Errorf("Unknown notify: %s %v", s, args) 157 } 158 159 const expectCall = true 160 const expectCached = false 161 162 func testCurrentSession( 163 t *testing.T, client *fakeKeybaseClient, c *KeybaseDaemonRPC, 164 expectedSession idutil.SessionInfo, expectedCalled bool) { 165 client.currentSessionCalled = false 166 167 ctx := context.Background() 168 sessionID := 0 169 session, err := c.CurrentSession(ctx, sessionID) 170 require.NoError(t, err) 171 172 assert.Equal(t, expectedSession, session) 173 assert.Equal(t, expectedCalled, client.currentSessionCalled) 174 } 175 176 // Test that the session cache works and is invalidated as expected. 177 func TestKeybaseDaemonSessionCache(t *testing.T) { 178 name := kbname.NormalizedUsername("fake username") 179 k := idutil.MakeLocalUserCryptPublicKeyOrBust(name) 180 v := idutil.MakeLocalUserVerifyingKeyOrBust(name) 181 session := idutil.SessionInfo{ 182 Name: name, 183 UID: keybase1.MakeTestUID(1), 184 CryptPublicKey: k, 185 VerifyingKey: v, 186 } 187 188 client := &fakeKeybaseClient{session: session} 189 c := newKeybaseDaemonRPCWithClient( 190 nil, client, logger.NewTestLogger(t)) 191 192 // Should fill cache. 193 testCurrentSession(t, client, c, session, expectCall) 194 195 // Should be cached. 196 testCurrentSession(t, client, c, session, expectCached) 197 198 // Should invalidate cache. 199 err := c.LoggedOut(context.Background()) 200 require.NoError(t, err) 201 202 // Should fill cache again. 203 testCurrentSession(t, client, c, session, expectCall) 204 205 // Should be cached again. 206 testCurrentSession(t, client, c, session, expectCached) 207 208 // Should invalidate cache. 209 c.OnDisconnected(context.Background(), rpc.UsingExistingConnection) 210 211 // Should fill cache again. 212 testCurrentSession(t, client, c, session, expectCall) 213 } 214 215 func testLoadUserPlusKeys( 216 t *testing.T, client *fakeKeybaseClient, c *KeybaseDaemonRPC, 217 uid keybase1.UID, expectedName kbname.NormalizedUsername, 218 expectedCalled bool) { 219 client.loadUserPlusKeysCalled = false 220 221 ctx := context.Background() 222 info, err := c.LoadUserPlusKeys( 223 ctx, uid, "", keybase1.OfflineAvailability_NONE) 224 require.NoError(t, err) 225 226 assert.Equal(t, expectedName, info.Name) 227 assert.Equal(t, expectedCalled, client.loadUserPlusKeysCalled) 228 } 229 230 func testIdentify( 231 t *testing.T, client *fakeKeybaseClient, c *KeybaseDaemonRPC, 232 uid keybase1.UID, expectedName kbname.NormalizedUsername, 233 expectedCalled bool) { 234 client.identifyCalled = false 235 236 ctx := context.Background() 237 name, _, err := c.Identify( 238 ctx, "uid:"+string(uid), "", keybase1.OfflineAvailability_NONE) 239 require.NoError(t, err) 240 241 assert.Equal(t, expectedName, name) 242 assert.Equal(t, expectedCalled, client.identifyCalled) 243 } 244 245 // Test that the user cache works and is invalidated as expected. 246 func TestKeybaseDaemonUserCache(t *testing.T) { 247 uid1 := keybase1.MakeTestUID(1) 248 uid2 := keybase1.MakeTestUID(2) 249 name1 := kbname.NewNormalizedUsername("name1") 250 name2 := kbname.NewNormalizedUsername("name2") 251 users := map[keybase1.UID]idutil.UserInfo{ 252 uid1: {Name: name1}, 253 uid2: {Name: name2}, 254 } 255 client := &fakeKeybaseClient{users: users} 256 c := newKeybaseDaemonRPCWithClient( 257 nil, client, logger.NewTestLogger(t)) 258 259 // Should fill cache. 260 testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) 261 262 // Should be cached. 263 testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) 264 265 // IdentifyLite doesn't fill the cache. 266 testIdentify(t, client, c, uid2, name2, expectCall) 267 268 // Shouldn't be cached yet after just an identify. 269 testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) 270 271 // Should be cached. 272 testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) 273 274 // Should not be cached. 275 testIdentify(t, client, c, uid2, name2, expectCall) 276 277 // Should invalidate cache for uid1. 278 err := c.KeyfamilyChanged(context.Background(), uid1) 279 require.NoError(t, err) 280 281 // Should fill cache again. 282 testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) 283 284 // Should be cached again. 285 testLoadUserPlusKeys(t, client, c, uid1, name1, expectCached) 286 287 // Should invalidate cache for uid2. 288 err = c.KeyfamilyChanged(context.Background(), uid2) 289 require.NoError(t, err) 290 291 // Should fill cache again. 292 testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) 293 294 // Should be cached again. 295 testLoadUserPlusKeys(t, client, c, uid2, name2, expectCached) 296 297 // Should invalidate cache for all users. 298 c.OnDisconnected(context.Background(), rpc.UsingExistingConnection) 299 300 // Should fill cache again. 301 testLoadUserPlusKeys(t, client, c, uid1, name1, expectCall) 302 testLoadUserPlusKeys(t, client, c, uid2, name2, expectCall) 303 304 // Test that CheckForRekey gets called only if the logged-in user 305 // changes. 306 session := idutil.SessionInfo{ 307 UID: uid1, 308 } 309 c.setCachedCurrentSession(session) 310 ctr := NewSafeTestReporter(t) 311 mockCtrl := gomock.NewController(ctr) 312 config := NewConfigMock(mockCtrl, ctr) 313 c.config = config 314 defer func() { 315 config.ctr.CheckForFailures() 316 mockCtrl.Finish() 317 }() 318 errChan := make(chan error, 1) 319 config.mockMdserv.EXPECT().CheckForRekeys(gomock.Any()).Do( 320 func(ctx context.Context) { 321 errChan <- nil 322 }).Return(errChan) 323 err = c.KeyfamilyChanged(context.Background(), uid1) 324 require.NoError(t, err) 325 <-errChan 326 // This one shouldn't trigger CheckForRekeys; if it does, the mock 327 // controller will catch it during Finish. 328 err = c.KeyfamilyChanged(context.Background(), uid2) 329 require.NoError(t, err) 330 } 331 332 func TestKeybaseDaemonRPCEditList(t *testing.T) { 333 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 334 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 335 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 336 // kbfsOpsConcurInit turns off notifications, so turn them back on. 337 config1.SetMode(modeTest{NewInitModeFromType(InitDefault)}) 338 339 clock, first := clocktest.NewTestClockAndTimeNow() 340 config1.SetClock(clock) 341 342 config2 := ConfigAsUser(config1, userName2) 343 defer CheckConfigAndShutdown(ctx, t, config2) 344 345 name := userName1.String() + "," + userName2.String() 346 347 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 348 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 349 350 // user 1 creates a file 351 kbfsOps1 := config1.KBFSOps() 352 _, _, err := kbfsOps1.CreateFile( 353 ctx, rootNode1, testPPS("a"), false, NoExcl) 354 require.NoError(t, err) 355 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 356 require.NoError(t, err) 357 358 kbfsOps2 := config2.KBFSOps() 359 err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch(), nil) 360 require.NoError(t, err) 361 362 clock.Add(1 * time.Minute) 363 second := clock.Now() 364 365 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("b"), false, NoExcl) 366 require.NoError(t, err) 367 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 368 require.NoError(t, err) 369 370 err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch(), nil) 371 require.NoError(t, err) 372 err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch(), nil) 373 require.NoError(t, err) 374 375 session1, err := config1.KBPKI().GetCurrentSession(context.Background()) 376 require.NoError(t, err) 377 uid1 := session1.UID 378 session2, err := config2.KBPKI().GetCurrentSession(context.Background()) 379 require.NoError(t, err) 380 uid2 := session2.UID 381 382 // We should see 1 create edit for each user. 383 expectedHistory := keybase1.FSFolderEditHistory{ 384 Folder: keybase1.Folder{ 385 Name: name, 386 FolderType: keybase1.FolderType_PRIVATE, 387 Private: true, 388 }, 389 ServerTime: keybase1.ToTime(second), 390 History: []keybase1.FSFolderWriterEditHistory{ 391 { 392 WriterName: "u2", 393 Edits: []keybase1.FSFolderWriterEdit{{ 394 Filename: "/keybase/private/u1,u2/b", 395 NotificationType: keybase1.FSNotificationType_FILE_MODIFIED, 396 ServerTime: keybase1.ToTime(second), 397 }}, 398 Deletes: []keybase1.FSFolderWriterEdit{}, 399 }, 400 { 401 WriterName: "u1", 402 Edits: []keybase1.FSFolderWriterEdit{{ 403 Filename: "/keybase/private/u1,u2/a", 404 NotificationType: keybase1.FSNotificationType_FILE_MODIFIED, 405 ServerTime: keybase1.ToTime(first), 406 }}, 407 Deletes: []keybase1.FSFolderWriterEdit{}, 408 }, 409 }, 410 } 411 412 users := map[keybase1.UID]idutil.UserInfo{ 413 uid1: {Name: userName1}, 414 uid2: {Name: userName2}, 415 } 416 client1 := &fakeKeybaseClient{users: users} 417 c1 := newKeybaseDaemonRPCWithClient( 418 nil, client1, logger.NewTestLogger(t)) 419 c1.config = config1 420 421 reqID := 10 422 err = c1.FSEditListRequest(ctx, keybase1.FSEditListRequest{ 423 Folder: keybase1.Folder{Name: name, Private: true}, 424 RequestID: reqID, 425 }) 426 require.NoError(t, err) 427 history := client1.editResponse.Edits 428 require.Equal(t, expectedHistory, history) 429 }