use crate::acl::constant_equals;
pub trait IGarnetAuthenticator {
fn is_authenticated(&self) -> bool;
fn can_authenticate(&self) -> bool;
fn has_acl_support(&self) -> bool;
fn authenticate(&mut self, password: &[u8], username: &[u8]) -> bool;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct GarnetNoAuthAuthenticator;
impl IGarnetAuthenticator for GarnetNoAuthAuthenticator {
#[inline]
fn is_authenticated(&self) -> bool {
true
}
#[inline]
fn can_authenticate(&self) -> bool {
false
}
#[inline]
fn has_acl_support(&self) -> bool {
false
}
#[inline]
fn authenticate(&mut self, _password: &[u8], _username: &[u8]) -> bool {
false
}
}
#[derive(Debug, Clone)]
pub struct GarnetPasswordAuthenticator {
password: Vec<u8>,
authenticated: bool,
}
impl GarnetPasswordAuthenticator {
pub fn new(password: Vec<u8>) -> Self {
Self {
password,
authenticated: false,
}
}
}
impl IGarnetAuthenticator for GarnetPasswordAuthenticator {
#[inline]
fn is_authenticated(&self) -> bool {
self.authenticated
}
#[inline]
fn can_authenticate(&self) -> bool {
true
}
#[inline]
fn has_acl_support(&self) -> bool {
false
}
fn authenticate(&mut self, password: &[u8], _username: &[u8]) -> bool {
self.authenticated = constant_equals(&self.password, password);
self.authenticated
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn password_authenticator_constant_time_match() {
let mut auth = GarnetPasswordAuthenticator::new(b"secret".to_vec());
assert!(!auth.is_authenticated());
assert!(auth.can_authenticate());
assert!(!auth.has_acl_support());
assert!(!auth.authenticate(b"secrat", b""));
assert!(!auth.is_authenticated());
assert!(auth.authenticate(b"secret", b""));
assert!(auth.is_authenticated());
assert!(!auth.authenticate(b"secret longer", b""));
}
#[test]
fn no_auth_authenticator_never_authenticates() {
let mut auth = GarnetNoAuthAuthenticator;
assert!(auth.is_authenticated());
assert!(!auth.can_authenticate());
assert!(!auth.authenticate(b"anything", b""));
}
}