use base64::Engine;
use base64::engine::general_purpose::STANDARD;
#[must_use]
pub fn plain_response(username: &str, password: &str) -> String {
let auth_string = format!("\0{username}\0{password}");
STANDARD.encode(auth_string.as_bytes())
}
#[must_use]
pub fn oauthbearer_response(user: &str, token: &str) -> String {
let auth_string = format!("n,a={user},\x01auth=Bearer {token}\x01\x01");
STANDARD.encode(auth_string.as_bytes())
}
#[must_use]
pub fn xoauth2_response(user: &str, token: &str) -> String {
let auth_string = format!("user={user}\x01auth=Bearer {token}\x01\x01");
STANDARD.encode(auth_string.as_bytes())
}
pub fn parse_oauth_error(response: &str) -> Result<OAuthError, serde_json::Error> {
serde_json::from_str(response)
}
#[derive(Debug, Clone, serde::Deserialize)]
pub struct OAuthError {
pub status: String,
pub schemes: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::redundant_clone, clippy::manual_string_new, clippy::needless_collect, clippy::unreadable_literal, clippy::used_underscore_items, clippy::similar_names)]
mod tests {
use super::*;
#[test]
fn test_oauthbearer_response() {
let response = oauthbearer_response("user@example.com", "token123");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert!(decoded_str.starts_with("n,a=user@example.com"));
assert!(decoded_str.contains("auth=Bearer token123"));
assert!(decoded_str.ends_with("\x01\x01"));
}
#[test]
fn test_xoauth2_response() {
let response = xoauth2_response("user@example.com", "token123");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert!(decoded_str.starts_with("user=user@example.com"));
assert!(decoded_str.contains("auth=Bearer token123"));
assert!(decoded_str.ends_with("\x01\x01"));
}
#[test]
fn test_oauthbearer_format() {
let response = oauthbearer_response("test@test.com", "abc");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert_eq!(decoded_str, "n,a=test@test.com,\x01auth=Bearer abc\x01\x01");
}
#[test]
fn test_xoauth2_format() {
let response = xoauth2_response("test@test.com", "abc");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert_eq!(decoded_str, "user=test@test.com\x01auth=Bearer abc\x01\x01");
}
#[test]
fn test_parse_oauth_error() {
let json = r#"{"status":"401","schemes":"bearer","scope":"https://mail.google.com/"}"#;
let error = parse_oauth_error(json).unwrap();
assert_eq!(error.status, "401");
assert_eq!(error.schemes, "bearer");
assert_eq!(error.scope.as_deref(), Some("https://mail.google.com/"));
}
#[test]
fn test_responses_are_base64() {
let response = oauthbearer_response("user@example.com", "token");
assert!(!response.contains("user@example.com"));
assert!(!response.contains("token"));
assert!(STANDARD.decode(&response).is_ok());
}
#[test]
fn test_plain_response() {
let response = plain_response("user@example.com", "password123");
let decoded = STANDARD.decode(&response).unwrap();
assert_eq!(decoded[0], 0); assert!(decoded.contains(&b'@'));
let second_nul = decoded.iter().skip(1).position(|&b| b == 0).unwrap() + 1;
assert!(second_nul > 1);
}
#[test]
fn test_plain_response_format() {
let response = plain_response("test", "pass");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert_eq!(decoded_str, "\0test\0pass");
}
#[test]
fn test_plain_response_special_chars() {
let response = plain_response("user", "pass@word!");
let decoded = STANDARD.decode(&response).unwrap();
let decoded_str = String::from_utf8(decoded).unwrap();
assert_eq!(decoded_str, "\0user\0pass@word!");
}
}