From f1a721ca8942de61c07617efd235e03388175259 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Mon, 4 Nov 2019 15:39:19 -0800 Subject: [PATCH 1/8] Refactor cloudflare-rs cli to call from http.rs --- src/commands/kv/mod.rs | 132 ++++++++++++----------------------------- src/http.rs | 95 +++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 94 deletions(-) diff --git a/src/commands/kv/mod.rs b/src/commands/kv/mod.rs index 419466287..e0678adc6 100644 --- a/src/commands/kv/mod.rs +++ b/src/commands/kv/mod.rs @@ -1,17 +1,16 @@ use std::collections::HashSet; use std::time::Duration; -use cloudflare::framework::auth::Credentials; use cloudflare::framework::response::ApiFailure; -use cloudflare::framework::{Environment, HttpApiClient, HttpApiClientConfig}; +use cloudflare::framework::{HttpApiClient, HttpApiClientConfig}; -use http::status::StatusCode; use percent_encoding::{percent_encode, PATH_SEGMENT_ENCODE_SET}; use crate::settings::global_user::GlobalUser; use crate::settings::target::Target; -use crate::terminal::emoji; -use crate::terminal::message; + +use crate::http; +use crate::http::ErrorCodeDetail; pub mod bucket; pub mod bulk; @@ -23,6 +22,40 @@ const INTERACTIVE_RESPONSE_LEN: usize = 1; const YES: &str = "y"; const NO: &str = "n"; +// Create a special API client that has a longer timeout than usual, given that KV operations +// can be lengthy if payloads are large. +fn api_client(user: &GlobalUser) -> Result { + http::api_client( + user, + HttpApiClientConfig { + // Use 5 minute timeout instead of default 30-second one. + // This is useful for bulk upload operations. + http_timeout: Duration::from_secs(5 * 60), + }, + ) +} + +fn format_error(e: ApiFailure) -> String { + http::format_error(e, ErrorCodeDetail::WorkersKV) +} + +pub fn validate_target(target: &Target) -> Result<(), failure::Error> { + let mut missing_fields = Vec::new(); + + if target.account_id.is_empty() { + missing_fields.push("account_id") + }; + + if !missing_fields.is_empty() { + failure::bail!( + "Your wrangler.toml is missing the following field(s): {:?}", + missing_fields + ) + } else { + Ok(()) + } +} + fn check_duplicate_namespaces(target: &Target) -> bool { // HashSet for detecting duplicate namespace bindings let mut binding_names: HashSet = HashSet::new(); @@ -64,41 +97,6 @@ pub fn get_namespace_id(target: &Target, binding: &str) -> Result Result { - HttpApiClient::new( - Credentials::from(user.to_owned()), - HttpApiClientConfig { - // Use 5 minute timeout instead of default 30-second one. - // This is useful for bulk upload operations. - http_timeout: Duration::from_secs(5 * 60), - }, - Environment::Production, - ) -} - -fn format_error(e: ApiFailure) -> String { - match e { - ApiFailure::Error(status, api_errors) => { - print_status_code_context(status); - let mut complete_err = "".to_string(); - for error in api_errors.errors { - let error_msg = - format!("{} Error {}: {}\n", emoji::WARN, error.code, error.message); - - let suggestion = help(error.code); - if !suggestion.is_empty() { - let help_msg = format!("{} {}\n", emoji::SLEUTH, suggestion); - complete_err.push_str(&format!("{}{}", error_msg, help_msg)); - } else { - complete_err.push_str(&error_msg) - } - } - complete_err - } - ApiFailure::Invalid(reqwest_err) => format!("{} Error: {}", emoji::WARN, reqwest_err), - } -} - // For interactively handling deletes (and discouraging accidental deletes). // Input like "yes", "Yes", "no", "No" will be accepted, thanks to the whitespace-stripping // and lowercasing logic below. @@ -119,60 +117,6 @@ fn url_encode_key(key: &str) -> String { percent_encode(key.as_bytes(), PATH_SEGMENT_ENCODE_SET).to_string() } -// For handling cases where the API gateway returns errors via HTTP status codes -// (no KV error code is given). -fn print_status_code_context(status_code: StatusCode) { - match status_code { - // Folks should never hit PAYLOAD_TOO_LARGE, given that Wrangler ensures that bulk file uploads - // are max ~50 MB in size. This case is handled anyways out of an abundance of caution. - StatusCode::PAYLOAD_TOO_LARGE => - message::warn("Returned status code 413, Payload Too Large. Please make sure your upload is less than 100MB in size"), - StatusCode::GATEWAY_TIMEOUT => - message::warn("Returned status code 504, Gateway Timeout. Please try again in a few seconds"), - _ => (), - } -} - -fn help(error_code: u16) -> &'static str { - // https://api.cloudflare.com/#workers-kv-namespace-errors - match error_code { - 7003 | 7000 => { - "Your wrangler.toml is likely missing the field \"account_id\", which is required to write to Workers KV." - } - // namespace errors - 10010 | 10011 | 10012 | 10013 | 10014 | 10018 => { - "Run `wrangler kv:namespace list` to see your existing namespaces with IDs" - } - 10009 => "Run `wrangler kv:key list` to see your existing keys", // key errors - // TODO: link to more info - // limit errors - 10022 | 10024 | 10030 => "See documentation", - // TODO: link to tool for this? - // legacy namespace errors - 10021 | 10035 | 10038 => "Consider moving this namespace", - // cloudflare account errors - 10017 | 10026 => "Workers KV is a paid feature, please upgrade your account (https://www.cloudflare.com/products/workers-kv/)", - _ => "", - } -} - -pub fn validate_target(target: &Target) -> Result<(), failure::Error> { - let mut missing_fields = Vec::new(); - - if target.account_id.is_empty() { - missing_fields.push("account_id") - }; - - if !missing_fields.is_empty() { - failure::bail!( - "Your wrangler.toml is missing the following field(s): {:?}", - missing_fields - ) - } else { - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::commands::kv; diff --git a/src/http.rs b/src/http.rs index 876d257f2..1aa60d554 100644 --- a/src/http.rs +++ b/src/http.rs @@ -5,6 +5,17 @@ use std::time::Duration; use crate::install; use crate::settings::global_user::GlobalUser; +use http::status::StatusCode; + +use cloudflare::framework::auth::Credentials; +use cloudflare::framework::response::ApiFailure; +use cloudflare::framework::{Environment, HttpApiClient, HttpApiClientConfig}; + +use crate::terminal::emoji; +use crate::terminal::message; + +////---------------------------OLD API CLIENT CODE---------------------------//// +// todo: remove this and replace it entirely with cloudflare-rs fn headers(feature: Option<&str>) -> HeaderMap { let version = if install::target::DEBUG { "dev" @@ -61,3 +72,87 @@ fn add_auth_headers<'a>(headers: &'a mut HeaderMap, user: &GlobalUser) { } } } + +////---------------------------NEW API CLIENT CODE---------------------------//// +pub fn api_client( + user: &GlobalUser, + config: HttpApiClientConfig, +) -> Result { + HttpApiClient::new( + Credentials::from(user.to_owned()), + config, + Environment::Production, + ) +} + +// This enum allows callers of format_error() to specify a help_*() function for adding additional +// detail to error messages, if desired. +pub enum ErrorCodeDetail { + None, + WorkersKV, +} + +// Format errors from the cloudflare-rs cli for printing. +pub fn format_error(e: ApiFailure, err_type: ErrorCodeDetail) -> String { + match e { + ApiFailure::Error(status, api_errors) => { + print_status_code_context(status); + let mut complete_err = "".to_string(); + for error in api_errors.errors { + let error_msg = + format!("{} Error {}: {}\n", emoji::WARN, error.code, error.message); + + let suggestion = match err_type { + ErrorCodeDetail::WorkersKV => kv_help(error.code), + _ => "", + }; + if !suggestion.is_empty() { + let help_msg = format!("{} {}\n", emoji::SLEUTH, suggestion); + complete_err.push_str(&format!("{}{}", error_msg, help_msg)); + } else { + complete_err.push_str(&error_msg) + } + } + complete_err + } + ApiFailure::Invalid(reqwest_err) => format!("{} Error: {}", emoji::WARN, reqwest_err), + } +} + +// For handling cases where the API gateway returns errors via HTTP status codes +// (no API-specific, more granular error code is given). +fn print_status_code_context(status_code: StatusCode) { + match status_code { + // Folks should never hit PAYLOAD_TOO_LARGE, given that Wrangler ensures that bulk file uploads + // are max ~50 MB in size. This case is handled anyways out of an abundance of caution. + StatusCode::PAYLOAD_TOO_LARGE => + message::warn("Returned status code 413, Payload Too Large. Please make sure your upload is less than 100MB in size"), + StatusCode::GATEWAY_TIMEOUT => + message::warn("Returned status code 504, Gateway Timeout. Please try again in a few seconds"), + _ => (), + } +} + +// kv_help() provides more detailed explanations of Workers KV API error codes. +// See https://api.cloudflare.com/#workers-kv-namespace-errors for details. +fn kv_help(error_code: u16) -> &'static str { + match error_code { + 7003 | 7000 => { + "Your wrangler.toml is likely missing the field \"account_id\", which is required to write to Workers KV." + } + // namespace errors + 10010 | 10011 | 10012 | 10013 | 10014 | 10018 => { + "Run `wrangler kv:namespace list` to see your existing namespaces with IDs" + } + 10009 => "Run `wrangler kv:key list` to see your existing keys", // key errors + // TODO: link to more info + // limit errors + 10022 | 10024 | 10030 => "See documentation", + // TODO: link to tool for this? + // legacy namespace errors + 10021 | 10035 | 10038 => "Consider moving this namespace", + // cloudflare account errors + 10017 | 10026 => "Workers KV is a paid feature, please upgrade your account (https://www.cloudflare.com/products/workers-kv/)", + _ => "", + } +} From 39bda9673469d668ead3067cc2f20c96574722d4 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Mon, 4 Nov 2019 16:49:59 -0800 Subject: [PATCH 2/8] Add credential checking logic when auth params are provided in wrangler config --- src/commands/config/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ src/http.rs | 3 +-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index 944685740..5a301022d 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -5,8 +5,14 @@ use std::fs::File; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use crate::http; +use crate::http::ErrorCodeDetail; use crate::settings::global_user::{get_global_config_dir, GlobalUser}; +use cloudflare::endpoints::user::{GetUserDetails, GetUserTokenStatus}; +use cloudflare::framework::apiclient::ApiClient; +use cloudflare::framework::HttpApiClientConfig; + // set the permissions on the dir, we want to avoid that other user reads to // file #[cfg(not(target_os = "windows"))] @@ -18,6 +24,9 @@ pub fn set_file_mode(file: &PathBuf) { } pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { + message::info("Verifying that provided credentials are valid..."); + validate_credentials(user)?; + let toml = toml::to_string(&user)?; let config_dir = get_global_config_dir().expect("could not find global config directory"); @@ -37,3 +46,30 @@ pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { Ok(()) } + +// validate_credentials() checks the /user/tokens/verify endpoint (for API token) +// or /user endpoint (for global API key) to ensure provided credentials actually work. +pub fn validate_credentials(user: &GlobalUser) -> Result<(), failure::Error> { + let client = http::api_client(user, HttpApiClientConfig::default())?; + + match user { + GlobalUser::TokenAuth { .. } => { + match client.request(&GetUserTokenStatus {}) { + Ok(success) => { + if success.result.status == "active" { + Ok(()) + } else { + failure::bail!("Auth check failed. Your token has status \"{}\", not \"active\".") + } + }, + Err(e) => failure::bail!("Auth check failed. Please make sure your API token is correct. \n{}", http::format_error(e, ErrorCodeDetail::None)) + } + } + GlobalUser::GlobalKeyAuth { .. } => { + match client.request(&GetUserDetails {}) { + Ok(_) => Ok(()), + Err(e) => failure::bail!("Auth check failed. Please make sure your email and global API key pair are correct. \n{}", http::format_error(e, ErrorCodeDetail::None)), + } + } + } +} diff --git a/src/http.rs b/src/http.rs index 1aa60d554..4a824b878 100644 --- a/src/http.rs +++ b/src/http.rs @@ -99,8 +99,7 @@ pub fn format_error(e: ApiFailure, err_type: ErrorCodeDetail) -> String { print_status_code_context(status); let mut complete_err = "".to_string(); for error in api_errors.errors { - let error_msg = - format!("{} Error {}: {}\n", emoji::WARN, error.code, error.message); + let error_msg = format!("{} Code {}: {}\n", emoji::WARN, error.code, error.message); let suggestion = match err_type { ErrorCodeDetail::WorkersKV => kv_help(error.code), From 60150c1a6b1e3b5c24d3956a4fec5b3a1d1d082d Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Mon, 4 Nov 2019 15:39:19 -0800 Subject: [PATCH 3/8] Refactor cloudflare-rs cli to call from http.rs --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- src/http.rs | 8 ++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6dfa0487..dc622b938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "cloudflare" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2377,7 +2377,7 @@ dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "binary-install 0.0.3-alpha (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudflare 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudflare 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2496,7 +2496,7 @@ dependencies = [ "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cloudflare 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da6a058eb23a3c0957c1fa0ec8ffd451d48701fed8f143b2a59653b43d79a7f" +"checksum cloudflare 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "59ad92e7809c9c30862f371bdb613d7aaac8a5372332e323ab3e1b978693665c" "checksum config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6" "checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628" "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" diff --git a/Cargo.toml b/Cargo.toml index 5ddbf16fb..2a42cce56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ clap = "2.32.0" config = "0.9.2" console = "0.7.5" dirs = "1.0.5" -cloudflare = "0.4.0" +cloudflare = "0.4.1" env_logger = "0.6.1" failure = "0.1.5" log = "0.4.6" diff --git a/src/http.rs b/src/http.rs index 2c2a9fcac..a6e3fd84b 100644 --- a/src/http.rs +++ b/src/http.rs @@ -97,12 +97,8 @@ pub fn format_error(e: ApiFailure, err_helper: Option<&dyn Fn(u16) -> &'static s let error_msg = format!("{} Error {}: {}\n", emoji::WARN, error.code, error.message); - let suggestion = if let Some(annotate_help) = err_helper { - Some(annotate_help(error.code)) - } else { - None - }; - if let Some(suggestion_text) = suggestion { + if let Some(annotate_help) = err_helper { + let suggestion_text = annotate_help(error.code); let help_msg = format!("{} {}\n", emoji::SLEUTH, suggestion_text); complete_err.push_str(&format!("{}{}", error_msg, help_msg)); } else { From d7d627d497bd4355074f60cdff968fca3d58bff9 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Mon, 4 Nov 2019 16:49:59 -0800 Subject: [PATCH 4/8] Add credential checking logic when auth params are provided in wrangler config --- src/commands/config/mod.rs | 38 +++++++++++++++++++++++++++++++++++++- src/http.rs | 3 +-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index e5b75e534..b5a2ffa0c 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -5,9 +5,15 @@ use std::fs::File; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use crate::http; +use crate::http::ErrorCodeDetail; use crate::settings::global_user::{get_global_config_dir, GlobalUser}; -// set the permissions on the dir, we want to avoid other user reads of the file +use cloudflare::endpoints::user::{GetUserDetails, GetUserTokenStatus}; +use cloudflare::framework::apiclient::ApiClient; +use cloudflare::framework::HttpApiClientConfig; + +// set the permissions on the dir, we want to avoid that other user reads to file #[cfg(not(target_os = "windows"))] pub fn set_file_mode(file: &PathBuf) { File::open(&file) @@ -17,6 +23,9 @@ pub fn set_file_mode(file: &PathBuf) { } pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { + message::info("Verifying that provided credentials are valid..."); + validate_credentials(user)?; + let toml = toml::to_string(&user)?; let config_dir = get_global_config_dir().expect("could not find global config directory"); @@ -36,3 +45,30 @@ pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { Ok(()) } + +// validate_credentials() checks the /user/tokens/verify endpoint (for API token) +// or /user endpoint (for global API key) to ensure provided credentials actually work. +pub fn validate_credentials(user: &GlobalUser) -> Result<(), failure::Error> { + let client = http::api_client(user, HttpApiClientConfig::default())?; + + match user { + GlobalUser::TokenAuth { .. } => { + match client.request(&GetUserTokenStatus {}) { + Ok(success) => { + if success.result.status == "active" { + Ok(()) + } else { + failure::bail!("Auth check failed. Your token has status \"{}\", not \"active\".") + } + }, + Err(e) => failure::bail!("Auth check failed. Please make sure your API token is correct. \n{}", http::format_error(e, ErrorCodeDetail::None)) + } + } + GlobalUser::GlobalKeyAuth { .. } => { + match client.request(&GetUserDetails {}) { + Ok(_) => Ok(()), + Err(e) => failure::bail!("Auth check failed. Please make sure your email and global API key pair are correct. \n{}", http::format_error(e, ErrorCodeDetail::None)), + } + } + } +} diff --git a/src/http.rs b/src/http.rs index a6e3fd84b..3e02334f3 100644 --- a/src/http.rs +++ b/src/http.rs @@ -94,8 +94,7 @@ pub fn format_error(e: ApiFailure, err_helper: Option<&dyn Fn(u16) -> &'static s print_status_code_context(status); let mut complete_err = "".to_string(); for error in api_errors.errors { - let error_msg = - format!("{} Error {}: {}\n", emoji::WARN, error.code, error.message); + let error_msg = format!("{} Code {}: {}\n", emoji::WARN, error.code, error.message); if let Some(annotate_help) = err_helper { let suggestion_text = annotate_help(error.code); From ec9ae69911344281f2cdc46ce4f61ca93b79fb83 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Sat, 9 Nov 2019 12:30:24 -0800 Subject: [PATCH 5/8] respond to feedback --- src/commands/config/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index b5a2ffa0c..c6f4f6636 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -6,7 +6,6 @@ use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use crate::http; -use crate::http::ErrorCodeDetail; use crate::settings::global_user::{get_global_config_dir, GlobalUser}; use cloudflare::endpoints::user::{GetUserDetails, GetUserTokenStatus}; @@ -23,7 +22,7 @@ pub fn set_file_mode(file: &PathBuf) { } pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { - message::info("Verifying that provided credentials are valid..."); + message::info("alidating credentials..."); validate_credentials(user)?; let toml = toml::to_string(&user)?; @@ -58,16 +57,16 @@ pub fn validate_credentials(user: &GlobalUser) -> Result<(), failure::Error> { if success.result.status == "active" { Ok(()) } else { - failure::bail!("Auth check failed. Your token has status \"{}\", not \"active\".") + failure::bail!("Authentication check failed. Your token has status \"{}\", not \"active\".\nTry rolling your token on the Cloudflare dashboard.") } }, - Err(e) => failure::bail!("Auth check failed. Please make sure your API token is correct. \n{}", http::format_error(e, ErrorCodeDetail::None)) + Err(e) => failure::bail!("Authentication check failed. Please make sure your API token is correct.\n{}", http::format_error(e, None)) } } GlobalUser::GlobalKeyAuth { .. } => { match client.request(&GetUserDetails {}) { Ok(_) => Ok(()), - Err(e) => failure::bail!("Auth check failed. Please make sure your email and global API key pair are correct. \n{}", http::format_error(e, ErrorCodeDetail::None)), + Err(e) => failure::bail!("Auth check failed. Please make sure your email and global API key pair are correct.\n{}", http::format_error(e, None)), } } } From 655b1c001b5ac0b87e4c6a4133c88aaad0626f71 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Sat, 9 Nov 2019 12:44:57 -0800 Subject: [PATCH 6/8] more merge cleanup + add whitespace trimming to end of format_error output --- src/commands/config/mod.rs | 15 +-------------- src/http.rs | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index 7b300e555..b5b79f6f6 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -6,22 +6,13 @@ use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use crate::http; -<<<<<<< HEAD -======= -use crate::http::ErrorCodeDetail; ->>>>>>> 39bda9673469d668ead3067cc2f20c96574722d4 use crate::settings::global_user::{get_global_config_dir, GlobalUser}; use cloudflare::endpoints::user::{GetUserDetails, GetUserTokenStatus}; use cloudflare::framework::apiclient::ApiClient; use cloudflare::framework::HttpApiClientConfig; -<<<<<<< HEAD // set the permissions on the dir, we want to avoid that other user reads to file -======= -// set the permissions on the dir, we want to avoid that other user reads to -// file ->>>>>>> 39bda9673469d668ead3067cc2f20c96574722d4 #[cfg(not(target_os = "windows"))] pub fn set_file_mode(file: &PathBuf) { File::open(&file) @@ -31,11 +22,7 @@ pub fn set_file_mode(file: &PathBuf) { } pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { -<<<<<<< HEAD - message::info("alidating credentials..."); -======= - message::info("Verifying that provided credentials are valid..."); ->>>>>>> 39bda9673469d668ead3067cc2f20c96574722d4 + message::info("Validating credentials..."); validate_credentials(user)?; let toml = toml::to_string(&user)?; diff --git a/src/http.rs b/src/http.rs index 3e02334f3..b74ac786c 100644 --- a/src/http.rs +++ b/src/http.rs @@ -104,7 +104,7 @@ pub fn format_error(e: ApiFailure, err_helper: Option<&dyn Fn(u16) -> &'static s complete_err.push_str(&error_msg) } } - complete_err + complete_err.trim_end().to_string() // Trimming strings in place for String is apparently not a thing... } ApiFailure::Invalid(reqwest_err) => format!("{} Error: {}", emoji::WARN, reqwest_err), } From b8ac9f747a71d8a40dca492a7a76a6f88cffdfd0 Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Sun, 10 Nov 2019 18:27:48 -0800 Subject: [PATCH 7/8] fix usage test by providing a new flag that allows user to skip verification step --- src/commands/config/mod.rs | 8 +++++--- src/main.rs | 12 ++++++++++-- tests/config.rs | 6 +++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index b5b79f6f6..b438ea944 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -21,9 +21,11 @@ pub fn set_file_mode(file: &PathBuf) { .expect("could not set permissions on file"); } -pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> { - message::info("Validating credentials..."); - validate_credentials(user)?; +pub fn global_config(user: &GlobalUser, verify: bool) -> Result<(), failure::Error> { + if verify { + message::info("Validating credentials..."); + validate_credentials(user)?; + } let toml = toml::to_string(&user)?; diff --git a/src/main.rs b/src/main.rs index 6b7c593d2..341774597 100644 --- a/src/main.rs +++ b/src/main.rs @@ -379,9 +379,15 @@ fn run() -> Result<(), failure::Error> { )) .arg( Arg::with_name("api-key") - .help("use an email and global API key for authentication. This is not recommended; use API tokens (the default) if possible.") + .help("use an email and global API key for authentication. This is not recommended; use API tokens (the default) if possible") .long("api-key") .takes_value(false), + ) + .arg( + Arg::with_name("no-verify") + .help("do not verify provided credentials before writing out Wrangler config file") + .long("no-verify") + .takes_value(false), ), ) .subcommand( @@ -429,7 +435,9 @@ fn run() -> Result<(), failure::Error> { GlobalUser::GlobalKeyAuth { email, api_key } }; - commands::global_config(&user)?; + let verify = !matches.is_present("no-verify"); + + commands::global_config(&user, verify)?; } else if let Some(matches) = matches.subcommand_matches("generate") { let name = matches.value_of("name").unwrap_or("worker"); let site = matches.is_present("site"); diff --git a/tests/config.rs b/tests/config.rs index 98a77dd30..1da2f0897 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -13,7 +13,7 @@ fn it_generates_the_config_unix_eol() { #[test] fn it_generates_the_config_windows_eol() { generate_config_with("\r\n", false); // Test `wrangler config` - generate_config_with("\r\n", true); // Test `wrangler config` + generate_config_with("\r\n", true); // Test `wrangler config --api-key` } fn generate_config_with(eol: &str, use_api_key: bool) { @@ -73,10 +73,13 @@ api_key = "apikeythisissecretandlong" fn config_with_wrangler_home(home_dir: &str, use_api_key: bool) -> Child { let mut wrangler = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + // Don't verify provided information in the `wrangler config` invocation below; + // this is distinct from the parsing functionality this I/O test focuses on if use_api_key { wrangler .arg("config") .arg("--api-key") + .arg("--no-verify") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .env("WRANGLER_HOME", home_dir) @@ -85,6 +88,7 @@ fn config_with_wrangler_home(home_dir: &str, use_api_key: bool) -> Child { } else { wrangler .arg("config") + .arg("--no-verify") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .env("WRANGLER_HOME", home_dir) From 5d453f66161d0bcbb32ebf8ecaee0a0d9a1c81bd Mon Sep 17 00:00:00 2001 From: Gabbi Fisher Date: Mon, 11 Nov 2019 09:55:10 -0800 Subject: [PATCH 8/8] add link to key auth help message --- src/commands/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/config/mod.rs b/src/commands/config/mod.rs index b438ea944..a0f5f51cc 100644 --- a/src/commands/config/mod.rs +++ b/src/commands/config/mod.rs @@ -68,7 +68,7 @@ pub fn validate_credentials(user: &GlobalUser) -> Result<(), failure::Error> { GlobalUser::GlobalKeyAuth { .. } => { match client.request(&GetUserDetails {}) { Ok(_) => Ok(()), - Err(e) => failure::bail!("Authentication check failed. Please make sure your email and global API key pair are correct.\n{}", http::format_error(e, None)), + Err(e) => failure::bail!("Authentication check failed. Please make sure your email and global API key pair are correct. (https://developers.cloudflare.com/workers/quickstart/#global-api-key)\n{}", http::format_error(e, None)), } } }