aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-01-25 22:38:06 +0100
committerMartin Fischer <martin@push-f.com>2021-01-25 22:50:40 +0100
commit3fd065757f02ebf1d055912e6809fac15c8bf058 (patch)
treef09e640f5c46111f5d63b68e5ec91bfbaee57541 /src
parente04c832200f38ab49ce8e7a6d08ffc549b8a98e2 (diff)
reintroduce CsrfToken typev0.3.3
Raw HTML is potentially dangerous so it's better to provide an encapsulating type instead of relying on naked strings. bump version to 0.3.3
Diffstat (limited to 'src')
-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.