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