aboutsummaryrefslogtreecommitdiff
path: root/src/request.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/request.rs')
-rw-r--r--src/request.rs36
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.