diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 66cb1110..fcb3f38a 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -14,6 +14,7 @@ license = "MIT/Apache-2.0" categories = ["web-programming"] [features] +default = [] tls = ["rustls", "hyper-sync-rustls"] private-cookies = ["cookie/secure"] diff --git a/core/http/src/cookies.rs b/core/http/src/cookies.rs index bfdb3065..31fdadb2 100644 --- a/core/http/src/cookies.rs +++ b/core/http/src/cookies.rs @@ -1,16 +1,29 @@ use std::fmt; use std::cell::RefMut; +use Header; use cookie::Delta; + +#[doc(hidden)] pub use self::key::*; pub use cookie::{Cookie, CookieJar, SameSite}; +/// Types and methods to manage a `Key` when private cookies are enabled. #[cfg(feature = "private-cookies")] -pub use cookie::Key; - -use Header; +mod key { + pub use cookie::Key; +} +/// Types and methods to manage a `Key` when private cookies are disabled. #[cfg(not(feature = "private-cookies"))] -type Key = (); +mod key { + #[derive(Copy, Clone)] + pub struct Key; + + impl Key { + pub fn generate() -> Self { Key } + pub fn from_master(_bytes: &[u8]) -> Self { Key } + } +} /// Collection of one or more HTTP cookies. /// @@ -245,8 +258,8 @@ impl<'a> Cookies<'a> { } /// WARNING: This is unstable! Do not use this method outside of Rocket! - #[doc(hidden)] #[inline] + #[doc(hidden)] pub fn delta(&self) -> Delta { match *self { Cookies::Jarred(ref jar, _) => jar.delta(), @@ -262,7 +275,8 @@ impl<'a> Cookies<'a> { /// `Cookie` with the decrypted value. If the cookie cannot be found, or the /// cookie fails to authenticate or decrypt, `None` is returned. /// - /// This method is only available when the `private-cookies` feature is enabled. + /// This method is only available when the `private-cookies` feature is + /// enabled. /// /// # Example /// @@ -298,7 +312,8 @@ impl<'a> Cookies<'a> { /// These defaults ensure maximum usability and security. For additional /// security, you may wish to set the `secure` flag. /// - /// This method is only available when the `private-cookies` feature is enabled. + /// This method is only available when the `private-cookies` feature is + /// enabled. /// /// # Example /// @@ -337,8 +352,6 @@ impl<'a> Cookies<'a> { /// * `HttpOnly`: `true` /// * `Expires`: 1 week from now /// - /// This method is only available when the `private-cookies` feature is enabled. - /// fn set_private_defaults(cookie: &mut Cookie<'static>) { if cookie.path().is_none() { cookie.set_path("/"); @@ -363,7 +376,8 @@ impl<'a> Cookies<'a> { /// and `domain` as the cookie that was initially set. If a path is not set /// on `cookie`, the `"/"` path will automatically be set. /// - /// This method is only available when the `private-cookies` feature is enabled. + /// This method is only available when the `private-cookies` feature is + /// enabled. /// /// # Example /// diff --git a/core/http/src/lib.rs b/core/http/src/lib.rs index e9bbbed2..3b5819ea 100644 --- a/core/http/src/lib.rs +++ b/core/http/src/lib.rs @@ -59,8 +59,7 @@ pub mod uncased; #[doc(hidden)] pub use smallvec::{SmallVec, Array}; // This one we need to expose for core. -#[doc(hidden)] pub use cookies::CookieJar; -#[doc(hidden)] #[cfg(feature = "private-cookies")] pub use cookies::Key; +#[doc(hidden)] pub use cookies::{Key, CookieJar}; pub use method::Method; pub use content_type::ContentType; diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 7a6efcd2..59cba8cd 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -15,6 +15,7 @@ build = "build.rs" categories = ["web-programming::http-server"] [features] +default = ["private-cookies"] tls = ["rocket_http/tls"] private-cookies = ["rocket_http/private-cookies"] diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 7e0e985e..5fba61ce 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -11,7 +11,7 @@ use config::Environment::*; use config::{Result, ConfigBuilder, Environment, ConfigError, LoggingLevel}; use config::{Table, Value, Array, Datetime}; -#[cfg(feature = "private-cookies")] use http::Key; +use http::Key; /// Structure for Rocket application configuration. /// @@ -50,7 +50,6 @@ pub struct Config { /// How much information to log. pub log_level: LoggingLevel, /// The secret key. - #[cfg(feature = "private-cookies")] crate secret_key: SecretKey, /// TLS configuration. crate tls: Option, @@ -233,7 +232,6 @@ impl Config { let default_workers = (num_cpus::get() * 2) as u16; // Use a generated secret key by default. - #[cfg(feature = "private-cookies")] let key = SecretKey::Generated(Key::generate()); Ok(match env { @@ -245,7 +243,6 @@ impl Config { workers: default_workers, keep_alive: Some(5), log_level: LoggingLevel::Normal, - #[cfg(feature = "private-cookies")] secret_key: key, tls: None, limits: Limits::default(), @@ -261,7 +258,6 @@ impl Config { workers: default_workers, keep_alive: Some(5), log_level: LoggingLevel::Normal, - #[cfg(feature = "private-cookies")] secret_key: key, tls: None, limits: Limits::default(), @@ -277,7 +273,6 @@ impl Config { workers: default_workers, keep_alive: Some(5), log_level: LoggingLevel::Critical, - #[cfg(feature = "private-cookies")] secret_key: key, tls: None, limits: Limits::default(), @@ -479,7 +474,6 @@ impl Config { /// # Ok(()) /// # } /// ``` - #[cfg(feature = "private-cookies")] pub fn set_secret_key>(&mut self, key: K) -> Result<()> { let key = key.into(); let error = self.bad_type("secret_key", "string", @@ -497,10 +491,6 @@ impl Config { self.secret_key = SecretKey::Provided(Key::from_master(&bytes)); Ok(()) } - #[cfg(not(feature = "private-cookies"))] - pub fn set_secret_key>(&mut self, key: K) -> Result<()> { - Ok(()) - } /// Sets the logging level for `self` to `log_level`. /// @@ -674,7 +664,6 @@ impl Config { } /// Retrieves the secret key from `self`. - #[cfg(feature = "private-cookies")] #[inline] crate fn secret_key(&self) -> &Key { self.secret_key.inner() diff --git a/core/lib/src/config/custom_values.rs b/core/lib/src/config/custom_values.rs index 1efbe0c7..4fabd377 100644 --- a/core/lib/src/config/custom_values.rs +++ b/core/lib/src/config/custom_values.rs @@ -1,19 +1,17 @@ use std::fmt; -#[cfg(feature = "tls")] use http::tls::{Certificate, PrivateKey}; +#[cfg(feature = "tls")] +use http::tls::{Certificate, PrivateKey}; +use http::Key; use config::{Result, Config, Value, ConfigError, LoggingLevel}; -#[cfg(feature = "private-cookies")] use http::Key; - -#[cfg(feature = "private-cookies")] #[derive(Clone)] pub enum SecretKey { Generated(Key), Provided(Key) } -#[cfg(feature = "private-cookies")] impl SecretKey { #[inline] crate fn inner(&self) -> &Key { @@ -25,19 +23,23 @@ impl SecretKey { #[inline] crate fn is_generated(&self) -> bool { match *self { + #[cfg(feature = "private-cookies")] SecretKey::Generated(_) => true, _ => false } } } -#[cfg(feature = "private-cookies")] impl fmt::Display for SecretKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(feature = "private-cookies")] match *self { SecretKey::Generated(_) => write!(f, "generated"), SecretKey::Provided(_) => write!(f, "provided"), } + + #[cfg(not(feature = "private-cookies"))] + write!(f, "private-cookies disabled") } } diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index c13b6389..49f681c2 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -267,9 +267,10 @@ impl<'c> LocalRequest<'c> { /// Add a [private cookie] to this request. /// - /// [private cookie]: ::http::Cookies::add_private() + /// This method is only available when the `private-cookies` feature is + /// enabled. /// - /// This method is only available when the `private-cookies` feature is enabled. + /// [private cookie]: ::http::Cookies::add_private() /// /// # Examples /// @@ -283,8 +284,8 @@ impl<'c> LocalRequest<'c> { /// # #[allow(unused_variables)] /// let req = client.get("/").private_cookie(Cookie::new("user_id", "sb")); /// ``` - #[cfg(feature = "private-cookies")] #[inline] + #[cfg(feature = "private-cookies")] pub fn private_cookie(self, cookie: Cookie<'static>) -> Self { self.request.cookies().add_original_private(cookie); self diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index 112cb5e9..6191eb2c 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -222,10 +222,10 @@ impl IntoOutcome for Result { /// ```rust /// # #![feature(proc_macro_hygiene, decl_macro)] /// # #[macro_use] extern crate rocket; -/// # +/// # #[cfg(feature = "private-cookies")] mod inner { /// # use rocket::outcome::{IntoOutcome, Outcome}; /// # use rocket::request::{self, FromRequest, Request}; -/// # struct User { id: String, is_admin: bool }; +/// # struct User { id: String, is_admin: bool } /// # struct Database; /// # impl Database { /// # fn get_user(&self, id: String) -> Result { @@ -239,7 +239,7 @@ impl IntoOutcome for Result { /// # } /// # } /// # -/// # struct Admin { user: User }; +/// # struct Admin { user: User } /// # /// impl<'a, 'r> FromRequest<'a, 'r> for User { /// type Error = (); @@ -274,6 +274,7 @@ impl IntoOutcome for Result { /// /// #[get("/dashboard", rank = 2)] /// fn user_dashboard(user: User) { } +/// # } /// ``` /// /// When a non-admin user is logged in, the database will be queried twice: once @@ -285,10 +286,10 @@ impl IntoOutcome for Result { /// # #![feature(proc_macro_hygiene, decl_macro)] /// # #![feature(never_type)] /// # #[macro_use] extern crate rocket; -/// # +/// # #[cfg(feature = "private-cookies")] mod inner { /// # use rocket::outcome::{IntoOutcome, Outcome}; /// # use rocket::request::{self, FromRequest, Request}; -/// # struct User { id: String, is_admin: bool }; +/// # struct User { id: String, is_admin: bool } /// # struct Database; /// # impl Database { /// # fn get_user(&self, id: String) -> Result { @@ -302,7 +303,7 @@ impl IntoOutcome for Result { /// # } /// # } /// # -/// # struct Admin<'a> { user: &'a User }; +/// # struct Admin<'a> { user: &'a User } /// # /// impl<'a, 'r> FromRequest<'a, 'r> for &'a User { /// type Error = !; @@ -335,6 +336,7 @@ impl IntoOutcome for Result { /// } /// } /// } +/// # } /// ``` /// /// Notice that these request guards provide access to *borrowed* data (`&'a diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 707ec3f8..84cbb783 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -290,10 +290,7 @@ impl<'r> Request<'r> { pub fn cookies(&self) -> Cookies { // FIXME: Can we do better? This is disappointing. match self.state.cookies.try_borrow_mut() { - #[cfg(feature = "private-cookies")] Ok(jar) => Cookies::new(jar, self.state.config.secret_key()), - #[cfg(not(feature = "private-cookies"))] - Ok(jar) => Cookies::new(jar, &()), Err(_) => { error_!("Multiple `Cookies` instances are active at once."); info_!("An instance of `Cookies` must be dropped before another \ diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 11e7792d..70b7be05 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -396,7 +396,6 @@ impl Rocket { launch_info_!("port: {}", Paint::white(&config.port)); launch_info_!("log: {}", Paint::white(config.log_level)); launch_info_!("workers: {}", Paint::white(config.workers)); - #[cfg(feature = "private-cookies")] launch_info_!("secret key: {}", Paint::white(&config.secret_key)); launch_info_!("limits: {}", Paint::white(&config.limits)); @@ -415,10 +414,8 @@ impl Rocket { launch_info_!("tls: {}", Paint::white("disabled")); } - #[cfg(feature = "private-cookies")] { - if config.secret_key.is_generated() && config.environment.is_prod() { - warn!("environment is 'production', but no `secret_key` is configured"); - } + if config.secret_key.is_generated() && config.environment.is_prod() { + warn!("environment is 'production', but no `secret_key` is configured"); } for (name, value) in config.extras() { diff --git a/core/lib/tests/local_request_private_cookie-issue-368.rs b/core/lib/tests/local_request_private_cookie-issue-368.rs index defafe01..ef6c8613 100644 --- a/core/lib/tests/local_request_private_cookie-issue-368.rs +++ b/core/lib/tests/local_request_private_cookie-issue-368.rs @@ -1,45 +1,48 @@ #![feature(proc_macro_hygiene, decl_macro)] -#[macro_use] extern crate rocket; - -use rocket::http::Cookies; +#[macro_use] +#[cfg(feature = "private-cookies")] +extern crate rocket; #[cfg(feature = "private-cookies")] -#[get("/")] -fn return_private_cookie(mut cookies: Cookies) -> Option { - match cookies.get_private("cookie_name") { - Some(cookie) => Some(cookie.value().into()), - None => None, - } -} - -#[cfg(feature = "private-cookies")] -mod tests { - use super::*; - use rocket::local::Client; - use rocket::http::Cookie; - use rocket::http::Status; - - #[test] - fn private_cookie_is_returned() { - let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); - - let client = Client::new(rocket).unwrap(); - let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); - let mut response = req.dispatch(); - - assert_eq!(response.body_string(), Some("cookie_value".into())); - assert_eq!(response.headers().get_one("Set-Cookie"), None); - } - - #[test] - fn regular_cookie_is_not_returned() { - let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); - - let client = Client::new(rocket).unwrap(); - let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value")); - let response = req.dispatch(); - - assert_eq!(response.status(), Status::NotFound); +mod private_cookie_test { + use rocket::http::Cookies; + + #[get("/")] + fn return_private_cookie(mut cookies: Cookies) -> Option { + match cookies.get_private("cookie_name") { + Some(cookie) => Some(cookie.value().into()), + None => None, + } + } + + mod tests { + use super::*; + use rocket::local::Client; + use rocket::http::Cookie; + use rocket::http::Status; + + #[test] + fn private_cookie_is_returned() { + let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); + + let client = Client::new(rocket).unwrap(); + let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); + let mut response = req.dispatch(); + + assert_eq!(response.body_string(), Some("cookie_value".into())); + assert_eq!(response.headers().get_one("Set-Cookie"), None); + } + + #[test] + fn regular_cookie_is_not_returned() { + let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); + + let client = Client::new(rocket).unwrap(); + let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value")); + let response = req.dispatch(); + + assert_eq!(response.status(), Status::NotFound); + } } } diff --git a/scripts/test.sh b/scripts/test.sh index 10888b57..41a219c1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -110,18 +110,15 @@ if [ "$1" = "--contrib" ]; then popd > /dev/null 2>&1 elif [ "$1" = "--core" ]; then FEATURES=( - private-cookies + private-cookies # this is already tested since it's the default feature tls ) - pushd "${CORE_ROOT}" > /dev/null 2>&1 + pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1 echo ":: Building and testing core [no features]..." CARGO_INCREMENTAL=0 cargo test --no-default-features - echo ":: Building and testing core [default]..." - CARGO_INCREMENTAL=0 cargo test - for feature in "${FEATURES[@]}"; do echo ":: Building and testing core [${feature}]..." CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index aa5e5450..9a530055 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -503,15 +503,18 @@ fn logout(mut cookies: Cookies) -> Flash { [`Cookies::add()`]: @api/rocket/http/enum.Cookies.html#method.add -Private Cookies can be omitted at build time by excluding the feature -`private-cookies`. You can do this by setting the `default-features` -directive to `false` in your `Cargo.toml`: +Support for private cookies, which depends on the [`ring`] library, can be +omitted at build time by disabling Rocket's default features, in-turn disabling +the default `private-cookies` feature. To do so, modify your `Cargo.toml` file +so that you depend on `rocket` as follows: ```toml -[dependencies.rocket] -default-features = false +[dependencies] +rocket = { version = "0.4.0-rc.1", default-features = false } ``` +[`ring`]: https://github.com/briansmith/ring + ### Secret Key To encrypt private cookies, Rocket uses the 256-bit key specified in the