From c23a457e72abe608715ac76f076f47dc42af07a5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 30 May 2024 20:31:44 +0200 Subject: Merging upstream version 1.74.1+dfsg1. Signed-off-by: Daniel Baumann --- .../cargo-credential-1password/Cargo.toml | 2 +- .../cargo-credential-1password/README.md | 17 +- .../cargo-credential-1password/src/main.rs | 4 +- .../cargo-credential-libsecret/Cargo.toml | 2 +- .../cargo-credential-libsecret/README.md | 6 +- .../cargo-credential-libsecret/src/lib.rs | 10 +- .../cargo-credential-macos-keychain/Cargo.toml | 2 +- .../cargo-credential-macos-keychain/README.md | 7 +- .../credential/cargo-credential-wincred/Cargo.toml | 2 +- .../credential/cargo-credential-wincred/README.md | 6 +- .../credential/cargo-credential-wincred/src/lib.rs | 4 +- .../cargo/credential/cargo-credential/Cargo.toml | 3 +- .../cargo/credential/cargo-credential/README.md | 4 +- .../cargo-credential/examples/file-provider.rs | 4 +- .../cargo-credential/examples/stdout-redirected.rs | 4 +- .../cargo/credential/cargo-credential/src/error.rs | 4 +- .../cargo/credential/cargo-credential/src/lib.rs | 204 ++++++++++++++++++--- 17 files changed, 230 insertions(+), 55 deletions(-) (limited to 'src/tools/cargo/credential') diff --git a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml index a607e6da1..d7bd949d1 100644 --- a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml +++ b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-credential-1password" -version = "0.3.0" +version = "0.4.0" edition.workspace = true license.workspace = true repository = "https://github.com/rust-lang/cargo" diff --git a/src/tools/cargo/credential/cargo-credential-1password/README.md b/src/tools/cargo/credential/cargo-credential-1password/README.md index 7cc15e05b..3648efe4b 100644 --- a/src/tools/cargo/credential/cargo-credential-1password/README.md +++ b/src/tools/cargo/credential/cargo-credential-1password/README.md @@ -1,7 +1,18 @@ # cargo-credential-1password -This is the implementation for the Cargo credential helper for [1password]. -See the [credential-process] documentation for how to use this. +A Cargo [credential provider] for [1password]. + +`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must +install the `op` CLI from the [1password +website](https://1password.com/downloads/command-line/). You must run `op signin` +at least once with the appropriate arguments (such as `op signin my.1password.com user@example.com`), +unless you provide the sign-in-address and email arguments. The master password will be required on each request +unless the appropriate `OP_SESSION` environment variable is set. It supports +the following command-line arguments: +* `--account`: The account shorthand name to use. +* `--vault`: The vault name to use. +* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`. +* `--email`: The email address to sign in with. [1password]: https://1password.com/ -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs index a2607fd2f..921b52145 100644 --- a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs +++ b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs @@ -255,8 +255,8 @@ pub struct OnePasswordCredential {} impl Credential for OnePasswordCredential { fn perform( &self, - registry: &RegistryInfo, - action: &Action, + registry: &RegistryInfo<'_>, + action: &Action<'_>, args: &[&str], ) -> Result { let op = OnePasswordKeychain::new(args)?; diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml index 1bd4bb7d0..5bedad3b9 100644 --- a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml +++ b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-credential-libsecret" -version = "0.3.1" +version = "0.3.2" edition.workspace = true license.workspace = true repository = "https://github.com/rust-lang/cargo" diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/README.md b/src/tools/cargo/credential/cargo-credential-libsecret/README.md index f169323e0..aaba2887f 100644 --- a/src/tools/cargo/credential/cargo-credential-libsecret/README.md +++ b/src/tools/cargo/credential/cargo-credential-libsecret/README.md @@ -1,7 +1,9 @@ # cargo-credential-libsecret This is the implementation for the Cargo credential helper for [GNOME libsecret]. -See the [credential-process] documentation for how to use this. +See the [credential-provider] documentation for how to use this. + +This credential provider is built-in to cargo as `cargo:libsecret`. [GNOME libsecret]: https://wiki.gnome.org/Projects/Libsecret -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs b/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs index f83b424ee..ee1797605 100644 --- a/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs +++ b/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs @@ -104,16 +104,16 @@ mod linux { impl Credential for LibSecretCredential { fn perform( &self, - registry: &RegistryInfo, - action: &Action, + registry: &RegistryInfo<'_>, + action: &Action<'_>, _args: &[&str], ) -> Result { // Dynamically load libsecret to avoid users needing to install // additional -dev packages when building this provider. let lib; - let secret_password_lookup_sync: Symbol; - let secret_password_store_sync: Symbol; - let secret_password_clear_sync: Symbol; + let secret_password_lookup_sync: Symbol<'_, SecretPasswordLookupSync>; + let secret_password_store_sync: Symbol<'_, SecretPasswordStoreSync>; + let secret_password_clear_sync: Symbol<'_, SecretPasswordClearSync>; unsafe { lib = Library::new("libsecret-1.so").context( "failed to load libsecret: try installing the `libsecret` \ diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml index 342c771b5..172e9c10b 100644 --- a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml +++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-credential-macos-keychain" -version = "0.3.0" +version = "0.3.1" edition.workspace = true license.workspace = true repository = "https://github.com/rust-lang/cargo" diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md index 554116b55..f5efe496b 100644 --- a/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md +++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md @@ -1,7 +1,10 @@ # cargo-credential-macos-keychain This is the implementation for the Cargo credential helper for [macOS Keychain]. -See the [credential-process] documentation for how to use this. +See the [credential-provider] documentation for how to use this. + +This credential provider is built-in to cargo as `cargo:macos-keychain`. [macOS Keychain]: https://support.apple.com/guide/keychain-access/welcome/mac -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html + diff --git a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml index 8c609dc4e..6da6578a5 100644 --- a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml +++ b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-credential-wincred" -version = "0.3.0" +version = "0.3.1" edition.workspace = true license.workspace = true repository = "https://github.com/rust-lang/cargo" diff --git a/src/tools/cargo/credential/cargo-credential-wincred/README.md b/src/tools/cargo/credential/cargo-credential-wincred/README.md index 8c8d18789..1995e9d76 100644 --- a/src/tools/cargo/credential/cargo-credential-wincred/README.md +++ b/src/tools/cargo/credential/cargo-credential-wincred/README.md @@ -1,7 +1,9 @@ # cargo-credential-wincred This is the implementation for the Cargo credential helper for [Windows Credential Manager]. -See the [credential-process] documentation for how to use this. +See the [credential-provider] documentation for how to use this. + +This credential provider is built-in to cargo as `cargo:wincred`. [Windows Credential Manager]: https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0 -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs b/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs index 9200ca58f..24b072ee2 100644 --- a/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs +++ b/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs @@ -38,8 +38,8 @@ mod win { impl Credential for WindowsCredential { fn perform( &self, - registry: &RegistryInfo, - action: &Action, + registry: &RegistryInfo<'_>, + action: &Action<'_>, _args: &[&str], ) -> Result { match action { diff --git a/src/tools/cargo/credential/cargo-credential/Cargo.toml b/src/tools/cargo/credential/cargo-credential/Cargo.toml index 8cd1348be..c8db996bf 100644 --- a/src/tools/cargo/credential/cargo-credential/Cargo.toml +++ b/src/tools/cargo/credential/cargo-credential/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "cargo-credential" -version = "0.3.0" +version = "0.4.0" edition.workspace = true license.workspace = true +rust-version = "1.70.0" repository = "https://github.com/rust-lang/cargo" description = "A library to assist writing Cargo credential helpers." diff --git a/src/tools/cargo/credential/cargo-credential/README.md b/src/tools/cargo/credential/cargo-credential/README.md index 049b3ba55..d87d41bb8 100644 --- a/src/tools/cargo/credential/cargo-credential/README.md +++ b/src/tools/cargo/credential/cargo-credential/README.md @@ -5,7 +5,7 @@ provides an interface to store tokens for authorizing access to a registry such as https://crates.io/. Documentation about credential processes may be found at -https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +https://doc.rust-lang.org/nightly/cargo/reference/credential-provider-protocol.html Example implementations may be found at https://github.com/rust-lang/cargo/tree/master/credential @@ -18,7 +18,7 @@ Create a Cargo project with this as a dependency: # Add this to your Cargo.toml: [dependencies] -cargo-credential = "0.3" +cargo-credential = "0.4" ``` And then include a `main.rs` binary which implements the `Credential` trait, and calls diff --git a/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs index d11958536..3ed312cb8 100644 --- a/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs +++ b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs @@ -12,8 +12,8 @@ struct FileCredential; impl Credential for FileCredential { fn perform( &self, - registry: &RegistryInfo, - action: &Action, + registry: &RegistryInfo<'_>, + action: &Action<'_>, _args: &[&str], ) -> Result { if registry.index_url != "https://github.com/rust-lang/crates.io-index" { diff --git a/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs index 0b9bcc2f7..75a2d16d1 100644 --- a/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs +++ b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs @@ -7,8 +7,8 @@ struct MyCredential; impl Credential for MyCredential { fn perform( &self, - _registry: &RegistryInfo, - _action: &Action, + _registry: &RegistryInfo<'_>, + _action: &Action<'_>, _args: &[&str], ) -> Result { // Informational messages should be sent on stderr. diff --git a/src/tools/cargo/credential/cargo-credential/src/error.rs b/src/tools/cargo/credential/cargo-credential/src/error.rs index 2ebaf9977..8c5fe19e5 100644 --- a/src/tools/cargo/credential/cargo-credential/src/error.rs +++ b/src/tools/cargo/credential/cargo-credential/src/error.rs @@ -42,9 +42,9 @@ pub enum Error { } impl From for Error { - fn from(err: String) -> Self { + fn from(message: String) -> Self { Box::new(StringTypedError { - message: err.to_string(), + message, source: None, }) .into() diff --git a/src/tools/cargo/credential/cargo-credential/src/lib.rs b/src/tools/cargo/credential/cargo-credential/src/lib.rs index 0fb495ed3..60bce65be 100644 --- a/src/tools/cargo/credential/cargo-credential/src/lib.rs +++ b/src/tools/cargo/credential/cargo-credential/src/lib.rs @@ -50,7 +50,7 @@ pub use secret::Secret; use stdio::stdin_stdout_to_console; /// Message sent by the credential helper on startup -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct CredentialHello { // Protocol versions supported by the credential process. pub v: Vec, @@ -61,8 +61,8 @@ pub struct UnsupportedCredential; impl Credential for UnsupportedCredential { fn perform( &self, - _registry: &RegistryInfo, - _action: &Action, + _registry: &RegistryInfo<'_>, + _action: &Action<'_>, _args: &[&str], ) -> Result { Err(Error::UrlNotSupported) @@ -70,7 +70,7 @@ impl Credential for UnsupportedCredential { } /// Message sent by Cargo to the credential helper after the hello -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct CredentialRequest<'a> { // Cargo will respond with the highest common protocol supported by both. @@ -80,23 +80,25 @@ pub struct CredentialRequest<'a> { #[serde(borrow, flatten)] pub action: Action<'a>, /// Additional command-line arguments passed to the credential provider. + #[serde(skip_serializing_if = "Vec::is_empty", default)] pub args: Vec<&'a str>, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct RegistryInfo<'a> { /// Registry index url pub index_url: &'a str, /// Name of the registry in configuration. May not be available. /// The crates.io registry will be `crates-io` (`CRATES_IO_REGISTRY`). + #[serde(skip_serializing_if = "Option::is_none")] pub name: Option<&'a str>, /// Headers from attempting to access a registry that resulted in a HTTP 401. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub headers: Vec, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] #[serde(tag = "kind", rename_all = "kebab-case")] pub enum Action<'a> { @@ -119,17 +121,19 @@ impl<'a> Display for Action<'a> { } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct LoginOptions<'a> { /// Token passed on the command line via --token or from stdin + #[serde(skip_serializing_if = "Option::is_none")] pub token: Option>, /// Optional URL that the user can visit to log in to the registry + #[serde(skip_serializing_if = "Option::is_none")] pub login_url: Option<&'a str>, } /// A record of what kind of operation is happening that we should generate a token for. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] #[serde(tag = "operation", rename_all = "kebab-case")] pub enum Operation<'a> { @@ -168,12 +172,13 @@ pub enum Operation<'a> { } /// Message sent by the credential helper -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(tag = "kind", rename_all = "kebab-case")] #[non_exhaustive] pub enum CredentialResponse { Get { token: Secret, + #[serde(flatten)] cache: CacheControl, operation_independent: bool, }, @@ -183,30 +188,35 @@ pub enum CredentialResponse { Unknown, } -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(rename_all = "kebab-case")] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(tag = "cache", rename_all = "kebab-case")] #[non_exhaustive] pub enum CacheControl { /// Do not cache this result. Never, /// Cache this result and use it for subsequent requests in the current Cargo invocation until the specified time. - Expires(#[serde(with = "time::serde::timestamp")] OffsetDateTime), + Expires { + #[serde(with = "time::serde::timestamp")] + expiration: OffsetDateTime, + }, /// Cache this result and use it for all subsequent requests in the current Cargo invocation. Session, #[serde(other)] Unknown, } -/// Credential process JSON protocol version. Incrementing -/// this version will prevent new credential providers -/// from working with older versions of Cargo. +/// Credential process JSON protocol version. If the protocol needs to make +/// a breaking change, a new protocol version should be defined (`PROTOCOL_VERSION_2`). +/// This library should offer support for both protocols if possible, by signaling +/// in the `CredentialHello` message. Cargo will then choose which protocol to use, +/// or it will error if there are no common protocol versions available. pub const PROTOCOL_VERSION_1: u32 = 1; pub trait Credential { /// Retrieves a token for the given registry. fn perform( &self, - registry: &RegistryInfo, - action: &Action, + registry: &RegistryInfo<'_>, + action: &Action<'_>, args: &[&str], ) -> Result; } @@ -236,11 +246,7 @@ fn doit( if len == 0 { return Ok(()); } - let request: CredentialRequest = serde_json::from_str(&buffer)?; - if request.v != PROTOCOL_VERSION_1 { - return Err(format!("unsupported protocol version {}", request.v).into()); - } - + let request = deserialize_request(&buffer)?; let response = stdin_stdout_to_console(|| { credential.perform(&request.registry, &request.action, &request.args) })?; @@ -250,6 +256,17 @@ fn doit( } } +/// Deserialize a request from Cargo. +fn deserialize_request( + value: &str, +) -> Result, Box> { + let request: CredentialRequest<'_> = serde_json::from_str(&value)?; + if request.v != PROTOCOL_VERSION_1 { + return Err(format!("unsupported protocol version {}", request.v).into()); + } + Ok(request) +} + /// Read a line of text from stdin. pub fn read_line() -> Result { let mut buf = String::new(); @@ -259,8 +276,8 @@ pub fn read_line() -> Result { /// Prompt the user for a token. pub fn read_token( - login_options: &LoginOptions, - registry: &RegistryInfo, + login_options: &LoginOptions<'_>, + registry: &RegistryInfo<'_>, ) -> Result, Error> { if let Some(token) = &login_options.token { return Ok(token.to_owned()); @@ -276,3 +293,142 @@ pub fn read_token( Ok(Secret::from(read_line().map_err(Box::new)?)) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unsupported_version() { + // This shouldn't ever happen in practice, since the credential provider signals to Cargo which + // protocol versions it supports, and Cargo should only attempt to use one of those. + let msg = r#"{"v":999, "registry": {"index-url":""}, "args":[], "kind": "unexpected"}"#; + assert_eq!( + "unsupported protocol version 999", + deserialize_request(msg).unwrap_err().to_string() + ); + } + + #[test] + fn cache_control() { + let cc = CacheControl::Expires { + expiration: OffsetDateTime::from_unix_timestamp(1693928537).unwrap(), + }; + let json = serde_json::to_string(&cc).unwrap(); + assert_eq!(json, r#"{"cache":"expires","expiration":1693928537}"#); + + let cc = CacheControl::Session; + let json = serde_json::to_string(&cc).unwrap(); + assert_eq!(json, r#"{"cache":"session"}"#); + + let cc: CacheControl = serde_json::from_str(r#"{"cache":"unknown-kind"}"#).unwrap(); + assert_eq!(cc, CacheControl::Unknown); + + assert_eq!( + "missing field `expiration`", + serde_json::from_str::(r#"{"cache":"expires"}"#) + .unwrap_err() + .to_string() + ); + } + + #[test] + fn credential_response() { + let cr = CredentialResponse::Get { + cache: CacheControl::Never, + operation_independent: true, + token: Secret::from("value".to_string()), + }; + let json = serde_json::to_string(&cr).unwrap(); + assert_eq!( + json, + r#"{"kind":"get","token":"value","cache":"never","operation_independent":true}"# + ); + + let cr = CredentialResponse::Login; + let json = serde_json::to_string(&cr).unwrap(); + assert_eq!(json, r#"{"kind":"login"}"#); + + let cr: CredentialResponse = + serde_json::from_str(r#"{"kind":"unknown-kind","extra-data":true}"#).unwrap(); + assert_eq!(cr, CredentialResponse::Unknown); + + let cr: CredentialResponse = + serde_json::from_str(r#"{"kind":"login","extra-data":true}"#).unwrap(); + assert_eq!(cr, CredentialResponse::Login); + + let cr: CredentialResponse = serde_json::from_str(r#"{"kind":"get","token":"value","cache":"never","operation_independent":true,"extra-field-ignored":123}"#).unwrap(); + assert_eq!( + cr, + CredentialResponse::Get { + cache: CacheControl::Never, + operation_independent: true, + token: Secret::from("value".to_string()) + } + ); + } + + #[test] + fn credential_request() { + let get_oweners = CredentialRequest { + v: PROTOCOL_VERSION_1, + args: vec![], + registry: RegistryInfo { + index_url: "url", + name: None, + headers: vec![], + }, + action: Action::Get(Operation::Owners { name: "pkg" }), + }; + + let json = serde_json::to_string(&get_oweners).unwrap(); + assert_eq!( + json, + r#"{"v":1,"registry":{"index-url":"url"},"kind":"get","operation":"owners","name":"pkg"}"# + ); + + let cr: CredentialRequest<'_> = + serde_json::from_str(r#"{"extra-1":true,"v":1,"registry":{"index-url":"url","extra-2":true},"kind":"get","operation":"owners","name":"pkg","args":[]}"#).unwrap(); + assert_eq!(cr, get_oweners); + } + + #[test] + fn credential_request_logout() { + let unknown = CredentialRequest { + v: PROTOCOL_VERSION_1, + args: vec![], + registry: RegistryInfo { + index_url: "url", + name: None, + headers: vec![], + }, + action: Action::Logout, + }; + + let cr: CredentialRequest<'_> = serde_json::from_str( + r#"{"v":1,"registry":{"index-url":"url"},"kind":"logout","extra-1":true,"args":[]}"#, + ) + .unwrap(); + assert_eq!(cr, unknown); + } + + #[test] + fn credential_request_unknown() { + let unknown = CredentialRequest { + v: PROTOCOL_VERSION_1, + args: vec![], + registry: RegistryInfo { + index_url: "", + name: None, + headers: vec![], + }, + action: Action::Unknown, + }; + + let cr: CredentialRequest<'_> = serde_json::from_str( + r#"{"v":1,"registry":{"index-url":""},"kind":"unexpected-1","extra-1":true,"args":[]}"#, + ) + .unwrap(); + assert_eq!(cr, unknown); + } +} -- cgit v1.2.3