use serde::Deserialize;
use subtle::ConstantTimeEq;
#[derive(Debug, Deserialize)]
pub struct AuthFrame {
#[serde(rename = "type")]
pub kind: String,
pub key: String,
}
impl AuthFrame {
pub fn from_json(bytes: &[u8]) -> Option<Self> {
let frame: AuthFrame = serde_json::from_slice(bytes).ok()?;
if frame.kind != "auth" {
return None;
}
Some(frame)
}
}
pub fn key_matches(presented: &str, expected: &str) -> bool {
presented.as_bytes().ct_eq(expected.as_bytes()).into()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_auth_frame() {
let frame = AuthFrame::from_json(br#"{"type":"auth","key":"secret"}"#).unwrap();
assert_eq!(frame.key, "secret");
}
#[test]
fn rejects_non_auth_type() {
let frame = AuthFrame::from_json(br#"{"type":"request","messages":[]}"#);
assert!(frame.is_none());
}
#[test]
fn rejects_garbage() {
assert!(AuthFrame::from_json(b"not json").is_none());
}
#[test]
fn key_matches_equal() {
assert!(key_matches("hello", "hello"));
}
#[test]
fn key_matches_different() {
assert!(!key_matches("hello", "helLo"));
assert!(!key_matches("short", "longer-string"));
}
}