]> git.proxmox.com Git - cargo.git/blob - tests/testsuite/credential_process.rs
Add RegistryBuilder to help initializing test registries.
[cargo.git] / tests / testsuite / credential_process.rs
1 //! Tests for credential-process.
2
3 use cargo_test_support::{basic_manifest, cargo_process, paths, project, registry, Project};
4 use std::fs;
5 use std::thread;
6
7 fn toml_bin(proj: &Project, name: &str) -> String {
8 proj.bin(name).display().to_string().replace('\\', "\\\\")
9 }
10
11 #[cargo_test]
12 fn gated() {
13 registry::RegistryBuilder::new()
14 .alternative(true)
15 .add_tokens(false)
16 .build();
17
18 let p = project()
19 .file(
20 ".cargo/config",
21 r#"
22 [registry]
23 credential-process = "false"
24 "#,
25 )
26 .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
27 .file("src/lib.rs", "")
28 .build();
29
30 p.cargo("publish --no-verify")
31 .masquerade_as_nightly_cargo()
32 .with_status(101)
33 .with_stderr(
34 "\
35 [UPDATING] [..]
36 [ERROR] no upload token found, please run `cargo login` or pass `--token`
37 ",
38 )
39 .run();
40
41 p.change_file(
42 ".cargo/config",
43 r#"
44 [registry.alternative]
45 credential-process = "false"
46 "#,
47 );
48
49 p.cargo("publish --no-verify --registry alternative")
50 .masquerade_as_nightly_cargo()
51 .with_status(101)
52 .with_stderr(
53 "\
54 [UPDATING] [..]
55 [ERROR] no upload token found, please run `cargo login` or pass `--token`
56 ",
57 )
58 .run();
59 }
60
61 #[cargo_test]
62 fn warn_both_token_and_process() {
63 // Specifying both credential-process and a token in config should issue a warning.
64 registry::RegistryBuilder::new()
65 .alternative(true)
66 .add_tokens(false)
67 .build();
68 let p = project()
69 .file(
70 ".cargo/config",
71 r#"
72 [registries.alternative]
73 token = "sekrit"
74 credential-process = "false"
75 "#,
76 )
77 .file(
78 "Cargo.toml",
79 r#"
80 [package]
81 name = "foo"
82 version = "0.1.0"
83 description = "foo"
84 authors = []
85 license = "MIT"
86 homepage = "https://example.com/"
87 "#,
88 )
89 .file("src/lib.rs", "")
90 .build();
91
92 p.cargo("publish --no-verify --registry alternative -Z credential-process")
93 .masquerade_as_nightly_cargo()
94 .with_status(101)
95 .with_stderr(
96 "\
97 [ERROR] both `registries.alternative.token` and `registries.alternative.credential-process` \
98 were specified in the config\n\
99 Only one of these values may be set, remove one or the other to proceed.
100 ",
101 )
102 .run();
103
104 // Try with global credential-process, and registry-specific `token`.
105 // This should silently use the config token, and not run the "false" exe.
106 p.change_file(
107 ".cargo/config",
108 r#"
109 [registry]
110 credential-process = "false"
111
112 [registries.alternative]
113 token = "sekrit"
114 "#,
115 );
116 p.cargo("publish --no-verify --registry alternative -Z credential-process")
117 .masquerade_as_nightly_cargo()
118 .with_stderr(
119 "\
120 [UPDATING] [..]
121 [PACKAGING] foo v0.1.0 [..]
122 [UPLOADING] foo v0.1.0 [..]
123 ",
124 )
125 .run();
126 }
127
128 /// Setup for a test that will issue a command that needs to fetch a token.
129 ///
130 /// This does the following:
131 ///
132 /// * Spawn a thread that will act as an API server.
133 /// * Create a simple credential-process that will generate a fake token.
134 /// * Create a simple `foo` project to run the test against.
135 /// * Configure the credential-process config.
136 ///
137 /// Returns a thread handle for the API server, the test should join it when
138 /// finished. Also returns the simple `foo` project to test against.
139 fn get_token_test() -> (Project, thread::JoinHandle<()>) {
140 // API server that checks that the token is included correctly.
141 let server = registry::RegistryBuilder::new()
142 .add_tokens(false)
143 .build_api_server(&|headers| {
144 assert!(headers
145 .iter()
146 .any(|header| header == "Authorization: sekrit"));
147
148 (200, &r#"{"ok": true, "msg": "completed!"}"#)
149 });
150
151 // The credential process to use.
152 let cred_proj = project()
153 .at("cred_proj")
154 .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
155 .file("src/main.rs", r#"fn main() { println!("sekrit"); } "#)
156 .build();
157 cred_proj.cargo("build").run();
158
159 let p = project()
160 .file(
161 ".cargo/config",
162 &format!(
163 r#"
164 [registries.alternative]
165 index = "{}"
166 credential-process = ["{}"]
167 "#,
168 registry::alt_registry_url(),
169 toml_bin(&cred_proj, "test-cred")
170 ),
171 )
172 .file(
173 "Cargo.toml",
174 r#"
175 [package]
176 name = "foo"
177 version = "0.1.0"
178 description = "foo"
179 authors = []
180 license = "MIT"
181 homepage = "https://example.com/"
182 "#,
183 )
184 .file("src/lib.rs", "")
185 .build();
186 (p, server)
187 }
188
189 #[cargo_test]
190 fn publish() {
191 // Checks that credential-process is used for `cargo publish`.
192 let (p, t) = get_token_test();
193
194 p.cargo("publish --no-verify --registry alternative -Z credential-process")
195 .masquerade_as_nightly_cargo()
196 .with_stderr(
197 "\
198 [UPDATING] [..]
199 [PACKAGING] foo v0.1.0 [..]
200 [UPLOADING] foo v0.1.0 [..]
201 ",
202 )
203 .run();
204
205 t.join().ok().unwrap();
206 }
207
208 #[cargo_test]
209 fn basic_unsupported() {
210 // Non-action commands don't support login/logout.
211 registry::RegistryBuilder::new().add_tokens(false).build();
212 cargo::util::paths::append(
213 &paths::home().join(".cargo/config"),
214 br#"
215 [registry]
216 credential-process = "false"
217 "#,
218 )
219 .unwrap();
220
221 cargo_process("login -Z credential-process abcdefg")
222 .masquerade_as_nightly_cargo()
223 .with_status(101)
224 .with_stderr(
225 "\
226 [UPDATING] [..]
227 [ERROR] credential process `false` cannot be used to log in, \
228 the credential-process configuration value must pass the \
229 `{action}` argument in the config to support this command
230 ",
231 )
232 .run();
233
234 cargo_process("logout -Z credential-process")
235 .masquerade_as_nightly_cargo()
236 .with_status(101)
237 .with_stderr(
238 "\
239 [ERROR] credential process `false` cannot be used to log out, \
240 the credential-process configuration value must pass the \
241 `{action}` argument in the config to support this command
242 ",
243 )
244 .run();
245 }
246
247 #[cargo_test]
248 fn login() {
249 registry::init();
250 // The credential process to use.
251 let cred_proj = project()
252 .at("cred_proj")
253 .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
254 .file(
255 "src/main.rs",
256 &r#"
257 use std::io::Read;
258
259 fn main() {
260 assert_eq!(std::env::var("CARGO_REGISTRY_NAME").unwrap(), "crates-io");
261 assert_eq!(std::env::var("CARGO_REGISTRY_API_URL").unwrap(), "__API__");
262 assert_eq!(std::env::args().skip(1).next().unwrap(), "store");
263 let mut buffer = String::new();
264 std::io::stdin().read_to_string(&mut buffer).unwrap();
265 assert_eq!(buffer, "abcdefg\n");
266 std::fs::write("token-store", buffer).unwrap();
267 }
268 "#
269 .replace("__API__", &registry::api_url().to_string()),
270 )
271 .build();
272 cred_proj.cargo("build").run();
273
274 cargo::util::paths::append(
275 &paths::home().join(".cargo/config"),
276 format!(
277 r#"
278 [registry]
279 credential-process = ["{}", "{{action}}"]
280 "#,
281 toml_bin(&cred_proj, "test-cred")
282 )
283 .as_bytes(),
284 )
285 .unwrap();
286
287 cargo_process("login -Z credential-process abcdefg")
288 .masquerade_as_nightly_cargo()
289 .with_stderr(
290 "\
291 [UPDATING] [..]
292 [LOGIN] token for `crates.io` saved
293 ",
294 )
295 .run();
296 assert_eq!(
297 fs::read_to_string(paths::root().join("token-store")).unwrap(),
298 "abcdefg\n"
299 );
300 }
301
302 #[cargo_test]
303 fn logout() {
304 registry::RegistryBuilder::new().add_tokens(false).build();
305 // The credential process to use.
306 let cred_proj = project()
307 .at("cred_proj")
308 .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
309 .file(
310 "src/main.rs",
311 r#"
312 use std::io::Read;
313
314 fn main() {
315 assert_eq!(std::env::var("CARGO_REGISTRY_NAME").unwrap(), "crates-io");
316 assert_eq!(std::env::args().skip(1).next().unwrap(), "erase");
317 std::fs::write("token-store", "").unwrap();
318 eprintln!("token for `{}` has been erased!",
319 std::env::var("CARGO_REGISTRY_NAME").unwrap());
320 }
321 "#,
322 )
323 .build();
324 cred_proj.cargo("build").run();
325
326 cargo::util::paths::append(
327 &paths::home().join(".cargo/config"),
328 format!(
329 r#"
330 [registry]
331 credential-process = ["{}", "{{action}}"]
332 "#,
333 toml_bin(&cred_proj, "test-cred")
334 )
335 .as_bytes(),
336 )
337 .unwrap();
338
339 cargo_process("logout -Z credential-process")
340 .masquerade_as_nightly_cargo()
341 .with_stderr(
342 "\
343 [UPDATING] [..]
344 token for `crates-io` has been erased!
345 [LOGOUT] token for `crates.io` has been removed from local storage
346 ",
347 )
348 .run();
349 assert_eq!(
350 fs::read_to_string(paths::root().join("token-store")).unwrap(),
351 ""
352 );
353 }
354
355 #[cargo_test]
356 fn yank() {
357 let (p, t) = get_token_test();
358
359 p.cargo("yank --vers 0.1.0 --registry alternative -Z credential-process")
360 .masquerade_as_nightly_cargo()
361 .with_stderr(
362 "\
363 [UPDATING] [..]
364 [YANK] foo:0.1.0
365 ",
366 )
367 .run();
368
369 t.join().ok().unwrap();
370 }
371
372 #[cargo_test]
373 fn owner() {
374 let (p, t) = get_token_test();
375
376 p.cargo("owner --add username --registry alternative -Z credential-process")
377 .masquerade_as_nightly_cargo()
378 .with_stderr(
379 "\
380 [UPDATING] [..]
381 [OWNER] completed!
382 ",
383 )
384 .run();
385
386 t.join().ok().unwrap();
387 }
388
389 #[cargo_test]
390 fn libexec_path() {
391 // cargo: prefixed names use the sysroot
392 registry::RegistryBuilder::new().add_tokens(false).build();
393 cargo::util::paths::append(
394 &paths::home().join(".cargo/config"),
395 br#"
396 [registry]
397 credential-process = "cargo:doesnotexist"
398 "#,
399 )
400 .unwrap();
401
402 cargo_process("login -Z credential-process abcdefg")
403 .masquerade_as_nightly_cargo()
404 .with_status(101)
405 .with_stderr(
406 &format!("\
407 [UPDATING] [..]
408 [ERROR] failed to execute `[..]libexec/cargo-credential-doesnotexist[EXE]` to store authentication token for registry `crates-io`
409
410 Caused by:
411 {}
412 ", cargo_test_support::no_such_file_err_msg()),
413 )
414 .run();
415 }
416
417 #[cargo_test]
418 fn invalid_token_output() {
419 // Error when credential process does not output the expected format for a token.
420 registry::RegistryBuilder::new()
421 .alternative(true)
422 .add_tokens(false)
423 .build();
424 let cred_proj = project()
425 .at("cred_proj")
426 .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
427 .file("src/main.rs", r#"fn main() { print!("a\nb\n"); } "#)
428 .build();
429 cred_proj.cargo("build").run();
430
431 cargo::util::paths::append(
432 &paths::home().join(".cargo/config"),
433 format!(
434 r#"
435 [registry]
436 credential-process = ["{}"]
437 "#,
438 toml_bin(&cred_proj, "test-cred")
439 )
440 .as_bytes(),
441 )
442 .unwrap();
443
444 let p = project()
445 .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
446 .file("src/lib.rs", "")
447 .build();
448
449 p.cargo("publish --no-verify --registry alternative -Z credential-process")
450 .masquerade_as_nightly_cargo()
451 .with_status(101)
452 .with_stderr(
453 "\
454 [UPDATING] [..]
455 [ERROR] credential process `[..]test-cred[EXE]` returned more than one line of output; expected a single token
456 ",
457 )
458 .run();
459 }