1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//! Abstraction of AUTH command.
//!
//! For general information about this command, see the [Redis documentation](<https://redis.io/commands/auth/>).
//!
//! *Authentication is done automatically by [ConnectionHandler](crate::network::ConnectionHandler), so there is usually no need for manual execution.*
//!
//! # Password-only
//! ```
//!# use core::str::FromStr;
//!# use std::str::Bytes;
//!# use embedded_nal::SocketAddr;
//!# use std_embedded_nal::Stack;
//!# use std_embedded_time::StandardClock;
//!# use embedded_redis::commands::auth::AuthCommand;
//!# use embedded_redis::network::{ConnectionHandler, Credentials};
//!#
//! let mut stack = Stack::default();
//! let clock = StandardClock::default();
//!
//! let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
//! let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
//!
//! // Cast from Credentials
//! let command = AuthCommand::from(&Credentials::password_only("secret123!"));
//! let _ = client.send(command);
//!
//! // Directly creating Auth command:
//! let command = AuthCommand::new(None as Option<&str>, "secret123!");
//! let _ = client.send(command);
//! ```
//! # Username/Password (ACL based authentication)
//! *Requires Redis version > 6.0 + serverside ACL configuration*
//! ```
//!# use core::str::FromStr;
//!# use embedded_nal::SocketAddr;
//!# use std_embedded_nal::Stack;
//!# use std_embedded_time::StandardClock;
//!# use embedded_redis::commands::auth::AuthCommand;
//!# use embedded_redis::network::{ConnectionHandler, Credentials};
//!#
//! let mut stack = Stack::default();
//! let clock = StandardClock::default();
//!
//! let mut connection_handler = ConnectionHandler::resp3(SocketAddr::from_str("127.0.0.1:6379").unwrap());
//! let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
//!
//! // Cast from Credentials
//! let command = AuthCommand::from(&Credentials::acl("user01", "secret123!"));
//! let _ = client.send(command);
//!
//! // Directly creating Auth command:
//! let command = AuthCommand::new(Some("user01"), "secret123!");
//! let _ = client.send(command);
//! ```
//! # Error handling
//! Successful execution is terminated by returning `Ok(())` response.
//!
//! Authentication errors are normally signalled by Redis with an error response, which is mapped
//! to [CommandErrors::ErrorResponse](crate::network::CommandErrors::ErrorResponse).
//! ```
//!# use core::str::FromStr;
//!# use embedded_nal::SocketAddr;
//!# use std_embedded_nal::Stack;
//!# use std_embedded_time::StandardClock;
//!# use embedded_redis::commands::auth::AuthCommand;
//!# use embedded_redis::network::CommandErrors;
//!# use embedded_redis::network::{ConnectionHandler, Credentials};
//!#
//!# let mut stack = Stack::default();
//!# let clock = StandardClock::default();
//!#
//!# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
//!# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
//!#
//!# let error_string = "ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?".to_string();
//! let command = AuthCommand::from(&Credentials::password_only("wrong_password"));
//! let result = client.send(command).unwrap().wait().unwrap_err();
//! assert_eq!(CommandErrors::ErrorResponse(error_string), result);
//! ```
use crate::commands::builder::{CommandBuilder, ToStringOption};
use crate::commands::{Command, ResponseTypeError};
use crate::network::handler::Credentials;
use bytes::Bytes;
pub struct AuthCommand {
/// Optionally sets a username for ACL based authentication, which requires
/// Redis version >= 6 + ACL enabled
username: Option<Bytes>,
password: Bytes,
}
impl AuthCommand {
pub fn new<U, P>(username: Option<U>, password: P) -> Self
where
U: Into<Bytes>,
P: Into<Bytes>,
{
let mut user_bytes = None;
if let Some(bytes) = username {
user_bytes = Some(bytes.into());
}
AuthCommand {
username: user_bytes,
password: password.into(),
}
}
}
impl<F> Command<F> for AuthCommand
where
F: ToStringOption + From<CommandBuilder>,
{
type Response = ();
fn encode(&self) -> F {
CommandBuilder::new("AUTH")
.arg_option(self.username.as_ref())
.arg(&self.password)
.into()
}
fn eval_response(&self, frame: F) -> Result<Self::Response, ResponseTypeError> {
if frame.to_string_option().ok_or(ResponseTypeError {})? != "OK" {
return Err(ResponseTypeError {});
}
Ok(())
}
}
impl From<&Credentials> for AuthCommand {
fn from(credentials: &Credentials) -> AuthCommand {
AuthCommand::new(credentials.username.clone(), credentials.password.clone())
}
}