diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/request.rs | 36 | 
1 files changed, 27 insertions, 9 deletions
| diff --git a/src/request.rs b/src/request.rs index 5b5679d..7734d03 100644 --- a/src/request.rs +++ b/src/request.rs @@ -23,11 +23,9 @@ pub trait SputnikParts {      /// Enforces a specific Content-Type.      fn enforce_content_type(&self, mime: Mime) -> Result<(), WrongContentTypeError>; -    /// Retrievs the CSRF token from a cookie or generates -    /// a new token and stores it as a cookie if it doesn't exist. -    /// Returns a hidden HTML input to be embedded in forms that are received -    /// with [`crate::request::SputnikBody::into_form_csrf`]. -    fn csrf_html_input(&mut self, builder: &mut Builder) -> String; +    /// Returns a CSRF token, either extracted from the `csrf` cookie or newly +    /// generated if the cookie wasn't sent (in which case the cookie is set). +    fn csrf_token(&mut self, builder: &mut Builder) -> CsrfToken;  }  impl SputnikParts for hyper::http::request::Parts { @@ -68,16 +66,36 @@ impl SputnikParts for hyper::http::request::Parts {          Err(WrongContentTypeError{expected: mime, received: self.headers.get(header::CONTENT_TYPE).as_ref().and_then(|h| h.to_str().ok().map(|s| s.to_owned()))})      } -    fn csrf_html_input(&mut self, builder: &mut Builder) -> String { +    fn csrf_token(&mut self, builder: &mut Builder) -> CsrfToken {          let token = csrf_token_from_cookies(self).unwrap_or_else(|| { -            let token: String = rand::thread_rng().sample_iter(Alphanumeric).take(16).collect(); +            let token: String = rand::thread_rng().sample_iter( +                Alphanumeric +                // must be HTML-safe because we embed it in CsrfToken::html_input +            ).take(16).collect();              let mut c = Cookie::new(CSRF_COOKIE_NAME, token.clone());              c.set_secure(Some(true));              c.set_max_age(Some(Duration::hours(1)));              builder.set_cookie(c);              token          }); -        format!("<input name=csrf type=hidden value=\"{}\">", token) +        CsrfToken(token) +    } +} + +/// A CSRF token retrievable with [`SputnikParts::csrf_token`]. +pub struct CsrfToken(String); + +impl std::fmt::Display for CsrfToken { +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +        write!(f, "{}", self.0) +    } +} + +impl CsrfToken { +    /// Returns a hidden HTML input to be embedded in forms that are received +    /// with [`crate::request::SputnikBody::into_form_csrf`]. +    pub fn html_input(&self) -> String { +        format!("<input name=csrf type=hidden value=\"{}\">", self)      }  } @@ -94,7 +112,7 @@ pub trait SputnikBody {      /// Parses a `application/x-www-form-urlencoded` request body into a given struct.      /// Protects from CSRF by checking that the request body contains the same token retrieved from the cookies.      /// -    /// The HTML form must embed a hidden input generated with [`crate::request::SputnikParts::csrf_html_input`]. +    /// The HTML form must embed a hidden input generated with [`CsrfToken::html_input`].      async fn into_form_csrf<T: DeserializeOwned>(self, req: &mut Parts) -> Result<T, CsrfProtectedFormError>;      /// Attempts to deserialize the request body as JSON. | 
