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