///|
/// Initial-response SASL credential. Credentials deliberately have no Debug impl.
pub struct Authentication {
priv mechanism : String
priv response : Bytes?
}
///|
fn check_mechanism(mechanism : String) -> Unit raise FrameError {
if mechanism.is_empty() ||
mechanism.length() > 255 ||
mechanism.iter().any(c => c.to_int() < 33 || c.to_int() > 126) {
raise Invalid("invalid SASL mechanism")
}
}
///|
/// An initial response, preserved byte-for-byte. No Unicode normalization.
pub fn Authentication::custom(
mechanism : String,
response : Bytes,
) -> Authentication raise FrameError {
check_mechanism(mechanism)
if response.length() > 1048576 {
raise Invalid("SASL response exceeds 1 MiB")
}
{ mechanism, response: Some(response), }
}
///|
/// Ask the host for an initial response only if this candidate is selected.
pub fn Authentication::deferred(
mechanism : String,
) -> Authentication raise FrameError {
check_mechanism(mechanism)
{ mechanism, response: None, }
}
///|
pub fn Authentication::plain(
username : String,
password : String,
) -> Authentication raise FrameError {
if username.contains("\u0000") || password.contains("\u0000") {
raise Invalid("invalid PLAIN credentials")
}
Authentication::custom(
"PLAIN",
@utf8.encode("\u0000" + username + "\u0000" + password),
)
}
///|
pub fn Authentication::amqplain(
username : String,
password : String,
) -> Authentication raise FrameError {
let table = encode_table([
("LOGIN", LongString(@utf8.encode(username))),
("PASSWORD", LongString(@utf8.encode(password))),
])
Authentication::custom("AMQPLAIN", table[4:].to_owned())
}
///|
/// RabbitMQ EXTERNAL response used by the pinned amqp091-go implementation.
pub fn Authentication::external() -> Authentication {
{ mechanism: "EXTERNAL", response: Some(b"\x00*\x00*"), }
}
///|
pub fn Authentication::mechanism(self : Authentication) -> String {
self.mechanism
}
///|
fn Session::auth_response(
self : Session,
response : Bytes,
) -> Unit raise FrameError {
if response.length() > 1048576 {
raise Invalid("SASL response exceeds 1 MiB")
}
self.emit(
"connection.start-ok",
[
Table(self.client_properties()),
ShortString(self.selected_auth),
LongString(response),
ShortString(self.locale),
],
0,
)
self.state = "tune"
}
///|
/// Supply a deferred initial response. This is not a connection.secure challenge.
pub fn Session::respond_authentication(
self : Session,
response : Bytes,
) -> Unit raise FrameError {
if self.state != "authenticate" {
raise Invalid("no initial SASL response is pending")
}
self.auth_response(response)
}
///|
pub fn Session::authentication_mechanism(self : Session) -> String {
self.selected_auth
}