use super::API_URL;
use crate::error::NeocitiesErr;
use std::env;
use url::form_urlencoded::byte_serialize;
pub const ENV_KEY: &'static str = "NEOCITIES_KEY";
pub const ENV_USER: &'static str = "NEOCITIES_USER";
pub const ENV_PASS: &'static str = "NEOCITIES_PASS";
pub struct Credentials {}
impl Credentials {
pub fn new() -> Credentials {
Credentials {}
}
pub fn get_api_key(&self) -> Option<String> {
match env::var(ENV_KEY) {
Ok(s) => Some(s),
_ => None,
}
}
pub fn get_username(&self) -> Option<String> {
match env::var(ENV_USER) {
Ok(u) => Some(u),
_ => None,
}
}
pub fn get_password(&self) -> Option<String> {
match env::var(ENV_PASS) {
Ok(p) => Some(p),
_ => None,
}
}
pub fn have_env_vars() -> bool {
let cred = Credentials::new();
if cred.get_username().is_none() || cred.get_password().is_none() {
return false;
}
return true;
}
pub fn set_app_env(user: Option<&str>, password: Option<&str>, api_key: Option<&str>) {
if let Some(u) = user {
env::set_var(ENV_USER, u)
} else {
env::remove_var(ENV_USER);
}
if let Some(p) = password {
env::set_var(ENV_PASS, p)
} else {
env::remove_var(ENV_PASS);
}
if let Some(k) = api_key {
env::set_var(ENV_KEY, k)
} else {
env::remove_var(ENV_KEY);
}
}
pub fn run_inside_temp_env(
user: Option<&str>,
password: Option<&str>,
api_key: Option<&str>,
callback: &dyn Fn(),
) {
let u_orig: String;
let u_wrapped = match env::var(ENV_USER) {
Ok(u) => {
u_orig = u;
Some(u_orig.as_str())
}
_ => None,
};
let p_orig: String;
let p_wrapped = match env::var(ENV_PASS) {
Ok(p) => {
p_orig = p;
Some(p_orig.as_str())
}
_ => None,
};
let k_orig: String;
let k_wrapped = match env::var(ENV_KEY) {
Ok(k) => {
k_orig = k;
Some(k_orig.as_str())
}
_ => None,
};
Self::set_app_env(user, password, api_key);
callback();
Self::set_app_env(u_wrapped, p_wrapped, k_wrapped);
}
}
#[derive(Debug)]
pub struct Auth {
pub url: String,
pub api_key: Option<String>,
}
impl Auth {
pub fn authenticate(
cred: Credentials,
path: &str,
query_string: Option<QueryString>,
) -> Result<Auth, NeocitiesErr> {
let mut url: String;
let mut api_key: Option<String> = None;
if let Some(k) = cred.get_api_key() {
api_key = Some(k);
url = format!("https://{}{}", API_URL, path);
} else {
let user = match cred.get_username() {
Some(u) => {
let user_urlencoded: String = byte_serialize(u.as_bytes()).collect();
user_urlencoded
}
None => return Err(NeocitiesErr::MissingUser),
};
let pass = match cred.get_password() {
Some(p) => {
let pass_urlencoded: String = byte_serialize(p.as_bytes()).collect();
pass_urlencoded
}
None => return Err(NeocitiesErr::MissingPassword),
};
url = format!("https://{}:{}@{}{}", user, pass, API_URL, path);
}
if let Some(q) = query_string {
url.push_str(format!("?{}={}", q.key, q.value).as_str());
}
Ok(Auth { url, api_key })
}
}
pub const ENV_VAR_MSG: &'static str = "
Before you can interact with Neocities CLI, you must first set the following
environment variables:
Example (Linux):
export NEOCITIES_USER=<your_username>
export NEOCITIES_USER=<your_password>
You can also use your Neocities API key (Optional):
export NEOCITIES_KEY=<your_key>
";
pub struct QueryString {
pub key: String,
pub value: String,
}
impl QueryString {
pub fn new(key: String, value: String) -> QueryString {
QueryString { key, value }
}
}
#[cfg(test)]
mod tests {
use super::{Auth, Credentials};
use crate::{client::info, error::NeocitiesErr};
use serial_test::serial;
#[test]
#[serial(env)]
fn env_key() {
let key = "foo";
Credentials::run_inside_temp_env(None, None, Some(key), &|| {
let creds = Credentials::new();
assert_eq!(creds.get_api_key().unwrap(), key);
})
}
#[test]
#[serial(env)]
fn env_user() {
let usr = "foo";
Credentials::run_inside_temp_env(Some(usr), None, None, &|| {
let creds = Credentials::new();
assert_eq!(creds.get_username().unwrap(), usr);
})
}
#[test]
#[serial(env)]
fn env_pass() {
let pass = "foo";
Credentials::run_inside_temp_env(None, Some(pass), None, &|| {
let creds = Credentials::new();
assert_eq!(creds.get_password().unwrap(), pass);
})
}
#[test]
#[serial(env)]
fn have_no_env_vars() {
Credentials::run_inside_temp_env(None, None, None, &|| {
assert_eq!(Credentials::have_env_vars(), false);
});
}
#[test]
#[serial(env)]
fn have_env_vars_usr() {
Credentials::run_inside_temp_env(Some("foo"), None, None, &|| {
assert_eq!(Credentials::have_env_vars(), false);
});
}
#[test]
#[serial(env)]
fn have_env_vars_pass() {
Credentials::run_inside_temp_env(None, Some("bar"), None, &|| {
assert_eq!(Credentials::have_env_vars(), false);
});
}
#[test]
#[serial(env)]
fn have_env_vars_usr_pass() {
Credentials::run_inside_temp_env(Some("foo"), Some("bar"), None, &|| {
assert_eq!(Credentials::have_env_vars(), true);
});
}
#[test]
#[serial(env)]
fn have_all_env_vars_set() {
Credentials::run_inside_temp_env(Some("foo"), Some("bar"), Some("baz"), &|| {
assert_eq!(Credentials::have_env_vars(), true);
});
}
#[test]
#[serial(env)]
fn auth_no_env_vars() {
Credentials::run_inside_temp_env(None, None, None, &|| {
let result = Auth::authenticate(Credentials::new(), info::KEY, None);
assert_eq!(
result.unwrap_err().to_string(),
NeocitiesErr::MissingUser.to_string()
);
});
}
#[test]
#[serial(env)]
fn auth_no_env_password() {
Credentials::run_inside_temp_env(Some("foo"), None, None, &|| {
let result = Auth::authenticate(Credentials::new(), info::KEY, None);
assert_eq!(
result.unwrap_err().to_string(),
NeocitiesErr::MissingPassword.to_string()
);
});
}
#[test]
#[serial(env)]
fn auth_no_env_api_key() {
Credentials::run_inside_temp_env(Some("foo"), Some("bar"), None, &|| {
let result = Auth::authenticate(Credentials::new(), info::KEY, None);
assert_eq!(result.is_ok(), true);
assert_eq!(
result.as_ref().unwrap().url,
format!("https://{}:{}@neocities.org/api/info", "foo", "bar")
);
assert_eq!(result.unwrap().api_key, None);
});
}
#[test]
#[serial(env)]
fn auth_all_env_vars_set() {
Credentials::run_inside_temp_env(Some("foo"), Some("bar"), Some("baz"), &|| {
let result = Auth::authenticate(Credentials::new(), info::KEY, None);
assert_eq!(result.is_ok(), true);
assert_eq!(
result.as_ref().unwrap().url,
format!("https://neocities.org/api/info")
);
assert_eq!(result.as_ref().unwrap().api_key.is_some(), true);
assert_eq!(result.unwrap().api_key.unwrap(), "baz".to_string());
});
}
}