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}