use std::env::{self, VarError};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::result::Result as StdResult;
use crate::{InitializationError, InitializationResult};
mod errors;
pub mod parsers;
pub use self::errors::*;
use self::parsers::*;
pub struct ResourceOwnerCredentials {
pub username: String,
pub password: String,
}
pub struct ClientCredentials {
pub client_id: String,
pub client_secret: String,
}
pub struct RequestTokenCredentials {
pub client_credentials: ClientCredentials,
pub owner_credentials: ResourceOwnerCredentials,
}
pub trait CredentialsProvider {
fn client_credentials(&self) -> CredentialsResult<ClientCredentials>;
fn owner_credentials(&self) -> CredentialsResult<ResourceOwnerCredentials>;
fn credentials(&self) -> CredentialsResult<RequestTokenCredentials> {
let client_credentials = self.client_credentials()?;
let owner_credentials = self.owner_credentials()?;
Ok(RequestTokenCredentials {
client_credentials,
owner_credentials,
})
}
}
pub struct SplitFileCredentialsProvider {
client_credentials_file_path: PathBuf,
owner_credentials_file_path: PathBuf,
client_credentials_parser: Box<dyn ClientCredentialsParser + Send + Sync + 'static>,
owner_credentials_parser: Box<dyn ResourceOwnerCredentialsParser + Send + Sync + 'static>,
}
impl SplitFileCredentialsProvider {
pub fn new<C, O, CP, UP>(
client_credentials_file_path: C,
owner_credentials_file_path: O,
client_credentials_parser: CP,
owner_credentials_parser: UP,
) -> Self
where
C: Into<PathBuf>,
O: Into<PathBuf>,
CP: ClientCredentialsParser + Send + Sync + 'static,
UP: ResourceOwnerCredentialsParser + Send + Sync + 'static,
{
SplitFileCredentialsProvider {
client_credentials_file_path: client_credentials_file_path.into(),
owner_credentials_file_path: owner_credentials_file_path.into(),
client_credentials_parser: Box::new(client_credentials_parser),
owner_credentials_parser: Box::new(owner_credentials_parser),
}
}
pub fn with_default_parsers<C, O>(
client_credentials_file_path: C,
owner_credentials_file_path: O,
) -> Self
where
C: Into<PathBuf>,
O: Into<PathBuf>,
{
SplitFileCredentialsProvider::new(
client_credentials_file_path,
owner_credentials_file_path,
DefaultClientCredentialsParser,
DefaultResourceOwnerCredentialsParser,
)
}
pub fn with_default_client_parser<C, O, P>(
client_credentials_file_path: C,
owner_credentials_file_path: O,
owner_credentials_parser: P,
) -> Self
where
C: Into<PathBuf>,
O: Into<PathBuf>,
P: ResourceOwnerCredentialsParser + Send + Sync + 'static,
{
SplitFileCredentialsProvider::new(
client_credentials_file_path,
owner_credentials_file_path,
DefaultClientCredentialsParser,
owner_credentials_parser,
)
}
pub fn with_default_client_parser_from_env<P>(
owner_credentials_parser: P,
) -> InitializationResult<Self>
where
P: ResourceOwnerCredentialsParser + Send + Sync + 'static,
{
let credentials_dir = credentials_dir_from_env().map_err(InitializationError)?;
let owner_file_name: PathBuf = match env::var("TOKKIT_CREDENTIALS_RESOURCE_OWNER_FILENAME")
{
Ok(dir) => dir.into(),
Err(VarError::NotPresent) => {
warn!("No owner file name. Assuming 'user.json'");
"user.json".into()
}
Err(err) => return Err(InitializationError(err.to_string())),
};
let client_file_name: PathBuf = match env::var("TOKKIT_CREDENTIALS_CLIENT_FILENAME") {
Ok(dir) => dir.into(),
Err(VarError::NotPresent) => {
warn!("No client file name. Assuming 'client.json'");
"client.json".into()
}
Err(err) => return Err(InitializationError(err.to_string())),
};
let mut owner_credentials_file_path = credentials_dir.clone();
owner_credentials_file_path.push(owner_file_name);
let mut client_credentials_file_path = credentials_dir;
client_credentials_file_path.push(client_file_name);
info!(
"Client credentials file path is '{}', owner credentials file path is '{}'.",
client_credentials_file_path.display(),
owner_credentials_file_path.display()
);
Ok(SplitFileCredentialsProvider::with_default_client_parser(
client_credentials_file_path,
owner_credentials_file_path,
owner_credentials_parser,
))
}
pub fn with_default_parsers_from_env() -> InitializationResult<Self> {
SplitFileCredentialsProvider::with_default_client_parser_from_env(
DefaultResourceOwnerCredentialsParser,
)
}
}
fn credentials_dir_from_env() -> StdResult<PathBuf, String> {
match env::var("TOKKIT_CREDENTIALS_DIR") {
Ok(dir) => Ok(dir.into()),
Err(VarError::NotPresent) => {
info!("'TOKKIT_CREDENTIALS_DIR' not found. Looking for 'CREDENTIALS_DIR'");
match env::var("CREDENTIALS_DIR") {
Ok(dir) => Ok(dir.into()),
Err(VarError::NotPresent) => {
Err("Path for credentials files not found. Please \
set 'TOKKIT_CREDENTIALS_DIR' or 'CREDENTIALS_DIR'."
.into())
}
Err(err) => Err(err.to_string()),
}
}
Err(err) => Err(err.to_string()),
}
}
impl CredentialsProvider for SplitFileCredentialsProvider {
fn client_credentials(&self) -> CredentialsResult<ClientCredentials> {
let mut file = File::open(&self.client_credentials_file_path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
self.client_credentials_parser.parse(&contents)
}
fn owner_credentials(&self) -> CredentialsResult<ResourceOwnerCredentials> {
let mut file = File::open(&self.owner_credentials_file_path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
self.owner_credentials_parser.parse(&contents)
}
}