github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/ftl_test.go (about) 1 package teams 2 3 import ( 4 "testing" 5 6 "github.com/keybase/client/go/kbtest" 7 "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/protocol/keybase1" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func TestFastLoaderBasic(t *testing.T) { 13 tc := SetupTest(t, "team", 1) 14 defer tc.Cleanup() 15 16 _, err := kbtest.CreateAndSignupFakeUser("team", tc.G) 17 require.NoError(t, err) 18 19 t.Logf("create a team") 20 teamName, teamID := createTeam2(tc) 21 22 t.Logf("load the team") 23 arg := keybase1.FastTeamLoadArg{ 24 ID: teamID, 25 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 26 NeedLatestKey: true, 27 } 28 m := libkb.NewMetaContextForTest(tc) 29 team, err := tc.G.GetFastTeamLoader().Load(m, arg) 30 require.NoError(t, err) 31 require.Equal(t, len(team.ApplicationKeys), 1) 32 require.True(t, teamName.Eq(team.Name)) 33 34 t.Logf("load the team again") 35 team, err = tc.G.GetFastTeamLoader().Load(m, arg) 36 require.NoError(t, err) 37 require.Equal(t, len(team.ApplicationKeys), 1) 38 require.True(t, teamName.Eq(team.Name)) 39 } 40 41 // Test fast loading a team that does several key rotations. 42 func TestFastLoaderKeyGen(t *testing.T) { 43 fus, tcs, cleanup := setupNTests(t, 4) 44 defer cleanup() 45 46 t.Logf("create team") 47 teamName, teamID := createTeam2(*tcs[0]) 48 m := make([]libkb.MetaContext, 4) 49 for i, tc := range tcs { 50 m[i] = libkb.NewMetaContextForTest(*tc) 51 } 52 53 t.Logf("add B to the team so they can load it") 54 _, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 55 require.NoError(t, err) 56 t.Logf("add C to the team so they can load it") 57 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[2].Username, keybase1.TeamRole_BOT, nil) 58 require.NoError(t, err) 59 t.Logf("add D to the team so they can load it") 60 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{}) 61 require.NoError(t, err) 62 63 t.Logf("B's first load at gen 1") 64 arg := keybase1.FastTeamLoadArg{ 65 ID: teamID, 66 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 67 NeedLatestKey: true, 68 } 69 team, err := tcs[1].G.GetFastTeamLoader().Load(m[1], arg) 70 require.NoError(t, err) 71 require.Equal(t, len(team.ApplicationKeys), 1) 72 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 73 require.True(t, teamName.Eq(team.Name)) 74 75 t.Logf("C's first load at gen 1") 76 arg = keybase1.FastTeamLoadArg{ 77 ID: teamID, 78 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 79 NeedLatestKey: true, 80 } 81 team, err = tcs[2].G.GetFastTeamLoader().Load(m[2], arg) 82 require.NoError(t, err) 83 require.Equal(t, len(team.ApplicationKeys), 1) 84 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 85 require.True(t, teamName.Eq(team.Name)) 86 87 t.Logf("D's first load at gen 1") 88 arg = keybase1.FastTeamLoadArg{ 89 ID: teamID, 90 } 91 team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg) 92 require.NoError(t, err) 93 // since D is a restricted bot, they should not have access to any keys 94 require.Zero(t, len(team.ApplicationKeys)) 95 require.True(t, teamName.Eq(team.Name)) 96 arg = keybase1.FastTeamLoadArg{ 97 ID: teamID, 98 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 99 NeedLatestKey: true, 100 } 101 team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg) 102 require.Error(t, err) 103 require.IsType(t, FTLMissingSeedError{}, err) 104 require.Zero(t, len(team.ApplicationKeys)) 105 106 t.Logf("rotate the key a bunch of times") 107 // Rotate the key by removing and adding B from the team 108 for i := 0; i < 3; i++ { 109 err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username) 110 require.NoError(t, err) 111 112 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 113 require.NoError(t, err) 114 } 115 116 t.Logf("load as A to check the progression") 117 team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 118 require.NoError(t, err) 119 require.Equal(t, len(team.ApplicationKeys), 1) 120 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4)) 121 require.True(t, teamName.Eq(team.Name)) 122 123 t.Logf("B loads the new PTK by number") 124 arg.NeedLatestKey = false 125 arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(4)} 126 team, err = tcs[1].G.GetFastTeamLoader().Load(m[1], arg) 127 require.NoError(t, err) 128 require.Equal(t, len(team.ApplicationKeys), 1) 129 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4)) 130 require.True(t, teamName.Eq(team.Name)) 131 132 t.Logf("B loads the new PTK by latest") 133 arg.NeedLatestKey = true 134 arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{} 135 team, err = tcs[1].G.GetFastTeamLoader().Load(m[1], arg) 136 require.NoError(t, err) 137 require.Equal(t, len(team.ApplicationKeys), 1) 138 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4)) 139 require.True(t, teamName.Eq(team.Name)) 140 141 t.Logf("clear A's FTL state") 142 ftl, ok := tcs[0].G.GetFastTeamLoader().(*FastTeamChainLoader) 143 require.True(t, ok) 144 require.NoError(t, ftl.OnLogout(m[0])) 145 146 t.Logf("more tests as A; let's first load at generation=1") 147 arg = keybase1.FastTeamLoadArg{ 148 ID: teamID, 149 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 150 KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(1)}, 151 } 152 team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 153 require.NoError(t, err) 154 require.Equal(t, len(team.ApplicationKeys), 1) 155 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 156 require.True(t, teamName.Eq(team.Name)) 157 158 t.Logf("let's now load at the latest generation") 159 arg = keybase1.FastTeamLoadArg{ 160 ID: teamID, 161 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 162 NeedLatestKey: true, 163 } 164 team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 165 require.NoError(t, err) 166 require.Equal(t, len(team.ApplicationKeys), 1) 167 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4)) 168 require.True(t, teamName.Eq(team.Name)) 169 170 t.Logf("make sure D still doesn't have access") 171 arg = keybase1.FastTeamLoadArg{ 172 ID: teamID, 173 } 174 team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg) 175 require.NoError(t, err) 176 require.Zero(t, len(team.ApplicationKeys)) 177 require.True(t, teamName.Eq(team.Name)) 178 179 arg = keybase1.FastTeamLoadArg{ 180 ID: teamID, 181 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 182 KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(1)}, 183 } 184 team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg) 185 require.Error(t, err) 186 require.IsType(t, FTLMissingSeedError{}, err) 187 require.Zero(t, len(team.ApplicationKeys)) 188 189 t.Logf("upgrade D to a bot and check they have access") 190 err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username) 191 require.NoError(t, err) 192 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username, keybase1.TeamRole_BOT, nil) 193 require.NoError(t, err) 194 195 arg.NeedLatestKey = false 196 arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(4)} 197 team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg) 198 require.NoError(t, err) 199 require.Equal(t, len(team.ApplicationKeys), 1) 200 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4)) 201 require.True(t, teamName.Eq(team.Name)) 202 } 203 204 // Test loading a sub-sub-team: a.b.c. 205 func TestFastLoaderMultilevel(t *testing.T) { 206 fus, tcs, cleanup := setupNTests(t, 2) 207 defer cleanup() 208 209 t.Logf("create a team") 210 parentName, _ := createTeam2(*tcs[0]) 211 212 t.Logf("create a subteam (of parent %s)", parentName) 213 m := make([]libkb.MetaContext, 2) 214 for i, tc := range tcs { 215 m[i] = libkb.NewMetaContextForTest(*tc) 216 } 217 _, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", parentName, keybase1.TeamRole_NONE /* addSelfAs */) 218 require.NoError(t, err) 219 220 subTeamName, err := parentName.Append("abc") 221 require.NoError(t, err) 222 t.Logf("create a sub-subteam (of parent %s)", subTeamName) 223 subsubteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "def", subTeamName, keybase1.TeamRole_NONE /* addSelfAs */) 224 require.NoError(t, err) 225 226 expectedSubsubTeamName, err := subTeamName.Append("def") 227 require.NoError(t, err) 228 t.Logf("subsubteam is: %s (%s)", expectedSubsubTeamName.String(), *subsubteamID) 229 230 t.Logf("add the other user to the subsubteam") 231 _, err = AddMember(m[0].Ctx(), tcs[0].G, expectedSubsubTeamName.String(), fus[1].Username, keybase1.TeamRole_WRITER, nil) 232 require.NoError(t, err) 233 234 t.Logf("load the subteam") 235 arg := keybase1.FastTeamLoadArg{ 236 ID: *subsubteamID, 237 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 238 NeedLatestKey: true, 239 } 240 team, err := tcs[1].G.GetFastTeamLoader().Load(m[1], arg) 241 require.NoError(t, err) 242 require.Equal(t, len(team.ApplicationKeys), 1) 243 require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 244 require.True(t, expectedSubsubTeamName.Eq(team.Name)) 245 } 246 247 func TestFastLoaderUpPointerUnstub(t *testing.T) { 248 fus, tcs, cleanup := setupNTests(t, 2) 249 defer cleanup() 250 251 // Require that a team is at this key generation 252 t.Logf("create team") 253 teamName, teamID := createTeam2(*tcs[0]) 254 t.Logf("add B to the team so they can load it") 255 m := make([]libkb.MetaContext, 2) 256 for i, tc := range tcs { 257 m[i] = libkb.NewMetaContextForTest(*tc) 258 } 259 _, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 260 require.NoError(t, err) 261 subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */) 262 require.NoError(t, err) 263 264 expectedSubTeamName, err := teamName.Append("abc") 265 require.NoError(t, err) 266 t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID) 267 268 t.Logf("rotate the key a bunch of times") 269 // Rotate the key by removing and adding B from the team 270 for i := 0; i < 3; i++ { 271 err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username) 272 require.NoError(t, err) 273 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 274 require.NoError(t, err) 275 } 276 t.Logf("load the team") 277 arg := keybase1.FastTeamLoadArg{ 278 ID: teamID, 279 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 280 NeedLatestKey: true, 281 } 282 _, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 283 require.NoError(t, err) 284 285 loadSubteam := func() { 286 t.Logf("load the subteam") 287 arg = keybase1.FastTeamLoadArg{ 288 ID: *subteamID, 289 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 290 NeedLatestKey: true, 291 } 292 team, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 293 require.NoError(t, err) 294 require.True(t, expectedSubTeamName.Eq(team.Name)) 295 } 296 297 // Try again via the unstub system 298 loadSubteam() 299 300 // Also check that it works on a fresh load on a clean cache (thought this 301 // duplicates what we did in TestFastLoaderMultilevel) 302 ftl, ok := tcs[0].G.GetFastTeamLoader().(*FastTeamChainLoader) 303 require.True(t, ok) 304 require.NoError(t, ftl.OnLogout(m[0])) 305 loadSubteam() 306 } 307 308 // See CORE-8859, there was a bug that showed up when we loaded the subteam first and then 309 // the parent, since when we loaded the subteam, we were in "subteam reader" mode, and 310 // due to a previous server bug, didn't get boxes and prevs back when in subteam reader mode. 311 // Then, when we tried to access a box in the parent, we would fail. Test that it works. 312 func TestLoadSubteamThenParent(t *testing.T) { 313 fus, tcs, cleanup := setupNTests(t, 2) 314 defer cleanup() 315 316 t.Logf("create team") 317 teamName, teamID := createTeam2(*tcs[0]) 318 t.Logf("add B to the team so they can load it") 319 m := make([]libkb.MetaContext, 2) 320 for i, tc := range tcs { 321 m[i] = libkb.NewMetaContextForTest(*tc) 322 } 323 _, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 324 require.NoError(t, err) 325 subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */) 326 require.NoError(t, err) 327 328 expectedSubTeamName, err := teamName.Append("abc") 329 require.NoError(t, err) 330 t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID) 331 332 t.Logf("rotate the parent team a bunch of times") 333 // Rotate the key by removing and adding B from the team 334 for i := 0; i < 3; i++ { 335 err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username) 336 require.NoError(t, err) 337 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 338 require.NoError(t, err) 339 } 340 341 loadSubteam := func() { 342 t.Logf("load the subteam") 343 arg := keybase1.FastTeamLoadArg{ 344 ID: *subteamID, 345 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 346 NeedLatestKey: true, 347 } 348 team, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 349 require.NoError(t, err) 350 require.True(t, expectedSubTeamName.Eq(team.Name)) 351 } 352 353 loadTeam := func(g keybase1.PerTeamKeyGeneration) { 354 t.Logf("load the team") 355 arg := keybase1.FastTeamLoadArg{ 356 ID: teamID, 357 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 358 KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{g}, 359 } 360 _, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 361 require.NoError(t, err) 362 } 363 364 loadSubteam() 365 loadTeam(3) 366 } 367 368 // See CORE-9207, there was a bug with this order of operations: (1) loading foo.bar; 369 // (2) being let into foo; (3) loading foo. 370 func TestLoadSubteamThenAllowedInThenParent(t *testing.T) { 371 fus, tcs, cleanup := setupNTests(t, 3) 372 defer cleanup() 373 374 t.Logf("create team") 375 teamName, teamID := createTeam2(*tcs[0]) 376 t.Logf("add B to the team so they can load it") 377 m := make([]libkb.MetaContext, 3) 378 for i, tc := range tcs { 379 m[i] = libkb.NewMetaContextForTest(*tc) 380 } 381 382 rotateKey := func(name keybase1.TeamName) { 383 _, err := AddMember(m[0].Ctx(), tcs[0].G, name.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 384 require.NoError(t, err) 385 err = RemoveMember(m[0].Ctx(), tcs[0].G, name.String(), fus[1].Username) 386 require.NoError(t, err) 387 } 388 389 for i := 0; i < 3; i++ { 390 rotateKey(teamName) 391 } 392 393 subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_ADMIN /* addSelfAs */) 394 require.NoError(t, err) 395 expectedSubTeamName, err := teamName.Append("abc") 396 require.NoError(t, err) 397 _, err = AddMember(m[0].Ctx(), tcs[0].G, expectedSubTeamName.String(), fus[2].Username, keybase1.TeamRole_WRITER, nil) 398 require.NoError(t, err) 399 400 loadTeam := func(teamID keybase1.TeamID, g keybase1.PerTeamKeyGeneration) { 401 t.Logf("load the team") 402 arg := keybase1.FastTeamLoadArg{ 403 ID: teamID, 404 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 405 KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{g}, 406 } 407 _, err := tcs[2].G.GetFastTeamLoader().Load(m[2], arg) 408 require.NoError(t, err) 409 } 410 411 loadTeam(*subteamID, 1) 412 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[2].Username, keybase1.TeamRole_WRITER, nil) 413 require.NoError(t, err) 414 loadTeam(teamID, 1) 415 } 416 417 // See CORE-8894 for what happened here. The flow is: (1) user loads parent team at generation=N; 418 // (2) there's a key rotation; (3) loads child team and gets the new box and prevs for generation=N+1, 419 // but no RKMs; (4) loads the RKMs for the most recent generation. Test a fix for this case. 420 func TestLoadRKMForLatestCORE8894(t *testing.T) { 421 fus, tcs, cleanup := setupNTests(t, 2) 422 defer cleanup() 423 424 // Require that a team is at this key generation 425 t.Logf("create team") 426 teamName, teamID := createTeam2(*tcs[0]) 427 t.Logf("add B to the team so they can load it") 428 m := make([]libkb.MetaContext, 2) 429 for i, tc := range tcs { 430 m[i] = libkb.NewMetaContextForTest(*tc) 431 } 432 _, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 433 require.NoError(t, err) 434 subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */) 435 require.NoError(t, err) 436 437 expectedSubTeamName, err := teamName.Append("abc") 438 require.NoError(t, err) 439 t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID) 440 441 loadTeam := func(id keybase1.TeamID, forceRefresh bool) { 442 t.Logf("load the team %s", id) 443 arg := keybase1.FastTeamLoadArg{ 444 ID: id, 445 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_CHAT}, 446 ForceRefresh: forceRefresh, 447 NeedLatestKey: true, 448 } 449 _, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg) 450 require.NoError(t, err) 451 } 452 453 loadTeam(teamID, false) 454 455 // Rotate the key by removing and adding B from the team 456 for i := 0; i < 3; i++ { 457 err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username) 458 require.NoError(t, err) 459 _, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil) 460 require.NoError(t, err) 461 } 462 err = RotateKeyVisible(m[0].Ctx(), tcs[0].G, teamID) 463 require.NoError(t, err) 464 465 loadTeam(*subteamID, true) 466 loadTeam(teamID, false) 467 }