use crate::context::SaTokenContext;
use crate::error::{SaTokenError, SaTokenResult};
use crate::util::StpUtil;
pub const DEFAULT_REALM: &str = "sa-token";
pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
diff |= x ^ y;
}
diff == 0
}
pub fn decode_basic_authorization(header: &str) -> Option<String> {
let rest = header
.strip_prefix("Basic ")
.or_else(|| header.strip_prefix("basic "))?;
let rest = rest.trim();
if rest.is_empty() {
return None;
}
use base64::Engine;
let bytes = base64::engine::general_purpose::STANDARD
.decode(rest.as_bytes())
.ok()?;
String::from_utf8(bytes).ok()
}
pub fn check(realm: &str, account: &str) -> SaTokenResult<()> {
let expected = if account.is_empty() {
StpUtil::try_get_config()
.map(|c| c.http_basic.clone())
.unwrap_or_default()
} else {
account.to_string()
};
if expected.is_empty() {
return Err(SaTokenError::BasicAuthFailed {
realm: realm.to_string(),
});
}
let header = SaTokenContext::try_current()
.and_then(|ctx| ctx.auth_meta().authorization)
.ok_or_else(|| SaTokenError::BasicAuthFailed {
realm: realm.to_string(),
})?;
let decoded =
decode_basic_authorization(&header).ok_or_else(|| SaTokenError::BasicAuthFailed {
realm: realm.to_string(),
})?;
if !ct_eq(decoded.as_bytes(), expected.as_bytes()) {
return Err(SaTokenError::BasicAuthFailed {
realm: realm.to_string(),
});
}
Ok(())
}
pub fn check_account(account: &str) -> SaTokenResult<()> {
check(DEFAULT_REALM, account)
}