Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Add API token support #471

Merged
merged 26 commits into from
Nov 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
570588f
Add API token support
gabbifish Aug 26, 2019
087abce
remove debug prints
gabbifish Aug 26, 2019
b54132f
remove unnecessary print
gabbifish Aug 27, 2019
41bb7e2
fix tests
gabbifish Aug 27, 2019
b964210
implementation of interactive config
gabbifish Oct 24, 2019
5eee36d
introduce --token flag for inputting CF API tokens as auth (tokens ar…
gabbifish Oct 24, 2019
b1072f6
address nits
gabbifish Oct 24, 2019
317ead3
fix merge conflicts from master
gabbifish Oct 24, 2019
d612d54
make sure token support exists for KV, also fix up whoami so we can t…
gabbifish Oct 24, 2019
cd4b05e
upgrade to cloudflare-rs 0.4.0 and remove calls to user detail API (t…
gabbifish Oct 24, 2019
b5ffdef
cargo fmt
gabbifish Oct 24, 2019
8208d86
Merge branch 'master' into gabbi/add-api-token-support
gabbifish Oct 24, 2019
c7ed593
Update src/settings/global_user.rs
gabbifish Oct 25, 2019
ce2ceb6
use option 3
gabbifish Oct 31, 2019
6f02318
fix
gabbifish Oct 31, 2019
d159825
fix readme merge
gabbifish Oct 31, 2019
50b1911
another readme fix
gabbifish Oct 31, 2019
36cd2da
add extra test for new api token config option
gabbifish Nov 1, 2019
ee96340
Merge branch 'master' into gabbi/add-api-token-support
gabbifish Nov 1, 2019
73f127f
remove unnecessary return from add_auth_headers
gabbifish Nov 1, 2019
2acd231
Merge branch 'gabbi/add-api-token-support' of github.com:cloudflare/w…
gabbifish Nov 1, 2019
496f7a6
Update src/commands/preview/upload.rs
gabbifish Nov 1, 2019
2aa1c94
Update src/commands/preview/upload.rs
gabbifish Nov 1, 2019
64c9c62
Update src/commands/whoami/mod.rs
gabbifish Nov 1, 2019
bda3f82
move interactive logic to main.rs
gabbifish Nov 1, 2019
481c0e8
Merge branch 'master' into gabbi/add-api-token-support
gabbifish Nov 1, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ clap = "2.32.0"
config = "0.9.2"
console = "0.7.5"
dirs = "1.0.5"
cloudflare = "0.3.2"
cloudflare = "0.4.0"
env_logger = "0.6.1"
failure = "0.1.5"
log = "0.4.6"
Expand Down
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,21 @@ $ wrangler publish

### 🔧 `config`

Configure your global Cloudflare user. This is an interactive command that will prompt you for your email and API key:
Configure your global Cloudflare user. This is an interactive command that will prompt you for your API token:

```bash
wrangler config
Enter API token:
superlongapitoken
```

You can also provide your email and global API key (this is not recommended for security reasons):
gabbifish marked this conversation as resolved.
Show resolved Hide resolved
```bash
wrangler config --api-key
Enter email:
[email protected]
Enter api key:
...
Enter global API key:
superlongapikey
```

You can also [use environment variables](https://developers.cloudflare.com/workers/tooling/wrangler/configuration/) to configure these values.
ashleymichal marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -102,10 +109,19 @@ $ wrangler publish

Additionally, you can configure different [environments](https://developers.cloudflare.com/workers/tooling/wrangler/configuration/environments).

You can also use environment variables to handle authentication when you publish a Worker.

```bash
wrangler publish
```
```bash
# e.g.
CF_API_TOKEN=superlongtoken wrangler publish
# where
# $CF_API_TOKEN -> your Cloudflare API token

CF_API_KEY=superlongapikey [email protected] wrangler publish
# where
# $CF_API_KEY -> your Cloudflare API key
# $CF_EMAIL -> your Cloudflare account email
```

### 🗂 `kv`

Expand Down
6 changes: 2 additions & 4 deletions src/commands/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ pub fn set_file_mode(file: &PathBuf) {
.expect("could not set permissions on file");
}

pub fn global_config(email: String, api_key: String) -> Result<(), failure::Error> {
let s = GlobalUser { email, api_key };

let toml = toml::to_string(&s)?;
pub fn global_config(user: &GlobalUser) -> Result<(), failure::Error> {
let toml = toml::to_string(&user)?;

let config_dir = get_global_config_dir().expect("could not find global config directory");
fs::create_dir_all(&config_dir)?;
Expand Down
3 changes: 2 additions & 1 deletion src/commands/kv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::time::Duration;

use cloudflare::framework::auth::Credentials;
use cloudflare::framework::response::ApiFailure;
use cloudflare::framework::{HttpApiClient, HttpApiClientConfig};
use cloudflare::framework::{Environment, HttpApiClient, HttpApiClientConfig};

use http::status::StatusCode;
use percent_encoding::{percent_encode, PATH_SEGMENT_ENCODE_SET};
Expand Down Expand Up @@ -72,6 +72,7 @@ fn api_client(user: &GlobalUser) -> Result<HttpApiClient, failure::Error> {
// This is useful for bulk upload operations.
http_timeout: Duration::from_secs(5 * 60),
},
Environment::Production,
ashleymichal marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/preview/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn upload(
"You haven't run `wrangler config`. Running preview without authentication",
);
message::help(
"Run `wrangler config` or set $CF_API_KEY and $CF_EMAIL to configure your user.",
"Run `wrangler config` or set either $CF_API_TOKEN or ($CF_EMAIL, $CF_API_KEY) to configure your user.",
);

if sites_preview {
Expand Down
14 changes: 11 additions & 3 deletions src/commands/whoami/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
use crate::settings::global_user::GlobalUser;
use crate::terminal::{emoji, message};

pub fn whoami(user: &GlobalUser) {
pub fn whoami(user: &GlobalUser) -> Result<(), failure::Error> {
// If using email + API key for auth, simply prints out email from config file.
let email: String = match &user.email {
Some(email) => email.to_string(),
None => failure::bail!(
"At the moment, Wrangler cannot get user information for users using API tokens"
gabbifish marked this conversation as resolved.
Show resolved Hide resolved
),
};

let msg = format!(
"{} You are logged in with the email '{}'.",
emoji::WAVING,
user.email
email
);

message::info(&msg);
Ok(())
}
28 changes: 25 additions & 3 deletions src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,34 @@ pub fn client(feature: Option<&str>) -> Client {

pub fn auth_client(feature: Option<&str>, user: &GlobalUser) -> Client {
let mut headers = headers(feature);
headers.insert("X-Auth-Key", HeaderValue::from_str(&user.api_key).unwrap());
headers.insert("X-Auth-Email", HeaderValue::from_str(&user.email).unwrap());
add_auth_headers(&mut headers, user);

builder()
.default_headers(headers)
.default_headers(headers.to_owned())
.redirect(RedirectPolicy::none())
.build()
.expect("could not create authenticated http client")
}

fn add_auth_headers<'a>(headers: &'a mut HeaderMap, user: &GlobalUser) {
match &user.api_token {
Some(token) => headers.insert(
"Authorization",
HeaderValue::from_str(&format!("Bearer {}", &token)).unwrap(),
),
None => {
// fallback to email + API key auth option
match &user.email {
Some(email) => {
headers.insert("X-Auth-Email", HeaderValue::from_str(&email).unwrap())
}
None => None,
};
match &user.api_key {
Some(key) => headers.insert("X-Auth-Key", HeaderValue::from_str(&key).unwrap()),
None => None,
};
None
}
};
}
54 changes: 44 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use wrangler::commands;
use wrangler::commands::kv::key::KVMetaData;
use wrangler::installer;
use wrangler::settings;
use wrangler::settings::global_user::GlobalUser;
use wrangler::settings::target::TargetType;
use wrangler::terminal::emoji;
use wrangler::terminal::message;
Expand Down Expand Up @@ -369,7 +370,13 @@ fn run() -> Result<(), failure::Error> {
.about(&*format!(
"{} Setup wrangler with your Cloudflare account",
emoji::SLEUTH
)),
))
.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.")
.long("api-key")
.takes_value(false),
),
)
.subcommand(
SubCommand::with_name("subdomain")
Expand All @@ -391,15 +398,42 @@ fn run() -> Result<(), failure::Error> {

let config_path = Path::new("./wrangler.toml");

if let Some(_matches) = matches.subcommand_matches("config") {
println!("Enter email: ");
let mut email: String = read!("{}\n");
gabbifish marked this conversation as resolved.
Show resolved Hide resolved
email.truncate(email.trim_end().len());
println!("Enter api key: ");
let mut api_key: String = read!("{}\n");
api_key.truncate(api_key.trim_end().len());
if let Some(matches) = matches.subcommand_matches("config") {
let api_key = matches.is_present("api-key");

let mut user = GlobalUser {
email: None,
api_key: None,
api_token: None,
};

if !api_key {
// Default: use API token.
message::info("Looking to use a Global API Key and email instead? Run \"wrangler config --api-key\". (Not Recommended)");
println!("Enter API token: ");
let mut api_token_str: String = read!("{}\n");
api_token_str.truncate(api_token_str.trim_end().len());
if !api_token_str.is_empty() {
user.api_token = Some(api_token_str);
}
} else {
message::warn("We don't recommend using your Global API Key! Please consider using an API Token instead. https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys");
println!("Enter email: ");
let mut email_str: String = read!("{}\n");
email_str.truncate(email_str.trim_end().len());
if !email_str.is_empty() {
user.email = Some(email_str);
}

println!("Enter global API key: ");
let mut api_key_str: String = read!("{}\n");
api_key_str.truncate(api_key_str.trim_end().len());
if !api_key_str.is_empty() {
user.api_key = Some(api_key_str);
}
}

commands::global_config(email, api_key)?;
commands::global_config(&user)?;
} else if let Some(matches) = matches.subcommand_matches("generate") {
let name = matches.value_of("name").unwrap_or("worker");
let site = matches.is_present("site");
Expand Down Expand Up @@ -478,7 +512,7 @@ fn run() -> Result<(), failure::Error> {
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;

commands::whoami(&user);
commands::whoami(&user)?;
gabbifish marked this conversation as resolved.
Show resolved Hide resolved
} else if let Some(matches) = matches.subcommand_matches("publish") {
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;
Expand Down
18 changes: 13 additions & 5 deletions src/settings/global_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use config::{Config, Environment, File};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GlobalUser {
pub email: String,
pub api_key: String,
pub email: Option<String>,
pub api_key: Option<String>,
pub api_token: Option<String>,
gabbifish marked this conversation as resolved.
Show resolved Hide resolved
}

impl GlobalUser {
Expand All @@ -22,9 +23,16 @@ impl GlobalUser {

impl From<GlobalUser> for Credentials {
fn from(user: GlobalUser) -> Credentials {
if let Some(token) = user.api_token {
return Credentials::UserAuthToken { token: token };
}

// fallback to email and global API key.
// If either of the fields below are None, just substitute in an empty string
// and these credentials will trigger the appropriate "missing field" response from the API.
Credentials::UserAuthKey {
key: user.api_key,
email: user.email,
key: user.api_key.unwrap_or("".to_string()),
email: user.email.unwrap_or("".to_string()),
}
}
}
Expand All @@ -50,7 +58,7 @@ fn get_global_config() -> Result<GlobalUser, failure::Error> {
}

// Eg.. `CF_API_KEY=farts` would set the `account_auth_key` key
// envs are: CF_API_KEY and CF_EMAIL
// envs are: CF_EMAIL, CF_API_KEY and CF_API_TOKEN
s.merge(Environment::with_prefix("CF"))?;

let global_user: Result<GlobalUser, config::ConfigError> = s.try_into();
Expand Down
Loading