LCOV - code coverage report
Current view: top level - proxy/src/auth - password_hack.rs (source / functions) Coverage Total Hit
Test: a43a77853355b937a79c57b07a8f05607cf29e6c.info Lines: 100.0 % 73 73
Test Date: 2024-09-19 12:04:32 Functions: 100.0 % 7 7

            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           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              : }
        

Generated by: LCOV version 2.1-beta