Skip to main content

kafrust_protocol/api/
alter_user_scram_credentials.rs

1use crate::codec::{Decoder, Encoder};
2use crate::error::Result;
3use crate::header::RequestHeader;
4
5pub const API_KEY: i16 = 51;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct AlterUserScramCredentialsRequestV0 {
9    pub correlation_id: i32,
10    pub client_id: Option<String>,
11    pub deletions: Vec<AlterUserScramCredentialsDeletionV0>,
12    pub upsertions: Vec<AlterUserScramCredentialsUpsertionV0>,
13}
14
15impl AlterUserScramCredentialsRequestV0 {
16    pub fn encode(&self) -> Result<Vec<u8>> {
17        let mut encoder = Encoder::new();
18        RequestHeader {
19            api_key: API_KEY,
20            api_version: 0,
21            correlation_id: self.correlation_id,
22            client_id: self.client_id.clone(),
23        }
24        .encode_v2(&mut encoder)?;
25        encoder.write_compact_array(Some(&self.deletions), |encoder, deletion| {
26            encoder.write_compact_string(&deletion.name)?;
27            encoder.write_i8(deletion.mechanism);
28            encoder.write_empty_tagged_fields();
29            Ok(())
30        })?;
31        encoder.write_compact_array(Some(&self.upsertions), |encoder, upsertion| {
32            encoder.write_compact_string(&upsertion.name)?;
33            encoder.write_i8(upsertion.mechanism);
34            encoder.write_i32(upsertion.iterations);
35            encoder.write_compact_bytes(&upsertion.salt)?;
36            encoder.write_compact_bytes(&upsertion.salted_password)?;
37            encoder.write_empty_tagged_fields();
38            Ok(())
39        })?;
40        encoder.write_empty_tagged_fields();
41        Ok(encoder.into_bytes())
42    }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct AlterUserScramCredentialsDeletionV0 {
47    pub name: String,
48    pub mechanism: i8,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct AlterUserScramCredentialsUpsertionV0 {
53    pub name: String,
54    pub mechanism: i8,
55    pub iterations: i32,
56    pub salt: Vec<u8>,
57    pub salted_password: Vec<u8>,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct AlterUserScramCredentialsResponseV0 {
62    pub throttle_time_ms: i32,
63    pub results: Vec<AlterUserScramCredentialsResultV0>,
64}
65
66impl AlterUserScramCredentialsResponseV0 {
67    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
68        let throttle_time_ms = decoder.read_i32()?;
69        let results = decoder
70            .read_compact_array(
71                "alter user SCRAM credential results",
72                AlterUserScramCredentialsResultV0::decode,
73            )?
74            .unwrap_or_default();
75        decoder.read_tagged_fields()?;
76        Ok(Self {
77            throttle_time_ms,
78            results,
79        })
80    }
81}
82
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct AlterUserScramCredentialsResultV0 {
85    pub user: String,
86    pub error_code: i16,
87    pub error_message: Option<String>,
88}
89
90impl AlterUserScramCredentialsResultV0 {
91    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
92        let user = decoder.read_compact_string()?;
93        let error_code = decoder.read_i16()?;
94        let error_message = decoder.read_compact_nullable_string()?;
95        decoder.read_tagged_fields()?;
96        Ok(Self {
97            user,
98            error_code,
99            error_message,
100        })
101    }
102}
103
104#[cfg(test)]
105#[allow(clippy::unwrap_used)]
106mod tests {
107    use super::{
108        AlterUserScramCredentialsDeletionV0, AlterUserScramCredentialsRequestV0,
109        AlterUserScramCredentialsResponseV0, AlterUserScramCredentialsUpsertionV0, API_KEY,
110    };
111    use crate::codec::{Decoder, Encoder};
112
113    #[test]
114    fn encodes_alter_user_scram_credentials_v0_request() {
115        let request = AlterUserScramCredentialsRequestV0 {
116            correlation_id: 29,
117            client_id: None,
118            deletions: vec![AlterUserScramCredentialsDeletionV0 {
119                name: "alice".to_owned(),
120                mechanism: 1,
121            }],
122            upsertions: vec![AlterUserScramCredentialsUpsertionV0 {
123                name: "bob".to_owned(),
124                mechanism: 2,
125                iterations: 4096,
126                salt: vec![1, 2, 3],
127                salted_password: vec![4, 5, 6],
128            }],
129        };
130
131        let bytes = request.encode().unwrap();
132        assert_eq!(&bytes[0..4], &[0, API_KEY as u8, 0, 0]);
133        assert_eq!(&bytes[4..8], &[0, 0, 0, 29]);
134        assert_eq!(bytes[bytes.len() - 2..], [0, 0]);
135    }
136
137    #[test]
138    fn decodes_alter_user_scram_credentials_v0_response() {
139        let mut bytes = Encoder::new();
140        bytes.write_i32(4);
141        bytes.write_unsigned_varint(2); // one result
142        bytes.write_compact_string("alice").unwrap();
143        bytes.write_i16(31);
144        bytes.write_compact_nullable_string(Some("denied")).unwrap();
145        bytes.write_empty_tagged_fields();
146        bytes.write_empty_tagged_fields();
147        let bytes = bytes.into_bytes();
148        let mut decoder = Decoder::new(&bytes);
149
150        let response = AlterUserScramCredentialsResponseV0::decode_body(&mut decoder).unwrap();
151
152        assert_eq!(response.throttle_time_ms, 4);
153        assert_eq!(response.results[0].user, "alice");
154        assert_eq!(response.results[0].error_code, 31);
155        assert_eq!(response.results[0].error_message.as_deref(), Some("denied"));
156        assert!(decoder.is_empty());
157    }
158}