-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #12461 - arlosi:cred-doc, r=epage
docs: add example for cargo-credential Add additional docs and example for `cargo-credential`. r? `@epage`
- Loading branch information
Showing
5 changed files
with
164 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::path::Path; | ||
|
||
use snapbox::cmd::Command; | ||
|
||
#[test] | ||
fn file_provider() { | ||
let bin = snapbox::cmd::compile_example("file-provider", []).unwrap(); | ||
|
||
let hello = r#"{"v":[1]}"#; | ||
let login_request = r#"{"v": 1,"registry": {"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind": "login","token": "s3krit","args": []}"#; | ||
let login_response = r#"{"Ok":{"kind":"login"}}"#; | ||
|
||
let get_request = r#"{"v": 1,"registry": {"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind": "get","operation": "read","args": []}"#; | ||
let get_response = | ||
r#"{"Ok":{"kind":"get","token":"s3krit","cache":"session","operation_independent":true}}"#; | ||
|
||
let dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("cargo-credential-tests"); | ||
std::fs::create_dir(&dir).unwrap(); | ||
Command::new(bin) | ||
.current_dir(&dir) | ||
.stdin(format!("{login_request}\n{get_request}\n")) | ||
.arg("--cargo-plugin") | ||
.assert() | ||
.stdout_eq(format!("{hello}\n{login_response}\n{get_response}\n")) | ||
.stderr_eq("") | ||
.success(); | ||
std::fs::remove_dir_all(&dir).unwrap(); | ||
} |