Compare commits

...

13 Commits

Author SHA1 Message Date
Alex Kotov 58cebbac0b
Fix README.md 2022-07-13 07:52:44 +03:00
Alex Kotov b318019955
Fix README.md 2022-07-13 07:49:51 +03:00
Alex Kotov c0211870f0
Merge pull request #2 from fenhl/rocket05
Update for the rocket 0.5 release candidate
2022-07-13 07:42:14 +03:00
Alex Kotov f9068fca7f
Do not bump version 2022-07-13 07:38:50 +03:00
Alex Kotov d4d233526b
Fix example 2022-07-13 07:37:42 +03:00
Alex Kotov 2caf37e455
Bump version (0.4.0) 2022-07-13 07:24:17 +03:00
Alex Kotov e2fc0f052b
Merge branch 'master' into patch-rocket5 2022-07-13 07:23:29 +03:00
Alex Kotov 7e3aaecc45
Upgrade crate "rocket" to 0.4.11 2022-07-13 07:21:17 +03:00
Fenhl 5002bdbab5
Update for 2nd release candidate 2022-05-17 07:00:52 +00:00
Fenhl 610f27297c Update for the rocket 0.5 release candidate 2022-03-23 00:03:32 +00:00
Alex Kotov 0004bd2992 Add empty line 2021-03-06 08:47:47 +05:00
Alex Kotov 10a2780b42 Format code 2021-03-06 08:34:40 +05:00
Alex Kotov 113b69505e Add documentation for configuration 2021-03-06 08:32:10 +05:00
10 changed files with 1045 additions and 417 deletions

View File

@ -16,5 +16,4 @@ publish = true
base64 = { version = "0.13.0" }
bcrypt = { version = "0.9" }
rand = { version = "0.8.3" }
rocket = { version = "0.4.5", features = ["private-cookies"] }
time = { version = "0.1.38" }
rocket = { version = "0.5.0-rc.2", features = ["secrets"] }

View File

