-
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.
Add more docs and example for cargo-credential
- Loading branch information
Showing
5 changed files
with
158 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,87 @@ | ||
//! 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}; | ||
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> { | ||
let mut creds = FileCredential::read().unwrap_or_default(); | ||
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); | ||
} | ||
|
||
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 could 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); | ||
|
||
// `Error::Other` takes a boxed `std::error::Error` type that causes Cargo to | ||
// show the error. | ||
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> { | ||
let file = File::open("cargo-credentials.json")?; | ||
Ok(serde_json::from_reader(file)?) | ||
} | ||
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,23 @@ | ||
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}}"#; | ||
|
||
Command::new(bin) | ||
.current_dir(env!("CARGO_TARGET_TMPDIR")) | ||
.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(); | ||
} |