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::EndpointId;
9 :
10 : pub(crate) struct PasswordHackPayload {
11 : pub(crate) endpoint: EndpointId,
12 : pub(crate) password: Vec<u8>,
13 : }
14 :
15 : impl PasswordHackPayload {
16 78 : pub(crate) fn parse(bytes: &[u8]) -> Option<Self> {
17 78 : // The format is `project=<utf-8>;<password-bytes>` or `project=<utf-8>$<password-bytes>`.
18 78 : let separators = [";", "$"];
19 162 : for sep in separators {
20 126 : if let Some((endpoint, password)) = bytes.split_once_str(sep) {
21 42 : let endpoint = endpoint.to_str().ok()?;
22 : return Some(Self {
23 42 : endpoint: parse_endpoint_param(endpoint)?.into(),
24 42 : password: password.to_owned(),
25 : });
26 84 : }
27 : }
28 :
29 36 : None
30 78 : }
31 : }
32 :
33 264 : pub(crate) fn parse_endpoint_param(bytes: &str) -> Option<&str> {
34 264 : bytes
35 264 : .strip_prefix("project=")
36 264 : .or_else(|| bytes.strip_prefix("endpoint="))
37 264 : }
38 :
39 : #[cfg(test)]
40 : mod tests {
41 : use super::*;
42 :
43 : #[test]
44 6 : fn parse_endpoint_param_fn() {
45 6 : let input = "";
46 6 : assert!(parse_endpoint_param(input).is_none());
47 :
48 6 : let input = "project=";
49 6 : assert_eq!(parse_endpoint_param(input), Some(""));
50 :
51 6 : let input = "project=foobar";
52 6 : assert_eq!(parse_endpoint_param(input), Some("foobar"));
53 :
54 6 : let input = "endpoint=";
55 6 : assert_eq!(parse_endpoint_param(input), Some(""));
56 :
57 6 : let input = "endpoint=foobar";
58 6 : assert_eq!(parse_endpoint_param(input), Some("foobar"));
59 :
60 6 : let input = "other_option=foobar";
61 6 : assert!(parse_endpoint_param(input).is_none());
62 6 : }
63 :
64 : #[test]
65 6 : fn parse_password_hack_payload_project() {
66 6 : let bytes = b"";
67 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
68 :
69 6 : let bytes = b"project=";
70 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
71 :
72 6 : let bytes = b"project=;";
73 6 : let payload: PasswordHackPayload =
74 6 : PasswordHackPayload::parse(bytes).expect("parsing failed");
75 6 : assert_eq!(payload.endpoint, "");
76 6 : assert_eq!(payload.password, b"");
77 :
78 6 : let bytes = b"project=foobar;pass;word";
79 6 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
80 6 : assert_eq!(payload.endpoint, "foobar");
81 6 : assert_eq!(payload.password, b"pass;word");
82 6 : }
83 :
84 : #[test]
85 6 : fn parse_password_hack_payload_endpoint() {
86 6 : let bytes = b"";
87 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
88 :
89 6 : let bytes = b"endpoint=";
90 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
91 :
92 6 : let bytes = b"endpoint=;";
93 6 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
94 6 : assert_eq!(payload.endpoint, "");
95 6 : assert_eq!(payload.password, b"");
96 :
97 6 : let bytes = b"endpoint=foobar;pass;word";
98 6 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
99 6 : assert_eq!(payload.endpoint, "foobar");
100 6 : assert_eq!(payload.password, b"pass;word");
101 6 : }
102 :
103 : #[test]
104 6 : fn parse_password_hack_payload_dollar() {
105 6 : let bytes = b"";
106 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
107 :
108 6 : let bytes = b"endpoint=";
109 6 : assert!(PasswordHackPayload::parse(bytes).is_none());
110 :
111 6 : let bytes = b"endpoint=$";
112 6 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
113 6 : assert_eq!(payload.endpoint, "");
114 6 : assert_eq!(payload.password, b"");
115 :
116 6 : let bytes = b"endpoint=foobar$pass$word";
117 6 : let payload = PasswordHackPayload::parse(bytes).expect("parsing failed");
118 6 : assert_eq!(payload.endpoint, "foobar");
119 6 : assert_eq!(payload.password, b"pass$word");
120 6 : }
121 : }
|