Line data Source code
1 : //! Payload for ad hoc authentication method for clients that don't support SNI.
2 : //! See the `impl` for [`super::backend::Backend<ClientCredentials>`].
3 : //! Read more: <https://github.com/neondatabase/cloud/issues/1620#issuecomment-1165332290>.
4 : //! UPDATE (Mon Aug 8 13:20:34 UTC 2022): the payload format has been simplified.
5 :
6 : use bstr::ByteSlice;
7 :
8 : use crate::types::EndpointId;
9 :
10 : pub(crate) struct PasswordHackPayload {
11 : pub(crate) endpoint: EndpointId,
12 : pub(crate) password: Vec<u8>,
13 : }
14 :
15 : impl PasswordHackPayload {
16 13 : pub(crate) fn parse(bytes: &[u8]) -> Option<Self> {
17 13 : // The format is `project=<utf-8>;<password-bytes>` or `project=<utf-8>$<password-bytes>`.
18 13 : let separators = [";", "$"];
19 27 : for sep in separators {
20 21 : if let Some((endpoint, password)) = bytes.split_once_str(sep) {
21 7 : let endpoint = endpoint.to_str().ok()?;
22 : return Some(Self {
23 7 : endpoint: parse_endpoint_param(endpoint)?.into(),
24 7 : password: password.to_owned(),
25 : });
26 14 : }
27 : }
28 :
29 6 : None
30 13 : }
31 : }
32 :
33 44 : pub(crate) fn parse_endpoint_param(bytes: &str) -> Option<&str> {
34 44 : bytes
35 44 : .strip_prefix("project=")
36 44 : .or_else(|| bytes.strip_prefix("endpoint="))
37 44 : }
38 :
39 : #[cfg(test)]
40 : mod tests {
41 : use super::*;
42 :
43 : #[test]
44 1 : fn parse_endpoint_param_fn() {
45 1 : let input = "";
46 1 : assert!(parse_endpoint_param(input).is_none());
47 :
48 1 : let input = "project=";
49 1 : assert_eq!(parse_endpoint_param(input), Some(""));
50 :
51 1 : let input = "project=foobar";
52 1 : assert_eq!(parse_endpoint_param(input), Some("foobar"));
53 :
54 1 : let input = "endpoint=";
55 1 : assert_eq!(parse_endpoint_param(input), Some(""));
56 :
57 1 : let input = "endpoint=foobar";
58 1 : assert_eq!(parse_endpoint_param(input), Some("foobar"));
59 :
60 1 : let input = "other_option=foobar";
61 1 : assert!(parse_endpoint_param(input).is_none());
62 1 : }
63 :
64 : #[test]
65 1 : fn parse_password_hack_payload_project() {
66 1 : let bytes = b"";
67 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
68 :
69 1 : let bytes = b"project=";
70 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
71 :
72 1 : let bytes = b"project=;";
73 1 : let payload: PasswordHackPayload =
74 1 : PasswordHackPayload::parse(bytes).expect("parsing failed");
75 1 : assert_eq!(payload.endpoint, "");
76 1 : assert_eq!(payload.password, b"");
77 :
78 1 : let bytes = b"project=foobar;pass;word";
79 1 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
80 1 : assert_eq!(payload.endpoint, "foobar");
81 1 : assert_eq!(payload.password, b"pass;word");
82 1 : }
83 :
84 : #[test]
85 1 : fn parse_password_hack_payload_endpoint() {
86 1 : let bytes = b"";
87 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
88 :
89 1 : let bytes = b"endpoint=";
90 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
91 :
92 1 : let bytes = b"endpoint=;";
93 1 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
94 1 : assert_eq!(payload.endpoint, "");
95 1 : assert_eq!(payload.password, b"");
96 :
97 1 : let bytes = b"endpoint=foobar;pass;word";
98 1 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
99 1 : assert_eq!(payload.endpoint, "foobar");
100 1 : assert_eq!(payload.password, b"pass;word");
101 1 : }
102 :
103 : #[test]
104 1 : fn parse_password_hack_payload_dollar() {
105 1 : let bytes = b"";
106 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
107 :
108 1 : let bytes = b"endpoint=";
109 1 : assert!(PasswordHackPayload::parse(bytes).is_none());
110 :
111 1 : let bytes = b"endpoint=$";
112 1 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
113 1 : assert_eq!(payload.endpoint, "");
114 1 : assert_eq!(payload.password, b"");
115 :
116 1 : let bytes = b"endpoint=foobar$pass$word";
117 1 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
118 1 : assert_eq!(payload.endpoint, "foobar");
119 1 : assert_eq!(payload.password, b"pass$word");
120 1 : }
121 : }
|