1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//! Interacts with the registry [login API][1].
//!
//! This doesn't really call any web API at this moment. Instead, it's just an
//! operation for `cargo login`.
//!
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#login

use std::io::IsTerminal;

use crate::util::auth;
use crate::util::auth::AuthorizationError;
use crate::CargoResult;
use crate::Config;
use cargo_credential::LoginOptions;
use cargo_credential::Secret;

use super::get_source_id;
use super::registry;
use super::RegistryOrIndex;

pub fn registry_login(
    config: &Config,
    token_from_cmdline: Option<Secret<&str>>,
    reg_or_index: Option<&RegistryOrIndex>,
    args: &[&str],
) -> CargoResult<()> {
    let source_ids = get_source_id(config, reg_or_index)?;

    let login_url = match registry(
        config,
        token_from_cmdline.clone(),
        reg_or_index,
        false,
        None,
    ) {
        Ok((registry, _)) => Some(format!("{}/me", registry.host())),
        Err(e) if e.is::<AuthorizationError>() => e
            .downcast::<AuthorizationError>()
            .unwrap()
            .login_url
            .map(|u| u.to_string()),
        Err(e) => return Err(e),
    };

    let mut token_from_stdin = None;
    let token = token_from_cmdline.or_else(|| {
        if !std::io::stdin().is_terminal() {
            let token = std::io::read_to_string(std::io::stdin()).unwrap_or_default();
            if !token.is_empty() {
                token_from_stdin = Some(token);
            }
        }
        token_from_stdin.as_deref().map(Secret::from)
    });

    let options = LoginOptions {
        token,
        login_url: login_url.as_deref(),
    };

    auth::login(config, &source_ids.original, options, args)?;
    Ok(())
}