use std::{fmt, str::FromStr};
use base64::{Engine, prelude::BASE64_STANDARD};
use crate::error::AuthBasicError;
#[derive(PartialEq)]
pub struct Credentials {
pub user_id: String,
pub password: String,
}
impl Credentials {
pub fn new(user_id: &str, password: &str) -> Self {
Self {
user_id: user_id.to_string(),
password: password.to_string(),
}
}
pub fn decode(auth_header_value: String) -> Result<Self, AuthBasicError> {
let decoded = BASE64_STANDARD.decode(auth_header_value)?;
let as_utf8 = String::from_utf8(decoded)?;
if let Some((user_id, password)) = as_utf8.split_once(':') {
return Ok(Self::new(user_id, password));
}
Err(AuthBasicError::InvalidAuthorizationHeader)
}
pub fn encode(&self) -> String {
let credentials = format!("{}:{}", self.user_id, self.password);
BASE64_STANDARD.encode(credentials.as_bytes())
}
pub fn from_header(auth_header: String) -> Result<Credentials, AuthBasicError> {
if let Some((auth_type, encoded_credentials)) = auth_header.split_once(' ') {
if encoded_credentials.contains(' ') {
return Err(AuthBasicError::InvalidAuthorizationHeader);
}
if auth_type.to_lowercase() != "basic" {
return Err(AuthBasicError::InvalidScheme(auth_type.to_string()));
}
let credentials = Credentials::decode(encoded_credentials.to_string())?;
return Ok(credentials);
}
Err(AuthBasicError::InvalidAuthorizationHeader)
}
pub fn as_http_header(&self) -> String {
let as_base64 = self.encode();
format!("Basic {}", as_base64)
}
}
impl FromStr for Credentials {
type Err = AuthBasicError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.contains(' ') {
return Self::from_header(s.into());
}
Self::decode(s.into())
}
}
impl fmt::Debug for Credentials {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Credentials")
.field("user_id", &self.user_id)
.field("password", &"REDACTED")
.finish()
}
}