use std::sync::Arc;
use super::{
ascii_sanitize, garnet_acl_authenticator::GarnetAclAuthenticator,
i_garnet_authenticator::IGarnetAuthenticator,
};
use crate::acl::{AccessControlList, AclPassword, user_handle::UserHandle};
pub struct GarnetAclWithPasswordAuthenticator {
pub base: GarnetAclAuthenticator,
}
impl GarnetAclWithPasswordAuthenticator {
pub fn new(acl: Arc<AccessControlList>) -> Self {
Self {
base: GarnetAclAuthenticator::new(acl),
}
}
fn authenticate_internal(
user_handle: &Arc<UserHandle>,
_username: &[u8],
password: &[u8],
) -> bool {
let password_hash = AclPassword::from_string(&ascii_sanitize(password));
let user = user_handle.user();
user.is_enabled() && user.validate_password(&password_hash)
}
}
impl IGarnetAuthenticator for GarnetAclWithPasswordAuthenticator {
fn is_authenticated(&self) -> bool {
self.base.is_authenticated()
}
fn can_authenticate(&self) -> bool {
true
}
fn has_acl_support(&self) -> bool {
true
}
fn authenticate(&mut self, password: &[u8], username: &[u8]) -> bool {
self
.base
.authenticate(username, password, &mut |handle, username, password| {
Self::authenticate_internal(handle, username, password)
})
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::{
acl::{AccessControlList, AclParser, AclPassword},
auth::ascii_sanitize,
types::RespCommand,
};
#[test]
fn default_user_passwordless_auth() {
let acl = Arc::new(AccessControlList::new("", None).unwrap());
let mut auth = GarnetAclWithPasswordAuthenticator::new(Arc::clone(&acl));
assert!(auth.authenticate(b"whatever", b""));
let handle = auth.base.get_user_handle().unwrap();
assert_eq!(handle.user().name, "default");
let mut auth2 = GarnetAclWithPasswordAuthenticator::new(acl);
assert!(auth2.authenticate(b"whatever", b"default"));
assert!(auth2.base.is_authenticated());
}
#[test]
fn disabled_user_rejected() {
let acl = Arc::new(AccessControlList::new("", None).unwrap());
AclParser::parse_acl_rule("user alice >pw +@all", Some(&acl)).unwrap();
let mut auth = GarnetAclWithPasswordAuthenticator::new(Arc::clone(&acl));
assert!(!auth.authenticate(b"pw", b"alice"));
assert!(auth.base.get_user_handle().is_none());
AclParser::parse_acl_rule("user alice on", Some(&acl)).unwrap();
assert!(auth.authenticate(b"pw", b"alice"));
}
#[test]
fn wrong_password_or_unknown_user() {
let acl = Arc::new(AccessControlList::new("", None).unwrap());
AclParser::parse_acl_rule("user alice on >pw", Some(&acl)).unwrap();
let mut auth = GarnetAclWithPasswordAuthenticator::new(acl);
assert!(!auth.authenticate(b"wrong", b"alice"));
assert!(!auth.authenticate(b"pw", b"nobody"));
assert!(!auth.authenticate(b"pw", b"\xff\xfe"));
}
#[test]
fn hash_password_auth() {
let acl = Arc::new(AccessControlList::new("", None).unwrap());
let rule = format!(
"user bob on #{} >plain",
"8f0e2f76e22b43e2855189877e7dc1e1e7d98c226c95db247cd1d547928334a9"
);
AclParser::parse_acl_rule(&rule, Some(&acl)).unwrap();
let mut auth = GarnetAclWithPasswordAuthenticator::new(Arc::clone(&acl));
assert!(auth.authenticate(b"passw0rd", b"bob"));
assert!(auth.authenticate(b"plain", b"bob"));
assert!(!auth.authenticate(b"nope", b"bob"));
}
#[test]
fn acl_and_permissions_orthogonal() {
let acl = Arc::new(AccessControlList::new("", None).unwrap());
AclParser::parse_acl_rule("user carol on >pw +get", Some(&acl)).unwrap();
let mut auth = GarnetAclWithPasswordAuthenticator::new(Arc::clone(&acl));
assert!(auth.authenticate(b"pw", b"carol"));
let user = auth.base.get_user_handle().unwrap().user();
assert!(user.can_access_command(RespCommand::Get));
assert!(!user.can_access_command(RespCommand::Set));
}
#[test]
fn sanitize_non_ascii_bytes_like_csharp() {
let hashed_question = AclPassword::from_string("?");
let a = AclPassword::from_string(&ascii_sanitize(&[0xC3, 0xA9]));
assert_eq!(a, AclPassword::from_string("??"));
assert_eq!(hashed_question, AclPassword::from_string("?"));
let _ = a;
}
}