@ -25,7 +25,7 @@ Table of contents
Usage
-----
Attach [fairing](https://rocket.rs/v0.4/guide/fairings/#fairings) to the Rocket
Attach [fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings) to the Rocket
instance:
```rust
@ -34,26 +34,44 @@ instance:
#[macro_use] extern crate rocket;
#[macro_use] extern crate serde_derive;
use rocket_contrib::templates::Template;
use rocket_dyn_templates::Template;
fn main() {
#[launch]
fn rocket() -> _ {
rocket::ignite()
.attach(rocket_csrf::Fairing::default())
.attach(Template::fairing())
.mount("/", routes![new, create])
.launch();
}
```
Add [guard](https://rocket.rs/v0.4/guide/requests/#request-guards) to any
You also can configure
[fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings):
```rust
#[launch]
fn rocket() -> _ {
rocket::ignite()
.attach(rocket_csrf::Fairing::new(
rocket_csrf::CsrfConfig::default()
.with_cookie_name("foobar")
.with_cookie_len(64)
.with_lifetime(time::Duration::days(3)),
))
.attach(Template::fairing())
.mount("/", routes![new, create])
}
```
Add [guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards) to any
request where you want to have access to session's CSRF token (e.g. to include
it in forms) or verify it (e.g. to validate form):
```rust
use rocket::form::Form;
use rocket::response::Redirect;
use rocket::request::Form;
use rocket_contrib::templates::Template;
use rocket_csrf::CsrfToken;
use rocket_dyn_templates::Template;
#[get("/comments/new")]
fn new(csrf_token: CsrfToken) -> Template {
@ -67,8 +85,8 @@ fn create(csrf_token: CsrfToken, form: Form<Comment>) -> Redirect {
```
Get CSRF token from
[guard](https://rocket.rs/v0.4/guide/requests/#request-guards)
to use it in [templates](https://rocket.rs/v0.4/guide/responses/#templates):
[guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards)
to use it in [templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
```rust
#[get("/comments/new")]
@ -80,7 +98,7 @@ fn new(csrf_token: CsrfToken) -> Template {
```
Add CSRF token to your HTML forms in
[templates](https://rocket.rs/v0.4/guide/responses/#templates):
[templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
```html
<form method="post" action="/comments">
@ -90,7 +108,7 @@ Add CSRF token to your HTML forms in
```
Add attribute `authenticity_token` to your
[forms](https://rocket.rs/v0.4/guide/requests/#forms):
[forms](https://rocket.rs/v0.5-rc/guide/requests/#forms):
```rust
#[derive(FromForm)]
@ -100,7 +118,7 @@ struct Comment {
}
```
Validate [forms](https://rocket.rs/v0.4/guide/requests/#forms) to have valid
Validate [forms](https://rocket.rs/v0.5-rc/guide/requests/#forms) to have valid
authenticity token:
```rust
@ -122,7 +140,7 @@ TODO
----
* [ ] Add fairing to verify all requests as an option.
* [ ] Add [data guard](https://api.rocket.rs/v0.4/rocket/data/trait.FromData.html) to verify forms with a guard.
* [ ] Add [data guard](https://api.rocket.rs/v0.5-rc/rocket/data/trait.FromData.html) to verify forms with a guard.
* [ ] Add helpers to render form field.
* [ ] Add helpers to add HTML meta tags for Ajax with `X-CSRF-Token` header.
* [ ] Verify `X-CSRF-Token` header.

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,8 @@ edition = "2018"
publish = false
[dependencies]
rocket = "0.4.5"
rocket = "0.5.0-rc.2"
rocket_csrf = { path = "../.." }
rocket_contrib = { version = "0.4.5", features = ["handlebars_templates"] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["handlebars"] }
serde = "1.0.117"
serde_derive = "1.0.117"

View File

@ -1,12 +1,15 @@
#![feature(decl_macro)]
#[macro_use] extern crate rocket;
#[macro_use] extern crate serde_derive;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate serde_derive;
use rocket::form::Form;
use rocket::request::FlashMessage;
use rocket::response::{Flash, Redirect};
use rocket::request::{FlashMessage, Form};
use rocket_contrib::templates::Template;
use rocket_csrf::CsrfToken;
use rocket_dyn_templates::Template;
#[derive(Serialize)]
struct TemplateContext {
@ -20,12 +23,12 @@ struct Comment {
text: String,
}
fn main() {
rocket::ignite()
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(rocket_csrf::Fairing::default())
.attach(Template::fairing())
.mount("/", routes![index, new, create])
.launch();
}
#[get("/")]
@ -37,7 +40,7 @@ fn index() -> Redirect {
fn new(csrf_token: CsrfToken, flash: Option<FlashMessage>) -> Template {
let template_context = TemplateContext {
authenticity_token: csrf_token.authenticity_token().to_string(),
flash: flash.map(|msg| format!("{}! {}", msg.name(), msg.msg())),
flash: flash.map(|flash| flash.message().to_string()),
};
Template::render("comments/new", &template_context)
@ -46,10 +49,7 @@ fn new(csrf_token: CsrfToken, flash: Option<FlashMessage>) -> Template {
#[post("/comments", data = "<form>")]
fn create(csrf_token: CsrfToken, form: Form<Comment>) -> Flash<Redirect> {
if let Err(_) = csrf_token.verify(&form.authenticity_token) {
return Flash::error(
Redirect::to(uri!(new)),
"invalid authenticity token",
);
return Flash::error(Redirect::to(uri!(new)), "invalid authenticity token");
}
Flash::success(

View File

@ -1,13 +1,14 @@
use bcrypt::{hash, verify};
use rand::{distributions::Standard, Rng};
use rocket::{
fairing::{Fairing as RocketFairing, Info, Kind},
async_trait,
fairing::{self, Fairing as RocketFairing, Info, Kind},
http::{Cookie, Status},
request::{FromRequest, Outcome},
time::{Duration, OffsetDateTime},
Data, Request, Rocket, State,
};
use std::borrow::Cow;
use time::Duration;
const BCRYPT_COST: u32 = 8;
@ -94,20 +95,21 @@ impl CsrfToken {
}
}
#[async_trait]
impl RocketFairing for Fairing {
fn info(&self) -> Info {
Info {
name: "CSRF",
kind: Kind::Attach | Kind::Request,
kind: Kind::Ignite | Kind::Request,
}
}
fn on_attach(&self, rocket: Rocket) -> std::result::Result<Rocket, Rocket> {
async fn on_ignite(&self, rocket: Rocket<rocket::Build>) -> fairing::Result {
Ok(rocket.manage(self.config.clone()))
}
fn on_request(&self, request: &mut Request, _: &Data) {
let config = request.guard::<State<CsrfConfig>>().unwrap();
async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
let config = request.guard::<&State<CsrfConfig>>().await.unwrap();
if let Some(_) = request.valid_csrf_token_from_session(&config) {
return;
@ -120,7 +122,7 @@ impl RocketFairing for Fairing {
let encoded = base64::encode(&values[..]);
let expires = time::now_utc() + config.lifespan;
let expires = OffsetDateTime::now_utc() + config.lifespan;
request.cookies().add_private(
Cookie::build(config.cookie_name.clone(), encoded)
@ -130,11 +132,13 @@ impl RocketFairing for Fairing {
}
}
impl<'a, 'r> FromRequest<'a, 'r> for CsrfToken {
#[async_trait]
impl<'r> FromRequest<'r> for CsrfToken {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
let config = request.guard::<State<CsrfConfig>>().unwrap();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let config = request.guard::<&State<CsrfConfig>>().await.unwrap();
match request.valid_csrf_token_from_session(&config) {
None => Outcome::Failure((Status::Forbidden, ())),
Some(token) => Outcome::Success(Self(base64::encode(token))),

View File

@ -1,32 +1,38 @@
#![feature(decl_macro)]
#[macro_use] extern crate rocket;
#[macro_use]
extern crate rocket;
const COOKIE_NAME: &str = "foobar";
const COOKIE_LEN: usize = 64;
fn client() -> rocket::local::Client {
rocket::local::Client::new(rocket()).unwrap()
fn client() -> rocket::local::blocking::Client {
rocket::local::blocking::Client::tracked(rocket()).unwrap()
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
fn rocket() -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(rocket_csrf::Fairing::new(
rocket_csrf::CsrfConfig::default()
.with_cookie_name(COOKIE_NAME)
.with_cookie_len(COOKIE_LEN)
.with_lifetime(time::Duration::days(3))
.with_lifetime(rocket::time::Duration::days(3)),
))
.mount("/", routes![index])
}
#[get("/")]
fn index() {
}
fn index() {}
#[test]
fn add_csrf_token_to_cookies() {
base64::decode(client().get("/").dispatch().cookies().iter().find(|cookie| {
cookie.name() == COOKIE_NAME
}).unwrap().value()).unwrap();
base64::decode(
client()
.get("/")
.dispatch()
.cookies()
.iter()
.find(|cookie| cookie.name() == COOKIE_NAME)
.unwrap()
.value(),
)
.unwrap();
}

View File

@ -1,24 +1,30 @@
#![feature(decl_macro)]
#[macro_use]
extern crate rocket;
#[macro_use] extern crate rocket;
fn client() -> rocket::local::Client {
rocket::local::Client::new(rocket()).unwrap()
fn client() -> rocket::local::blocking::Client {
rocket::local::blocking::Client::tracked(rocket()).unwrap()
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
fn rocket() -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(rocket_csrf::Fairing::default())
.mount("/", routes![index])
}
#[get("/")]
fn index() {
}
fn index() {}
#[test]
fn add_csrf_token_to_cookies() {
base64::decode(client().get("/").dispatch().cookies().iter().find(|cookie| {
cookie.name() == "csrf_token"
}).unwrap().value()).unwrap();
base64::decode(
client()
.get("/")
.dispatch()
.cookies()
.iter()
.find(|cookie| cookie.name() == "csrf_token")
.unwrap()
.value(),
)
.unwrap();
}

View File

@ -1,26 +1,25 @@
#![feature(decl_macro)]
#[macro_use] extern crate rocket;
#[macro_use]
extern crate rocket;
use bcrypt::verify;
use rand::RngCore;
use rocket::http::Cookie;
use rocket_csrf::CsrfToken;
use bcrypt::verify;
const COOKIE_NAME: &str = "foobar";
const COOKIE_LEN: usize = 64;
fn client() -> rocket::local::Client {
rocket::local::Client::new(rocket()).unwrap()
fn client() -> rocket::local::blocking::Client {
rocket::local::blocking::Client::tracked(rocket()).unwrap()
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
fn rocket() -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(rocket_csrf::Fairing::new(
rocket_csrf::CsrfConfig::default()
.with_cookie_name(COOKIE_NAME)
.with_cookie_len(COOKIE_LEN)
.with_lifetime(time::Duration::days(3))
.with_lifetime(rocket::time::Duration::days(3)),
))
.mount("/", routes![index])
}
@ -41,8 +40,6 @@ fn respond_with_valid_authenticity_token() {
.get("/")
.private_cookie(Cookie::new(COOKIE_NAME, encoded.to_string()))
.dispatch()
.body()
.unwrap()
.into_string()
.unwrap();

View File

@ -1,18 +1,17 @@
#![feature(decl_macro)]
#[macro_use] extern crate rocket;
#[macro_use]
extern crate rocket;
use bcrypt::verify;
use rand::RngCore;
use rocket::http::Cookie;
use rocket_csrf::CsrfToken;
use bcrypt::verify;
fn client() -> rocket::local::Client {
rocket::local::Client::new(rocket()).unwrap()
fn client() -> rocket::local::blocking::Client {
rocket::local::blocking::Client::tracked(rocket()).unwrap()
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
fn rocket() -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(rocket_csrf::Fairing::default())
.mount("/", routes![index])
}
@ -33,8 +32,6 @@ fn respond_with_valid_authenticity_token() {
.get("/")
.private_cookie(Cookie::new("csrf_token", encoded.to_string()))
.dispatch()
.body()
.unwrap()
.into_string()
.unwrap();