#
cargo/util/credential/
adaptor.rs

    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
    64
    65
    66
    67
    68

    //! Credential provider that launches an external process that only outputs a credential

    use std::{
        io::Read,
        process::{Command, Stdio},
    };

    use anyhow::Context;
    use cargo_credential::{
        Action, CacheControl, Credential, CredentialResponse, RegistryInfo, Secret,
    };

    pub struct BasicProcessCredential {}

    impl Credential for BasicProcessCredential {
        fn perform(
            &self,
            registry: &RegistryInfo<'_>,
            action: &Action<'_>,
            args: &[&str],
        ) -> Result<CredentialResponse, cargo_credential::Error> {
            match action {
                Action::Get(_) => {
                    let mut args = args.iter();
                    let exe = args.next()
                        .ok_or("The first argument to `cargo:token-from-stdout` must be a command that prints a token on stdout")?;
                    let args = args.map(|arg| arg.replace("{index_url}", registry.index_url));

                    let mut cmd = Command::new(exe);
                    cmd.args(args)
                        .env("CARGO_REGISTRY_INDEX_URL", registry.index_url);
                    if let Some(name) = registry.name {
                        cmd.env("CARGO_REGISTRY_NAME_OPT", name);
                    }
                    cmd.stdout(Stdio::piped());
                    let mut child = cmd.spawn().context("failed to spawn credential process")?;
                    let mut buffer = String::new();
                    child
                        .stdout
                        .take()
                        .unwrap()
                        .read_to_string(&mut buffer)
                        .context("failed to read from credential provider")?;
                    if let Some(end) = buffer.find('\n') {
                        if buffer.len() > end + 1 {
                            return Err(format!(
                                "process `{}` returned more than one line of output; \
                                expected a single token",
                                exe
                            )
                            .into());
                        }
                        buffer.truncate(end);
                    }
                    let status = child.wait().context("credential process never started")?;
                    if !status.success() {
                        return Err(format!("process `{}` failed with status `{status}`", exe).into());
                    }
                    Ok(CredentialResponse::Get {
                        token: Secret::from(buffer),
                        cache: CacheControl::Session,
                        operation_independent: true,
                    })
                }
                _ => Err(cargo_credential::Error::OperationNotSupported),
            }
        }
    }