pub mod error;
mod fs;
use std::{collections::HashMap, path::Path};
use reqwest::Url;
use serde::{Deserialize, Serialize};
use crate::error::{ArtifactoryAuthError, ArtifactoryAuthResult};
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Credentials {
pub username: String,
pub token: String,
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Tokens {
tokens: HashMap<String, Credentials>,
}
impl Tokens {
pub fn load(path: &Path) -> ArtifactoryAuthResult<Self> {
if let Some(contents) = fs::try_read(path)? {
let tokens: Tokens = serde_json::from_slice(&contents)
.map_err(|error| ArtifactoryAuthError::auth_parsing(path, error.to_string()))?;
Ok(tokens)
} else {
log::debug!("Artifactory tokens config not found");
Ok(Tokens::default())
}
}
pub fn get_credentials(&self, url: &Url) -> Option<&Credentials> {
if let Some(domain) = url.domain() {
self.tokens.get(domain)
} else {
log::warn!(
"Could not find credentials for artifactory url with invalid domain: {}",
url
);
None
}
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::*;
use jsonschema_valid::Config;
use serde_json::Value;
use tempfile::{tempdir, TempDir};
const SCHEMA: &str = include_str!("../resources/artiaa-format.json");
const EXAMPLE_FILE: &str = r#"{
"tokens": {
"example.com": {
"username": "example_user",
"token": "123456"
},
"artifactory.example.com": {
"username": "artifactory_user",
"token": "abcdef"
}
}
}"#;
fn write_test_config(contents: &str) -> TempDir {
let folder = tempdir().unwrap();
fs::write(folder.path().join("tokens.json"), contents).unwrap();
folder
}
#[test]
fn load_file() {
let folder = write_test_config(EXAMPLE_FILE);
Tokens::load(folder.path().join("tokens.json").as_ref()).unwrap();
}
#[test]
fn read_credential() {
let folder = write_test_config(EXAMPLE_FILE);
let tokens = Tokens::load(folder.path().join("tokens.json").as_ref()).unwrap();
let url = Url::from_str("https://example.com").unwrap();
assert_eq!(
tokens.get_credentials(&url).unwrap(),
&Credentials {
username: "example_user".to_string(),
token: "123456".to_string(),
}
);
let artifactory_url = Url::from_str("https://artifactory.example.com").unwrap();
assert_eq!(
tokens.get_credentials(&artifactory_url).unwrap(),
&Credentials {
username: "artifactory_user".to_string(),
token: "abcdef".to_string(),
}
);
}
#[test]
fn read_url_not_found() {
let folder = write_test_config(EXAMPLE_FILE);
let tokens: Tokens = Tokens::load(folder.path().join("tokens.json").as_ref()).unwrap();
let url = Url::from_str("https://other-example.com").unwrap();
assert!(tokens.get_credentials(&url).is_none())
}
#[test]
fn read_invalid_domain() {
let folder = write_test_config(EXAMPLE_FILE);
let tokens: Tokens = Tokens::load(folder.path().join("tokens.json").as_ref()).unwrap();
let url = Url::from_str("file://path/to/file").unwrap();
assert!(tokens.get_credentials(&url).is_none())
}
#[test]
fn valid_file_conforms_to_schema() {
let schema: Value = serde_json::from_str(SCHEMA).unwrap();
let example: Value = serde_json::from_str(EXAMPLE_FILE).unwrap();
let cfg = Config::from_schema(&schema, None).unwrap();
assert!(cfg.validate_schema().is_ok());
assert!(cfg.validate(&example).is_ok());
}
}