use crate::codec::{Decoder, Encoder};
use crate::error::Result;
use crate::header::RequestHeader;
pub const SASL_HANDSHAKE_API_KEY: i16 = 17;
pub const SASL_AUTHENTICATE_API_KEY: i16 = 36;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslHandshakeRequestV1 {
pub correlation_id: i32,
pub client_id: Option<String>,
pub mechanism: String,
}
impl SaslHandshakeRequestV1 {
pub fn encode(&self) -> Result<Vec<u8>> {
let mut encoder = Encoder::new();
RequestHeader {
api_key: SASL_HANDSHAKE_API_KEY,
api_version: 1,
correlation_id: self.correlation_id,
client_id: self.client_id.clone(),
}
.encode_v1(&mut encoder)?;
encoder.write_string(&self.mechanism)?;
Ok(encoder.into_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslHandshakeResponseV1 {
pub error_code: i16,
pub mechanisms: Vec<String>,
}
impl SaslHandshakeResponseV1 {
pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
let error_code = decoder.read_i16()?;
let mechanisms = decoder
.read_array("SASL mechanisms", |decoder| decoder.read_string())?
.unwrap_or_default();
Ok(Self {
error_code,
mechanisms,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateRequestV0 {
pub correlation_id: i32,
pub client_id: Option<String>,
pub auth_bytes: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateRequestV1 {
pub correlation_id: i32,
pub client_id: Option<String>,
pub auth_bytes: Vec<u8>,
}
impl SaslAuthenticateRequestV1 {
pub fn encode(&self) -> Result<Vec<u8>> {
let mut encoder = Encoder::new();
RequestHeader {
api_key: SASL_AUTHENTICATE_API_KEY,
api_version: 1,
correlation_id: self.correlation_id,
client_id: self.client_id.clone(),
}
.encode_v1(&mut encoder)?;
encoder.write_bytes(&self.auth_bytes)?;
Ok(encoder.into_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateRequestV2 {
pub correlation_id: i32,
pub client_id: Option<String>,
pub auth_bytes: Vec<u8>,
}
impl SaslAuthenticateRequestV2 {
pub fn encode(&self) -> Result<Vec<u8>> {
let mut encoder = Encoder::new();
RequestHeader {
api_key: SASL_AUTHENTICATE_API_KEY,
api_version: 2,
correlation_id: self.correlation_id,
client_id: self.client_id.clone(),
}
.encode_v2(&mut encoder)?;
encoder.write_compact_bytes(&self.auth_bytes)?;
encoder.write_empty_tagged_fields();
Ok(encoder.into_bytes())
}
}
impl SaslAuthenticateRequestV0 {
pub fn encode(&self) -> Result<Vec<u8>> {
let mut encoder = Encoder::new();
RequestHeader {
api_key: SASL_AUTHENTICATE_API_KEY,
api_version: 0,
correlation_id: self.correlation_id,
client_id: self.client_id.clone(),
}
.encode_v1(&mut encoder)?;
encoder.write_bytes(&self.auth_bytes)?;
Ok(encoder.into_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateResponseV0 {
pub error_code: i16,
pub error_message: Option<String>,
pub auth_bytes: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateResponseV1 {
pub error_code: i16,
pub error_message: Option<String>,
pub auth_bytes: Vec<u8>,
pub session_lifetime_ms: i64,
}
impl SaslAuthenticateResponseV1 {
pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
Ok(Self {
error_code: decoder.read_i16()?,
error_message: decoder.read_nullable_string()?,
auth_bytes: decoder.read_bytes()?,
session_lifetime_ms: decoder.read_i64()?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaslAuthenticateResponseV2 {
pub error_code: i16,
pub error_message: Option<String>,
pub auth_bytes: Vec<u8>,
pub session_lifetime_ms: i64,
}
impl SaslAuthenticateResponseV2 {
pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
let response = Self {
error_code: decoder.read_i16()?,
error_message: decoder.read_compact_nullable_string()?,
auth_bytes: decoder.read_compact_bytes()?,
session_lifetime_ms: decoder.read_i64()?,
};
decoder.read_tagged_fields()?;
Ok(response)
}
}
impl SaslAuthenticateResponseV0 {
pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
Ok(Self {
error_code: decoder.read_i16()?,
error_message: decoder.read_nullable_string()?,
auth_bytes: decoder.read_bytes()?,
})
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::{
SaslAuthenticateRequestV0, SaslAuthenticateRequestV1, SaslAuthenticateRequestV2,
SaslAuthenticateResponseV0, SaslAuthenticateResponseV1, SaslAuthenticateResponseV2,
SaslHandshakeRequestV1, SaslHandshakeResponseV1, SASL_AUTHENTICATE_API_KEY,
SASL_HANDSHAKE_API_KEY,
};
use crate::codec::Decoder;
#[test]
fn encodes_sasl_handshake_v1_request() {
let request = SaslHandshakeRequestV1 {
correlation_id: 7,
client_id: Some("kafrust".to_owned()),
mechanism: "PLAIN".to_owned(),
};
assert_eq!(
request.encode().unwrap(),
[
0, 17, 0, 1, 0, 0, 0, 7, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 5, b'P', b'L', b'A', b'I', b'N', ]
);
assert_eq!(SASL_HANDSHAKE_API_KEY, 17);
}
#[test]
fn decodes_sasl_handshake_v1_response() {
let bytes = [
0, 0, 0, 0, 0, 2, 0, 5, b'P', b'L', b'A', b'I', b'N', 0, 13, b'S', b'C', b'R', b'A', b'M', b'-', b'S', b'H', b'A', b'-', b'2', b'5',
b'6', ];
let mut decoder = Decoder::new(&bytes);
let response = SaslHandshakeResponseV1::decode_body(&mut decoder).unwrap();
assert_eq!(response.error_code, 0);
assert_eq!(response.mechanisms, ["PLAIN", "SCRAM-SHA-256"]);
assert!(decoder.is_empty());
}
#[test]
fn encodes_sasl_authenticate_v0_request() {
let request = SaslAuthenticateRequestV0 {
correlation_id: 8,
client_id: Some("kafrust".to_owned()),
auth_bytes: b"\0user\0pass".to_vec(),
};
assert_eq!(
request.encode().unwrap(),
[
0, 36, 0, 0, 0, 0, 0, 8, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 0, 0, 10, 0, b'u', b's', b'e', b'r', 0, b'p', b'a', b's', b's',
]
);
assert_eq!(SASL_AUTHENTICATE_API_KEY, 36);
}
#[test]
fn decodes_sasl_authenticate_v0_response() {
let bytes = [
0, 0, 0xff, 0xff, 0, 0, 0, 2, 1, 2, ];
let mut decoder = Decoder::new(&bytes);
let response = SaslAuthenticateResponseV0::decode_body(&mut decoder).unwrap();
assert_eq!(response.error_code, 0);
assert_eq!(response.error_message, None);
assert_eq!(response.auth_bytes, [1, 2]);
assert!(decoder.is_empty());
}
#[test]
fn encodes_sasl_authenticate_v1_request() {
let request = SaslAuthenticateRequestV1 {
correlation_id: 9,
client_id: Some("kafrust".to_owned()),
auth_bytes: b"n,,\x01auth=token\x01\x01".to_vec(),
};
assert_eq!(
request.encode().unwrap(),
[
0, 36, 0, 1, 0, 0, 0, 9, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 0, 0, 16, b'n', b',', b',', 1, b'a', b'u', b't', b'h', b'=', b't', b'o', b'k', b'e', b'n', 1,
1,
]
);
}
#[test]
fn encodes_sasl_authenticate_v2_request() {
let request = SaslAuthenticateRequestV2 {
correlation_id: 7,
client_id: Some("kafrust".to_owned()),
auth_bytes: vec![1, 2],
};
assert_eq!(
request.encode().unwrap(),
[
0, 36, 0, 2, 0, 0, 0, 7, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 3, 1, 2, 0, ]
);
}
#[test]
fn decodes_sasl_authenticate_v1_response() {
let bytes = [
0, 0, 0xff, 0xff, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 42, ];
let mut decoder = Decoder::new(&bytes);
let response = SaslAuthenticateResponseV1::decode_body(&mut decoder).unwrap();
assert_eq!(response.error_code, 0);
assert_eq!(response.error_message, None);
assert_eq!(response.auth_bytes, [1, 2]);
assert_eq!(response.session_lifetime_ms, 42);
assert!(decoder.is_empty());
}
#[test]
fn decodes_sasl_authenticate_v2_response() {
let bytes = [
0, 0, 0, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 42, 0, ];
let mut decoder = Decoder::new(&bytes);
let response = SaslAuthenticateResponseV2::decode_body(&mut decoder).unwrap();
assert_eq!(response.error_code, 0);
assert_eq!(response.error_message, None);
assert_eq!(response.auth_bytes, [1, 2]);
assert_eq!(response.session_lifetime_ms, 42);
assert!(decoder.is_empty());
}
}