diff options
Diffstat (limited to 'src/tools/cargo/credential/cargo-credential/examples/file-provider.rs')
-rw-r--r-- | src/tools/cargo/credential/cargo-credential/examples/file-provider.rs | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs new file mode 100644 index 000000000..d11958536 --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs @@ -0,0 +1,90 @@ +//! Example credential provider that stores credentials in a JSON file. +//! This is not secure + +use cargo_credential::{ + Action, CacheControl, Credential, CredentialResponse, RegistryInfo, Secret, +}; +use std::{collections::HashMap, fs::File, io::ErrorKind}; +type Error = Box<dyn std::error::Error + Send + Sync + 'static>; + +struct FileCredential; + +impl Credential for FileCredential { + fn perform( + &self, + registry: &RegistryInfo, + action: &Action, + _args: &[&str], + ) -> Result<CredentialResponse, cargo_credential::Error> { + if registry.index_url != "https://github.com/rust-lang/crates.io-index" { + // Restrict this provider to only work for crates.io. Cargo will skip it and attempt + // another provider for any other registry. + // + // If a provider supports any registry, then this check should be omitted. + return Err(cargo_credential::Error::UrlNotSupported); + } + + // `Error::Other` takes a boxed `std::error::Error` type that causes Cargo to show the error. + let mut creds = FileCredential::read().map_err(cargo_credential::Error::Other)?; + + match action { + Action::Get(_) => { + // Cargo requested a token, look it up. + if let Some(token) = creds.get(registry.index_url) { + Ok(CredentialResponse::Get { + token: token.clone(), + cache: CacheControl::Session, + operation_independent: true, + }) + } else { + // Credential providers should respond with `NotFound` when a credential can not be + // found, allowing Cargo to attempt another provider. + Err(cargo_credential::Error::NotFound) + } + } + Action::Login(login_options) => { + // The token for `cargo login` can come from the `login_options` parameter or i + // interactively reading from stdin. + // + // `cargo_credential::read_token` automatically handles this. + let token = cargo_credential::read_token(login_options, registry)?; + creds.insert(registry.index_url.to_string(), token); + + FileCredential::write(&creds).map_err(cargo_credential::Error::Other)?; + + // Credentials were successfully stored. + Ok(CredentialResponse::Login) + } + Action::Logout => { + if creds.remove(registry.index_url).is_none() { + // If the user attempts to log out from a registry that has no credentials + // stored, then NotFound is the appropriate error. + Err(cargo_credential::Error::NotFound) + } else { + // Credentials were successfully erased. + Ok(CredentialResponse::Logout) + } + } + // If a credential provider doesn't support a given operation, it should respond with `OperationNotSupported`. + _ => Err(cargo_credential::Error::OperationNotSupported), + } + } +} + +impl FileCredential { + fn read() -> Result<HashMap<String, Secret<String>>, Error> { + match File::open("cargo-credentials.json") { + Ok(f) => Ok(serde_json::from_reader(f)?), + Err(e) if e.kind() == ErrorKind::NotFound => Ok(HashMap::new()), + Err(e) => Err(e)?, + } + } + fn write(value: &HashMap<String, Secret<String>>) -> Result<(), Error> { + let file = File::create("cargo-credentials.json")?; + Ok(serde_json::to_writer_pretty(file, value)?) + } +} + +fn main() { + cargo_credential::main(FileCredential); +} |