fe2o3_amqp_types/sasl/
mod.rs

1//! Types defined in AMQP 1.0 specification Part 5.3: SASL
2
3use serde_amqp::{
4    primitives::{Array, Binary, Symbol},
5    DeserializeComposite, SerializeComposite,
6};
7use serde_repr::{Deserialize_repr, Serialize_repr};
8
9mod mechanisms;
10
11/// 5.3.3.1 SASL Mechanisms
12/// Advertise available sasl mechanisms.
13/// <type name="sasl-mechanisms" class="composite" source="list" provides="sasl-frame">
14///     <descriptor name="amqp:sasl-mechanisms:list" code="0x00000000:0x00000040"/>
15///     <field name="sasl-server-mechanisms" type="symbol" multiple="true" mandatory="true"/>
16/// </type>
17/// Advertises the available SASL mechanisms that can be used for authentication.
18///
19/// NOTE: Serialize and Deserialize are manually implemented because
20/// > A field which is defined as both multiple and mandatory MUST contain at least one value
21/// > (i.e. for such a field both null and an array with no entries are invalid).
22#[derive(Debug, Clone)]
23pub struct SaslMechanisms {
24    /// sasl-server-mechanisms supported sasl mechanisms
25    ///
26    /// A list of the sasl security mechanisms supported by the sending peer. It is invalid for
27    /// this list to be null or empty. If the sending peer does not require its partner to
28    /// authenticate with it, then it SHOULD send a list of one element with its value as the
29    /// SASL mechanism ANONYMOUS. The server mechanisms are ordered in decreasing level of
30    /// preference.
31    pub sasl_server_mechanisms: Array<Symbol>,
32}
33
34/// 5.3.3.2 SASL Init
35/// Initiate sasl exchange.
36/// <type name="sasl-init" class="composite" source="list" provides="sasl-frame">
37///     <descriptor name="amqp:sasl-init:list" code="0x00000000:0x00000041"/>
38///     <field name="mechanism" type="symbol" mandatory="true"/>
39///     <field name="initial-response" type="binary"/>
40///     <field name="hostname" type="string"/>
41/// </type>
42/// Selects the sasl mechanism and provides the initial response if needed.
43#[derive(Debug, Clone, SerializeComposite, DeserializeComposite)]
44#[amqp_contract(
45    name = "amqp:sasl-init:list",
46    code = "0x0000_0000:0x0000_0041",
47    encoding = "list",
48    rename_all = "kebab-case"
49)]
50pub struct SaslInit {
51    /// selected security mechanism
52    ///
53    /// The name of the SASL mechanism used for the SASL exchange. If the selected mechanism is
54    /// not supported by the receiving peer, it MUST close the connection with the authentication-failure
55    /// close-code. Each peer MUST authenticate using the highest-level security profile it can handle
56    /// from the list provided by the partner
57    pub mechanism: Symbol,
58
59    /// security response data
60    ///
61    /// A block of opaque data passed to the security mechanism. The contents of this data are defined
62    /// by the SASL security mechanism.
63    pub initial_response: Option<Binary>,
64
65    /// the name of the target host
66    ///
67    /// The DNS name of the host (either fully qualified or relative) to which the sending peer is connecting.
68    /// It is not mandatory to provide the hostname. If no hostname is provided the receiving peer
69    /// SHOULD select a default based on its own configuration.
70    /// This field can be used by AMQP proxies to determine the correct back-end service to connect the
71    /// client to, and to determine the domain to validate the client’s credentials against.
72    /// This field might already have been specified by the server name indication extension as described
73    /// in RFC-4366 \[RFC4366\], if a TLS layer is used, in which case this field SHOULD either be null or
74    /// contain the same value. It is undefined what a different value to those already specified means.
75    pub hostname: Option<String>,
76}
77
78/// 5.3.3.3 SASL Challenge
79/// Security mechanism challenge.
80/// <type name="sasl-challenge" class="composite" source="list" provides="sasl-frame">
81///     <descriptor name="amqp:sasl-challenge:list" code="0x00000000:0x00000042"/>
82///     <field name="challenge" type="binary" mandatory="true"/>
83/// </type>
84/// Send the SASL challenge data as defined by the SASL specification.
85#[derive(Debug, Clone, SerializeComposite, DeserializeComposite)]
86#[amqp_contract(
87    name = "amqp:sasl-challenge:list",
88    code = "0x0000_0000:0x0000_0042",
89    encoding = "list",
90    rename_all = "kebab-case"
91)]
92pub struct SaslChallenge {
93    /// <field name="challenge" type="binary" mandatory="true"/>
94    pub challenge: Binary,
95}
96
97/// 5.3.3.4 SASL Response
98/// Security mechanism response.
99/// <type name="sasl-response" class="composite" source="list" provides="sasl-frame">
100///     <descriptor name="amqp:sasl-response:list" code="0x00000000:0x00000043"/>
101///     <field name="response" type="binary" mandatory="true"/>
102/// </type>
103/// Send the SASL response data as defined by the SASL specification.
104#[derive(Debug, Clone, SerializeComposite, DeserializeComposite)]
105#[amqp_contract(
106    name = "amqp:sasl-response:list",
107    code = "0x0000_0000:0x0000_0043",
108    encoding = "list",
109    rename_all = "kebab-case"
110)]
111pub struct SaslResponse {
112    /// <field name="response" type="binary" mandatory="true"/>
113    pub response: Binary,
114}
115
116/// 5.3.3.5 SASL Outcome
117/// Indicates the outcome of the sasl dialog.
118/// <type name="sasl-outcome" class="composite" source="list" provides="sasl-frame">
119///     <descriptor name="amqp:sasl-outcome:list" code="0x00000000:0x00000044"/>
120///     <field name="code" type="sasl-code" mandatory="true"/>
121///     <field name="additional-data" type="binary"/>
122/// </type>
123/// This frame indicates the outcome of the SASL dialog. Upon successful completion of the SASL
124/// dialog the security layer has been established, and the peers MUST exchange protocol headers
125/// to either start a nested security layer, or to establish the AMQP connection.
126#[derive(Debug, Clone, SerializeComposite, DeserializeComposite)]
127#[amqp_contract(
128    name = "amqp:sasl-outcome:list",
129    code = "0x0000_0000:0x0000_0044",
130    encoding = "list",
131    rename_all = "kebab-case"
132)]
133pub struct SaslOutcome {
134    /// <field name="code" type="sasl-code" mandatory="true"/>
135    pub code: SaslCode,
136    /// <field name="additional-data" type="binary"/>
137    pub additional_data: Option<Binary>,
138}
139
140/// 5.3.3.6 SASL Code
141/// Codes to indicate the outcome of the sasl dialog.
142/// <type name="sasl-code" class="restricted" source="ubyte">
143///     <choice name="ok" value="0"/>
144///     <choice name="auth" value="1"/>
145///     <choice name="sys" value="2"/>
146///     <choice name="sys-perm" value="3"/>
147///     <choice name="sys-temp" value="4"/>
148/// </type>
149#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
150#[repr(u8)]
151pub enum SaslCode {
152    /// 0 Connection authentication succeeded.
153    Ok = 0u8,
154    /// 1 Connection authentication failed due to an unspecified problem with the supplied
155    /// credentials.
156    Auth = 1,
157    /// 2 Connection authentication failed due to a system error.
158    Sys = 2,
159    /// 3 Connection authentication failed due to a system error that is unlikely to be corrected without intervention.
160    SysPerm = 3,
161    /// 4 Connection authentication failed due to a transient system error.
162    SysTemp = 4,
163}
164
165pub mod constant {
166    //! Constans definedin AMQP 1.0 specification Part 5.3.4
167
168    ///  major protocol version
169    pub const SASL_MAJOR: u8 = 1;
170
171    /// minor protocol version.
172    pub const SASL_MINOR: u8 = 0;
173
174    /// protocol revision.
175    pub const SASL_REVISION: u8 = 0;
176}
177
178#[cfg(test)]
179mod tests {
180    use serde_amqp::{format_code::EncodingCodes, from_slice, to_vec};
181
182    use super::SaslCode;
183
184    fn assert_eq_on_sasl_code_and_deserialized(code: SaslCode, buf: Vec<u8>) {
185        let deserialized: SaslCode = from_slice(&buf).unwrap();
186        assert_eq!(code, deserialized);
187    }
188
189    #[test]
190    fn test_serialize_deserialize_sasl_code() {
191        let code = SaslCode::Ok;
192        let buf = to_vec(&code).unwrap();
193        let expected = vec![EncodingCodes::Ubyte as u8, code.clone() as u8];
194        assert_eq!(&buf, &expected);
195        assert_eq_on_sasl_code_and_deserialized(code, expected);
196
197        let code = SaslCode::Auth;
198        let buf = to_vec(&code).unwrap();
199        let expected = vec![EncodingCodes::Ubyte as u8, code.clone() as u8];
200        assert_eq!(&buf, &expected);
201        assert_eq_on_sasl_code_and_deserialized(code, expected);
202
203        let code = SaslCode::Sys;
204        let buf = to_vec(&code).unwrap();
205        let expected = vec![EncodingCodes::Ubyte as u8, code.clone() as u8];
206        assert_eq!(&buf, &expected);
207        assert_eq_on_sasl_code_and_deserialized(code, expected);
208
209        let code = SaslCode::SysPerm;
210        let buf = to_vec(&code).unwrap();
211        let expected = vec![EncodingCodes::Ubyte as u8, code.clone() as u8];
212        assert_eq!(&buf, &expected);
213        assert_eq_on_sasl_code_and_deserialized(code, expected);
214
215        let code = SaslCode::SysTemp;
216        let buf = to_vec(&code).unwrap();
217        let expected = vec![EncodingCodes::Ubyte as u8, code.clone() as u8];
218        assert_eq!(&buf, &expected);
219        assert_eq_on_sasl_code_and_deserialized(code, expected);
220    }
221}