1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"testing"
8
9	"github.com/keybase/client/go/libkb"
10	"github.com/stretchr/testify/require"
11	"golang.org/x/net/context"
12)
13
14const aliceFp string = "2373fd089f28f328916b88f99c7927c0bdfdadf9"
15const bobFp string = "91fe9b24ef6706b1f7898f2059a2a43f8b731f29"
16
17func createUserWhoTracks(tc libkb.TestContext, trackedUsers []string, sigVersion libkb.SigVersion) *FakeUser {
18	fu := CreateAndSignupFakeUser(tc, "pull")
19	fu.LoginOrBust(tc)
20
21	for _, trackedUser := range trackedUsers {
22		_, _, err := runTrack(tc, fu, trackedUser, sigVersion)
23		require.NoError(tc.T, err, "error while tracking")
24	}
25	return fu
26}
27
28func untrackUserList(tc libkb.TestContext, fu *FakeUser, trackedUsers []string, sigVersion libkb.SigVersion) {
29	for _, trackedUser := range trackedUsers {
30		err := runUntrack(tc, fu, trackedUser, sigVersion)
31		require.NoError(tc.T, err, "error while untracking %s", trackedUser)
32	}
33}
34
35func createGpgClient(tc libkb.TestContext) *libkb.GpgCLI {
36	gpgClient := libkb.NewGpgCLI(tc.G, tc.G.UI.GetLogUI())
37	err := gpgClient.Configure(tc.MetaContext())
38	require.NoError(tc.T, err, "Error while configuring gpg client.")
39	return gpgClient
40}
41
42func assertKeysPresent(t *testing.T, gpgClient *libkb.GpgCLI, fingerprints []string) {
43	for _, fingerprint := range fingerprints {
44		fpObj, err := gpgClient.ImportKey(gpgClient.MetaContext(context.Background()), false /*secret*/, *libkb.PGPFingerprintFromHexNoError(fingerprint), "")
45		require.NoError(t, err, "Should have fingerprint in keyring: %s", fingerprint)
46		require.Equal(t, fingerprint, fpObj.GetFingerprint().String())
47	}
48}
49
50func assertKeysMissing(t *testing.T, gpgClient *libkb.GpgCLI, fingerprints []string) {
51	for _, fingerprint := range fingerprints {
52		_, err := gpgClient.ImportKey(gpgClient.MetaContext(context.Background()), false /*secret*/, *libkb.PGPFingerprintFromHexNoError(fingerprint), "")
53		require.Error(t, err, "Should not already have fingerprint in keyring: %s", fingerprint)
54	}
55}
56
57func runPGPPull(tc libkb.TestContext, arg PGPPullEngineArg) {
58	eng := NewPGPPullEngine(tc.G, &arg)
59	m := NewMetaContextForTestWithLogUI(tc)
60	err := RunEngine2(m, eng)
61	require.NoError(tc.T, err, "Error in PGPPullEngine")
62}
63
64func runPGPPullExpectingError(tc libkb.TestContext, arg PGPPullEngineArg) {
65	eng := NewPGPPullEngine(tc.G, &arg)
66	m := NewMetaContextForTestWithLogUI(tc)
67	err := RunEngine2(m, eng)
68	require.Error(tc.T, err, "PGPPullEngine should have failed.")
69}
70
71func TestPGPPullAll(t *testing.T) {
72	tc := SetupEngineTest(t, "pgp_pull")
73	defer tc.Cleanup()
74	sigVersion := libkb.GetDefaultSigVersion(tc.G)
75
76	users := []string{"t_alice", "t_bob"}
77	fu := createUserWhoTracks(tc, users, sigVersion)
78	defer untrackUserList(tc, fu, users, sigVersion)
79	gpgClient := createGpgClient(tc)
80
81	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
82
83	runPGPPull(tc, PGPPullEngineArg{})
84
85	assertKeysPresent(t, gpgClient, []string{aliceFp, bobFp})
86}
87
88func TestPGPPullOne(t *testing.T) {
89	tc := SetupEngineTest(t, "pgp_pull")
90	defer tc.Cleanup()
91	sigVersion := libkb.GetDefaultSigVersion(tc.G)
92
93	users := []string{"t_alice", "t_bob"}
94	fu := createUserWhoTracks(tc, users, sigVersion)
95	defer untrackUserList(tc, fu, users, sigVersion)
96	gpgClient := createGpgClient(tc)
97
98	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
99
100	runPGPPull(tc, PGPPullEngineArg{
101		// ID'ing the same user twice should be ok.
102		UserAsserts: []string{"t_bob"},
103	})
104
105	assertKeysPresent(t, gpgClient, []string{bobFp})
106	assertKeysMissing(t, gpgClient, []string{aliceFp})
107}
108
109func TestPGPPullBadIDs(t *testing.T) {
110	tc := SetupEngineTest(t, "pgp_pull")
111	defer tc.Cleanup()
112	sigVersion := libkb.GetDefaultSigVersion(tc.G)
113
114	users := []string{"t_alice", "t_bob"}
115	fu := createUserWhoTracks(tc, users, sigVersion)
116	defer untrackUserList(tc, fu, users, sigVersion)
117	gpgClient := createGpgClient(tc)
118
119	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
120
121	runPGPPullExpectingError(tc, PGPPullEngineArg{
122		// ID'ing invalid user should fail the pull.
123		UserAsserts: []string{"t_bob", "t_NOT_TRACKED_BY_ME"},
124	})
125
126	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
127}
128
129func TestPGPPullNotTracked(t *testing.T) {
130	tc := SetupEngineTest(t, "pgp_pull")
131	defer tc.Cleanup()
132	sigVersion := libkb.GetDefaultSigVersion(tc.G)
133
134	// Only tracking alice
135	users := []string{"t_alice"}
136	fu := createUserWhoTracks(tc, users, sigVersion)
137	defer untrackUserList(tc, fu, users, sigVersion)
138	gpgClient := createGpgClient(tc)
139
140	// But want to pull bot alice and bob.
141	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
142
143	fui := &FakeIdentifyUI{FakeConfirm: true}
144	uis := libkb.UIs{
145		LogUI:      tc.G.UI.GetLogUI(),
146		GPGUI:      &gpgtestui{},
147		IdentifyUI: fui,
148	}
149	eng := NewPGPPullEngine(tc.G, &PGPPullEngineArg{
150		UserAsserts: []string{"t_bob", "t_alice"},
151	})
152	m := NewMetaContextForTest(tc).WithUIs(uis)
153	err := RunEngine2(m, eng)
154	require.NoError(t, err)
155	require.Equal(t, 1, fui.StartCount, "Expected 1 ID UI prompt")
156
157	assertKeysPresent(t, gpgClient, []string{bobFp, aliceFp})
158}
159
160func TestPGPPullNotLoggedIn(t *testing.T) {
161	tc := SetupEngineTest(t, "track")
162	defer tc.Cleanup()
163
164	gpgClient := createGpgClient(tc)
165
166	assertKeysMissing(t, gpgClient, []string{aliceFp, bobFp})
167
168	fui := &FakeIdentifyUI{FakeConfirm: true}
169	uis := libkb.UIs{
170		LogUI:      tc.G.UI.GetLogUI(),
171		GPGUI:      &gpgtestui{},
172		IdentifyUI: fui,
173	}
174	eng := NewPGPPullEngine(tc.G, &PGPPullEngineArg{
175		UserAsserts: []string{"t_bob", "t_alice"},
176	})
177	m := NewMetaContextForTest(tc).WithUIs(uis)
178	err := RunEngine2(m, eng)
179	require.NoError(t, err)
180	require.Equal(t, 2, fui.StartCount, "Expected 2 ID UI prompt")
181
182	assertKeysPresent(t, gpgClient, []string{aliceFp, bobFp})
183}
184
185func TestPGPPullMultiplePrompts(t *testing.T) {
186	tc := SetupEngineTest(t, "pgp_pull")
187	defer tc.Cleanup()
188	sigVersion := libkb.GetDefaultSigVersion(tc.G)
189	createUserWhoTracks(tc, []string{}, sigVersion)
190
191	gpgClient := createGpgClient(tc)
192	assertKeysMissing(t, gpgClient, []string{aliceFp})
193
194	// Try the first time, declining in prompt. We expect keys not to
195	// be imported.
196	fui := &FakeIdentifyUI{FakeConfirm: false}
197	uis := libkb.UIs{
198		LogUI:      tc.G.UI.GetLogUI(),
199		GPGUI:      &gpgtestui{},
200		IdentifyUI: fui,
201	}
202
203	eng := NewPGPPullEngine(tc.G, &PGPPullEngineArg{
204		UserAsserts: []string{"t_alice"},
205	})
206	m := NewMetaContextForTest(tc).WithUIs(uis)
207	err := RunEngine2(m, eng)
208	require.NoError(t, err)
209
210	require.Equal(t, 1, fui.StartCount, "Expected 1 ID UI prompt")
211	assertKeysMissing(t, gpgClient, []string{aliceFp})
212
213	// Run again, declining like before, but make sure we got asked
214	// second time and our answer wasn't just cached.
215	err = RunEngine2(m, eng)
216	require.NoError(t, err)
217
218	require.Equal(t, 2, fui.StartCount, "Expected 2 ID UI prompts")
219	assertKeysMissing(t, gpgClient, []string{aliceFp})
220
221	// Run again, attempt to confirm in prompt. PGP Pull should ask us
222	// again even though we declined before, and successfully import
223	// the keys.
224	fui.FakeConfirm = true
225	err = RunEngine2(m, eng)
226	require.NoError(t, err)
227
228	require.Equal(t, 3, fui.StartCount, "Expected 2 ID UI prompts")
229	assertKeysPresent(t, gpgClient, []string{aliceFp})
230}
231