github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/identify3/identify3_test.go (about) 1 package identify3 2 3 import ( 4 "io" 5 "net/http" 6 "sync" 7 "testing" 8 9 "github.com/keybase/client/go/engine" 10 "github.com/keybase/client/go/externalstest" 11 "github.com/keybase/client/go/kbtest" 12 "github.com/keybase/client/go/libkb" 13 keybase1 "github.com/keybase/client/go/protocol/keybase1" 14 insecureTriplesec "github.com/keybase/go-triplesec-insecure" 15 "github.com/stretchr/testify/require" 16 "golang.org/x/net/context" 17 ) 18 19 func SetupTest(tb libkb.TestingTB, name string) libkb.TestContext { 20 tc := externalstest.SetupTest(tb, name, 2) 21 // use an insecure triplesec in tests 22 tc.G.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) { 23 warner := func() { tc.G.Log.Warning("Installing insecure Triplesec with weak stretch parameters") } 24 isProduction := func() bool { 25 return tc.G.Env.GetRunMode() == libkb.ProductionRunMode 26 } 27 return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction) 28 } 29 return tc 30 } 31 32 type id3results struct { 33 resultType keybase1.Identify3ResultType 34 rows []keybase1.Identify3Row 35 cards []keybase1.UserCard 36 timedOut bool 37 userWasReset bool 38 numProofsToCheck int 39 } 40 41 func (r *id3results) pushRow(row keybase1.Identify3Row) { 42 r.rows = append(r.rows, row) 43 } 44 45 func (r *id3results) pushUserCard(card keybase1.UserCard) { 46 r.cards = append(r.cards, card) 47 } 48 49 func (r *id3results) hitTimeout() { 50 r.timedOut = true 51 } 52 53 func (r *id3results) hitUserReset() { 54 r.userWasReset = true 55 } 56 57 type fakeUI3 struct { 58 sync.Mutex 59 resultCh chan<- keybase1.Identify3ResultType 60 id3results id3results 61 } 62 63 func (f *fakeUI3) Identify3ShowTracker(context.Context, keybase1.Identify3ShowTrackerArg) error { 64 return nil 65 } 66 func (f *fakeUI3) Identify3UpdateRow(_ context.Context, row keybase1.Identify3Row) error { 67 f.Lock() 68 defer f.Unlock() 69 f.id3results.pushRow(row) 70 return nil 71 } 72 func (f *fakeUI3) Identify3UserReset(context.Context, keybase1.Identify3GUIID) error { 73 f.Lock() 74 defer f.Unlock() 75 f.id3results.hitUserReset() 76 return nil 77 } 78 func (f *fakeUI3) Identify3UpdateUserCard(_ context.Context, card keybase1.Identify3UpdateUserCardArg) error { 79 f.Lock() 80 defer f.Unlock() 81 f.id3results.pushUserCard(card.Card) 82 return nil 83 } 84 func (f *fakeUI3) Identify3TrackerTimedOut(context.Context, keybase1.Identify3GUIID) error { 85 f.Lock() 86 defer f.Unlock() 87 f.id3results.hitTimeout() 88 return nil 89 } 90 func (f *fakeUI3) Identify3Result(_ context.Context, res keybase1.Identify3ResultArg) error { 91 f.Lock() 92 f.id3results.resultType = res.Result 93 f.Unlock() 94 f.resultCh <- res.Result 95 return nil 96 } 97 func (f *fakeUI3) Identify3Summary(_ context.Context, arg keybase1.Identify3Summary) error { 98 f.Lock() 99 f.id3results.numProofsToCheck = arg.NumProofsToCheck 100 f.Unlock() 101 return nil 102 } 103 104 func (f *fakeUI3) results() id3results { 105 f.Lock() 106 defer f.Unlock() 107 return f.id3results 108 } 109 110 func findRows(t *testing.T, haystack []keybase1.Identify3Row, needles []keybase1.Identify3Row) { 111 i := 0 112 for _, h := range haystack { 113 needle := needles[i] 114 if h.Key != needle.Key || h.Value != needle.Value { 115 continue 116 } 117 if needle.SiteURL != "" { 118 require.Equal(t, h.SiteURL, needle.SiteURL) 119 } 120 if needle.ProofURL != "" { 121 require.Equal(t, h.ProofURL, needle.ProofURL) 122 } 123 if needle.State != keybase1.Identify3RowState(0) { 124 require.Equal(t, h.State, needle.State) 125 } 126 if needle.Color != keybase1.Identify3RowColor(0) { 127 require.Equal(t, h.Color, needle.Color) 128 } 129 if needle.Metas != nil { 130 require.Equal(t, h.Metas, needle.Metas) 131 } 132 i++ 133 if i == len(needles) { 134 return 135 } 136 } 137 require.Fail(t, "didn't find all wanted rows") 138 } 139 140 func addBTCAddr(tc libkb.TestContext, u *kbtest.FakeUser, addr string) { 141 t := tc.T 142 uis := libkb.UIs{ 143 LogUI: tc.G.UI.GetLogUI(), 144 SecretUI: u.NewSecretUI(), 145 } 146 e := engine.NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: addr}) 147 m := libkb.NewMetaContextForTest(tc).WithUIs(uis) 148 err := engine.RunEngine2(m, e) 149 require.NoError(t, err) 150 } 151 152 func TestCryptocurrency(t *testing.T) { 153 tc := SetupTest(t, "id3") 154 defer tc.Cleanup() 155 alice, err := kbtest.CreateAndSignupFakeUser("alice", tc.G) 156 157 // The keybase Bitcoin pin address. 158 addr := "1HUCBSJeHnkhzrVKVjaVmWg2QtZS1mdfaz" 159 addBTCAddr(tc, alice, addr) 160 require.NoError(t, err) 161 bob, err := kbtest.CreateAndSignupFakeUser("bob", tc.G) 162 require.NoError(t, err) 163 164 assertTrackResult := func(res id3results, green bool) { 165 require.False(t, res.userWasReset) 166 167 // We get one row of results, just the cryptocurrency row. 168 require.Equal(t, 1, len(res.rows)) 169 require.Equal(t, "btc", res.rows[0].Key) 170 require.Equal(t, addr, res.rows[0].Value) 171 if green { 172 require.Equal(t, keybase1.Identify3RowColor_GREEN, res.rows[0].Color) 173 } else { 174 require.Equal(t, keybase1.Identify3RowColor_BLUE, res.rows[0].Color) 175 } 176 require.Equal(t, keybase1.Identify3RowState_VALID, res.rows[0].State) 177 } 178 179 mctx := libkb.NewMetaContextForTest(tc) 180 res := runID3(t, mctx, alice.Username, true) 181 // Row color should be blue because we are not tracking. 182 assertTrackResult(res, false /* green */) 183 184 _, err = kbtest.RunTrack(tc, bob, alice.Username) 185 require.NoError(t, err) 186 187 res = runID3(t, mctx, alice.Username, true) 188 assertTrackResult(res, true /* green */) 189 require.Equal(t, res.numProofsToCheck, 0) 190 } 191 192 func TestFollowUnfollowTracy(t *testing.T) { 193 tc := SetupTest(t, "id3") 194 defer tc.Cleanup() 195 _, err := kbtest.CreateAndSignupFakeUser("id3", tc.G) 196 require.NoError(t, err) 197 198 mctx := libkb.NewMetaContextForTest(tc) 199 res := runID3(t, mctx, "t_tracy", true /* follow */) 200 require.Equal(t, res.resultType, keybase1.Identify3ResultType_OK) 201 require.Equal(t, len(res.rows), 9) 202 require.Equal(t, len(res.cards), 1) 203 require.Equal(t, res.numProofsToCheck, 4) 204 205 findRows(t, res.rows, []keybase1.Identify3Row{ 206 { 207 Key: "twitter", 208 Value: "tacovontaco", 209 State: keybase1.Identify3RowState_CHECKING, 210 Color: keybase1.Identify3RowColor_GRAY, 211 }, 212 { 213 Key: "twitter", 214 Value: "tacovontaco", 215 State: keybase1.Identify3RowState_VALID, 216 Color: keybase1.Identify3RowColor_BLUE, 217 }, 218 }) 219 findRows(t, res.rows, []keybase1.Identify3Row{ 220 { 221 Key: "https", 222 Value: "keybase.io", 223 State: keybase1.Identify3RowState_CHECKING, 224 Color: keybase1.Identify3RowColor_GRAY, 225 }, 226 { 227 Key: "https", 228 Value: "keybase.io", 229 State: keybase1.Identify3RowState_WARNING, 230 Color: keybase1.Identify3RowColor_ORANGE, 231 Metas: []keybase1.Identify3RowMeta{{Color: keybase1.Identify3RowColor_ORANGE, Label: "unreachable"}}, 232 }, 233 }) 234 235 res = runID3(t, mctx, "t_tracy", false /* follow */) 236 require.Equal(t, res.resultType, keybase1.Identify3ResultType_OK) 237 require.Equal(t, len(res.rows), 9) 238 require.Equal(t, len(res.cards), 1) 239 240 findRows(t, res.rows, []keybase1.Identify3Row{ 241 { 242 Key: "twitter", 243 Value: "tacovontaco", 244 State: keybase1.Identify3RowState_CHECKING, 245 Color: keybase1.Identify3RowColor_GRAY, 246 }, 247 { 248 Key: "twitter", 249 Value: "tacovontaco", 250 State: keybase1.Identify3RowState_VALID, 251 Color: keybase1.Identify3RowColor_GREEN, 252 }, 253 }) 254 findRows(t, res.rows, []keybase1.Identify3Row{ 255 { 256 Key: "https", 257 Value: "keybase.io", 258 State: keybase1.Identify3RowState_CHECKING, 259 Color: keybase1.Identify3RowColor_GRAY, 260 }, 261 { 262 Key: "https", 263 Value: "keybase.io", 264 State: keybase1.Identify3RowState_WARNING, 265 Color: keybase1.Identify3RowColor_GREEN, 266 Metas: []keybase1.Identify3RowMeta{{Color: keybase1.Identify3RowColor_GREEN, Label: "ignored"}}, 267 }, 268 }) 269 } 270 271 func runID3(t *testing.T, mctx libkb.MetaContext, user string, follow bool) id3results { 272 guiid, err := libkb.NewIdentify3GUIID() 273 require.NoError(t, err) 274 resultCh := make(chan keybase1.Identify3ResultType) 275 fakeUI3 := fakeUI3{resultCh: resultCh} 276 err = Identify3(mctx, &fakeUI3, keybase1.Identify3Arg{ 277 Assertion: keybase1.Identify3Assertion(user), 278 GuiID: guiid, 279 }) 280 require.NoError(t, err) 281 <-resultCh 282 err = FollowUser(mctx, keybase1.Identify3FollowUserArg{ 283 GuiID: guiid, 284 Follow: follow, 285 }) 286 require.NoError(t, err) 287 res := fakeUI3.results() 288 for _, row := range res.rows { 289 checkIcon(t, row.Key, row.SiteIcon) 290 checkIcon(t, row.Key, row.SiteIconDarkmode) 291 checkIcon(t, row.Key, row.SiteIconFull) 292 checkIcon(t, row.Key, row.SiteIconFullDarkmode) 293 if row.Priority == 0 || row.Priority == 9999999 { 294 t.Fatalf("unexpected priority %v %v", row.Key, row.Priority) 295 } 296 } 297 return res 298 } 299 300 func TestFollowResetFollow(t *testing.T) { 301 302 tc := SetupTest(t, "id3") 303 defer tc.Cleanup() 304 alice, err := kbtest.CreateAndSignupFakeUser("id3a", tc.G) 305 require.NoError(t, err) 306 bob, err := kbtest.CreateAndSignupFakeUser("id3b", tc.G) 307 require.NoError(t, err) 308 mctx := libkb.NewMetaContextForTest(tc) 309 res := runID3(t, mctx, alice.Username, true) 310 require.False(t, res.userWasReset) 311 312 kbtest.Logout(tc) 313 kbtest.ResetAccount(tc, alice) 314 err = alice.Login(tc.G) 315 require.NoError(t, err) 316 kbtest.Logout(tc) 317 318 err = bob.Login(tc.G) 319 require.NoError(t, err) 320 res = runID3(t, mctx, alice.Username, true) 321 require.True(t, res.userWasReset) 322 res = runID3(t, mctx, alice.Username, true) 323 require.False(t, res.userWasReset) 324 } 325 326 func checkIcon(t testing.TB, service string, icon []keybase1.SizedImage) { 327 if service == "theqrl.org" { 328 // Skip checking for logos for this one. 329 return 330 } 331 require.Len(t, icon, 2, "%v", service) 332 for _, icon := range icon { 333 if icon.Width < 2 { 334 t.Fatalf("unreasonable icon size") 335 } 336 if kbtest.SkipIconRemoteTest() { 337 t.Logf("Skipping icon remote test") 338 require.True(t, len(icon.Path) > 8) 339 } else { 340 resp, err := http.Get(icon.Path) 341 require.NoError(t, err, "%v", service) 342 require.Equal(t, 200, resp.StatusCode, "icon file should be reachable") 343 require.NoError(t, err) 344 body, err := io.ReadAll(resp.Body) 345 require.NoError(t, err) 346 if len(body) < 150 { 347 t.Fatalf("unreasonable icon payload size") 348 } 349 } 350 } 351 }