Reduce 'cfg' usage for 'private-cookies' feature.

This commit is contained in:
Sergio Benitez 2018-11-08 23:37:37 -08:00
parent 53758c6dd7
commit 4224419e63
13 changed files with 102 additions and 96 deletions

View File

@ -14,6 +14,7 @@ license = "MIT/Apache-2.0"
categories = ["web-programming"] categories = ["web-programming"]
[features] [features]
default = []
tls = ["rustls", "hyper-sync-rustls"] tls = ["rustls", "hyper-sync-rustls"]
private-cookies = ["cookie/secure"] private-cookies = ["cookie/secure"]

View File

@ -1,16 +1,29 @@
use std::fmt; use std::fmt;
use std::cell::RefMut; use std::cell::RefMut;
use Header;
use cookie::Delta; use cookie::Delta;
#[doc(hidden)] pub use self::key::*;
pub use cookie::{Cookie, CookieJar, SameSite}; pub use cookie::{Cookie, CookieJar, SameSite};
/// Types and methods to manage a `Key` when private cookies are enabled.
#[cfg(feature = "private-cookies")] #[cfg(feature = "private-cookies")]
pub use cookie::Key; mod key {
pub use cookie::Key;
use Header; }
/// Types and methods to manage a `Key` when private cookies are disabled.
#[cfg(not(feature = "private-cookies"))] #[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. /// 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! /// WARNING: This is unstable! Do not use this method outside of Rocket!
#[doc(hidden)]
#[inline] #[inline]
#[doc(hidden)]
pub fn delta(&self) -> Delta { pub fn delta(&self) -> Delta {
match *self { match *self {
Cookies::Jarred(ref jar, _) => jar.delta(), 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` with the decrypted value. If the cookie cannot be found, or the
/// cookie fails to authenticate or decrypt, `None` is returned. /// 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 /// # Example
/// ///
@ -298,7 +312,8 @@ impl<'a> Cookies<'a> {
/// These defaults ensure maximum usability and security. For additional /// These defaults ensure maximum usability and security. For additional
/// security, you may wish to set the `secure` flag. /// 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 /// # Example
/// ///
@ -337,8 +352,6 @@ impl<'a> Cookies<'a> {
/// * `HttpOnly`: `true` /// * `HttpOnly`: `true`
/// * `Expires`: 1 week from now /// * `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>) { fn set_private_defaults(cookie: &mut Cookie<'static>) {
if cookie.path().is_none() { if cookie.path().is_none() {
cookie.set_path("/"); 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 /// and `domain` as the cookie that was initially set. If a path is not set
/// on `cookie`, the `"/"` path will automatically be 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 /// # Example
/// ///

View File

@ -59,8 +59,7 @@ pub mod uncased;
#[doc(hidden)] pub use smallvec::{SmallVec, Array}; #[doc(hidden)] pub use smallvec::{SmallVec, Array};
// This one we need to expose for core. // This one we need to expose for core.
#[doc(hidden)] pub use cookies::CookieJar; #[doc(hidden)] pub use cookies::{Key, CookieJar};
#[doc(hidden)] #[cfg(feature = "private-cookies")] pub use cookies::Key;
pub use method::Method; pub use method::Method;
pub use content_type::ContentType; pub use content_type::ContentType;

View File

@ -15,6 +15,7 @@ build = "build.rs"
categories = ["web-programming::http-server"] categories = ["web-programming::http-server"]
[features] [features]
default = ["private-cookies"]
tls = ["rocket_http/tls"] tls = ["rocket_http/tls"]
private-cookies = ["rocket_http/private-cookies"] private-cookies = ["rocket_http/private-cookies"]

View File

@ -11,7 +11,7 @@ use config::Environment::*;
use config::{Result, ConfigBuilder, Environment, ConfigError, LoggingLevel}; use config::{Result, ConfigBuilder, Environment, ConfigError, LoggingLevel};
use config::{Table, Value, Array, Datetime}; use config::{Table, Value, Array, Datetime};
#[cfg(feature = "private-cookies")] use http::Key; use http::Key;
/// Structure for Rocket application configuration. /// Structure for Rocket application configuration.
/// ///
@ -50,7 +50,6 @@ pub struct Config {
/// How much information to log. /// How much information to log.
pub log_level: LoggingLevel, pub log_level: LoggingLevel,
/// The secret key. /// The secret key.
#[cfg(feature = "private-cookies")]
crate secret_key: SecretKey, crate secret_key: SecretKey,
/// TLS configuration. /// TLS configuration.
crate tls: Option<TlsConfig>, crate tls: Option<TlsConfig>,
@ -233,7 +232,6 @@ impl Config {
let default_workers = (num_cpus::get() * 2) as u16; let default_workers = (num_cpus::get() * 2) as u16;
// Use a generated secret key by default. // Use a generated secret key by default.
#[cfg(feature = "private-cookies")]
let key = SecretKey::Generated(Key::generate()); let key = SecretKey::Generated(Key::generate());
Ok(match env { Ok(match env {
@ -245,7 +243,6 @@ impl Config {
workers: default_workers, workers: default_workers,
keep_alive: Some(5), keep_alive: Some(5),
log_level: LoggingLevel::Normal, log_level: LoggingLevel::Normal,
#[cfg(feature = "private-cookies")]
secret_key: key, secret_key: key,
tls: None, tls: None,
limits: Limits::default(), limits: Limits::default(),
@ -261,7 +258,6 @@ impl Config {
workers: default_workers, workers: default_workers,
keep_alive: Some(5), keep_alive: Some(5),
log_level: LoggingLevel::Normal, log_level: LoggingLevel::Normal,
#[cfg(feature = "private-cookies")]
secret_key: key, secret_key: key,
tls: None, tls: None,
limits: Limits::default(), limits: Limits::default(),
@ -277,7 +273,6 @@ impl Config {
workers: default_workers, workers: default_workers,
keep_alive: Some(5), keep_alive: Some(5),
log_level: LoggingLevel::Critical, log_level: LoggingLevel::Critical,
#[cfg(feature = "private-cookies")]
secret_key: key, secret_key: key,
tls: None, tls: None,
limits: Limits::default(), limits: Limits::default(),
@ -479,7 +474,6 @@ impl Config {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "private-cookies")]
pub fn set_secret_key<K: Into<String>>(&mut self, key: K) -> Result<()> { pub fn set_secret_key<K: Into<String>>(&mut self, key: K) -> Result<()> {
let key = key.into(); let key = key.into();
let error = self.bad_type("secret_key", "string", let error = self.bad_type("secret_key", "string",
@ -497,10 +491,6 @@ impl Config {
self.secret_key = SecretKey::Provided(Key::from_master(&bytes)); self.secret_key = SecretKey::Provided(Key::from_master(&bytes));
Ok(()) Ok(())
} }
#[cfg(not(feature = "private-cookies"))]
pub fn set_secret_key<K: Into<String>>(&mut self, key: K) -> Result<()> {
Ok(())
}
/// Sets the logging level for `self` to `log_level`. /// Sets the logging level for `self` to `log_level`.
/// ///
@ -674,7 +664,6 @@ impl Config {
} }
/// Retrieves the secret key from `self`. /// Retrieves the secret key from `self`.
#[cfg(feature = "private-cookies")]
#[inline] #[inline]
crate fn secret_key(&self) -> &Key { crate fn secret_key(&self) -> &Key {
self.secret_key.inner() self.secret_key.inner()

View File

@ -1,19 +1,17 @@
use std::fmt; 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}; use config::{Result, Config, Value, ConfigError, LoggingLevel};
#[cfg(feature = "private-cookies")] use http::Key;
#[cfg(feature = "private-cookies")]
#[derive(Clone)] #[derive(Clone)]
pub enum SecretKey { pub enum SecretKey {
Generated(Key), Generated(Key),
Provided(Key) Provided(Key)
} }
#[cfg(feature = "private-cookies")]
impl SecretKey { impl SecretKey {
#[inline] #[inline]
crate fn inner(&self) -> &Key { crate fn inner(&self) -> &Key {
@ -25,19 +23,23 @@ impl SecretKey {
#[inline] #[inline]
crate fn is_generated(&self) -> bool { crate fn is_generated(&self) -> bool {
match *self { match *self {
#[cfg(feature = "private-cookies")]
SecretKey::Generated(_) => true, SecretKey::Generated(_) => true,
_ => false _ => false
} }
} }
} }
#[cfg(feature = "private-cookies")]
impl fmt::Display for SecretKey { impl fmt::Display for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(feature = "private-cookies")]
match *self { match *self {
SecretKey::Generated(_) => write!(f, "generated"), SecretKey::Generated(_) => write!(f, "generated"),
SecretKey::Provided(_) => write!(f, "provided"), SecretKey::Provided(_) => write!(f, "provided"),
} }
#[cfg(not(feature = "private-cookies"))]
write!(f, "private-cookies disabled")
} }
} }

View File

@ -267,9 +267,10 @@ impl<'c> LocalRequest<'c> {
/// Add a [private cookie] to this request. /// 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 /// # Examples
/// ///
@ -283,8 +284,8 @@ impl<'c> LocalRequest<'c> {
/// # #[allow(unused_variables)] /// # #[allow(unused_variables)]
/// let req = client.get("/").private_cookie(Cookie::new("user_id", "sb")); /// let req = client.get("/").private_cookie(Cookie::new("user_id", "sb"));
/// ``` /// ```
#[cfg(feature = "private-cookies")]
#[inline] #[inline]
#[cfg(feature = "private-cookies")]
pub fn private_cookie(self, cookie: Cookie<'static>) -> Self { pub fn private_cookie(self, cookie: Cookie<'static>) -> Self {
self.request.cookies().add_original_private(cookie); self.request.cookies().add_original_private(cookie);
self self

View File

@ -222,10 +222,10 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// ```rust /// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)] /// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;
/// # /// # #[cfg(feature = "private-cookies")] mod inner {
/// # use rocket::outcome::{IntoOutcome, Outcome}; /// # use rocket::outcome::{IntoOutcome, Outcome};
/// # use rocket::request::{self, FromRequest, Request}; /// # use rocket::request::{self, FromRequest, Request};
/// # struct User { id: String, is_admin: bool }; /// # struct User { id: String, is_admin: bool }
/// # struct Database; /// # struct Database;
/// # impl Database { /// # impl Database {
/// # fn get_user(&self, id: String) -> Result<User, ()> { /// # fn get_user(&self, id: String) -> Result<User, ()> {
@ -239,7 +239,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// # } /// # }
/// # } /// # }
/// # /// #
/// # struct Admin { user: User }; /// # struct Admin { user: User }
/// # /// #
/// impl<'a, 'r> FromRequest<'a, 'r> for User { /// impl<'a, 'r> FromRequest<'a, 'r> for User {
/// type Error = (); /// type Error = ();
@ -274,6 +274,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// ///
/// #[get("/dashboard", rank = 2)] /// #[get("/dashboard", rank = 2)]
/// fn user_dashboard(user: User) { } /// fn user_dashboard(user: User) { }
/// # }
/// ``` /// ```
/// ///
/// When a non-admin user is logged in, the database will be queried twice: once /// When a non-admin user is logged in, the database will be queried twice: once
@ -285,10 +286,10 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// # #![feature(proc_macro_hygiene, decl_macro)] /// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #![feature(never_type)] /// # #![feature(never_type)]
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;
/// # /// # #[cfg(feature = "private-cookies")] mod inner {
/// # use rocket::outcome::{IntoOutcome, Outcome}; /// # use rocket::outcome::{IntoOutcome, Outcome};
/// # use rocket::request::{self, FromRequest, Request}; /// # use rocket::request::{self, FromRequest, Request};
/// # struct User { id: String, is_admin: bool }; /// # struct User { id: String, is_admin: bool }
/// # struct Database; /// # struct Database;
/// # impl Database { /// # impl Database {
/// # fn get_user(&self, id: String) -> Result<User, ()> { /// # fn get_user(&self, id: String) -> Result<User, ()> {
@ -302,7 +303,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// # } /// # }
/// # } /// # }
/// # /// #
/// # struct Admin<'a> { user: &'a User }; /// # struct Admin<'a> { user: &'a User }
/// # /// #
/// impl<'a, 'r> FromRequest<'a, 'r> for &'a User { /// impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
/// type Error = !; /// type Error = !;
@ -335,6 +336,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// } /// }
/// } /// }
/// } /// }
/// # }
/// ``` /// ```
/// ///
/// Notice that these request guards provide access to *borrowed* data (`&'a /// Notice that these request guards provide access to *borrowed* data (`&'a

View File

@ -290,10 +290,7 @@ impl<'r> Request<'r> {
pub fn cookies(&self) -> Cookies { pub fn cookies(&self) -> Cookies {
// FIXME: Can we do better? This is disappointing. // FIXME: Can we do better? This is disappointing.
match self.state.cookies.try_borrow_mut() { match self.state.cookies.try_borrow_mut() {
#[cfg(feature = "private-cookies")]
Ok(jar) => Cookies::new(jar, self.state.config.secret_key()), Ok(jar) => Cookies::new(jar, self.state.config.secret_key()),
#[cfg(not(feature = "private-cookies"))]
Ok(jar) => Cookies::new(jar, &()),
Err(_) => { Err(_) => {
error_!("Multiple `Cookies` instances are active at once."); error_!("Multiple `Cookies` instances are active at once.");
info_!("An instance of `Cookies` must be dropped before another \ info_!("An instance of `Cookies` must be dropped before another \

View File

@ -396,7 +396,6 @@ impl Rocket {
launch_info_!("port: {}", Paint::white(&config.port)); launch_info_!("port: {}", Paint::white(&config.port));
launch_info_!("log: {}", Paint::white(config.log_level)); launch_info_!("log: {}", Paint::white(config.log_level));
launch_info_!("workers: {}", Paint::white(config.workers)); launch_info_!("workers: {}", Paint::white(config.workers));
#[cfg(feature = "private-cookies")]
launch_info_!("secret key: {}", Paint::white(&config.secret_key)); launch_info_!("secret key: {}", Paint::white(&config.secret_key));
launch_info_!("limits: {}", Paint::white(&config.limits)); launch_info_!("limits: {}", Paint::white(&config.limits));
@ -415,10 +414,8 @@ impl Rocket {
launch_info_!("tls: {}", Paint::white("disabled")); launch_info_!("tls: {}", Paint::white("disabled"));
} }
#[cfg(feature = "private-cookies")] { if config.secret_key.is_generated() && config.environment.is_prod() {
if config.secret_key.is_generated() && config.environment.is_prod() { warn!("environment is 'production', but no `secret_key` is configured");
warn!("environment is 'production', but no `secret_key` is configured");
}
} }
for (name, value) in config.extras() { for (name, value) in config.extras() {

View File

@ -1,45 +1,48 @@
#![feature(proc_macro_hygiene, decl_macro)] #![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket; #[macro_use]
#[cfg(feature = "private-cookies")]
use rocket::http::Cookies; extern crate rocket;
#[cfg(feature = "private-cookies")] #[cfg(feature = "private-cookies")]
#[get("/")] mod private_cookie_test {
fn return_private_cookie(mut cookies: Cookies) -> Option<String> { use rocket::http::Cookies;
match cookies.get_private("cookie_name") {
Some(cookie) => Some(cookie.value().into()), #[get("/")]
None => None, fn return_private_cookie(mut cookies: Cookies) -> Option<String> {
} 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; mod tests {
use rocket::http::Cookie; use super::*;
use rocket::http::Status; use rocket::local::Client;
use rocket::http::Cookie;
#[test] use rocket::http::Status;
fn private_cookie_is_returned() {
let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); #[test]
fn private_cookie_is_returned() {
let client = Client::new(rocket).unwrap(); let rocket = rocket::ignite().mount("/", routes![return_private_cookie]);
let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value"));
let mut response = req.dispatch(); let client = Client::new(rocket).unwrap();
let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value"));
assert_eq!(response.body_string(), Some("cookie_value".into())); let mut response = req.dispatch();
assert_eq!(response.headers().get_one("Set-Cookie"), None);
} 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]); #[test]
fn regular_cookie_is_not_returned() {
let client = Client::new(rocket).unwrap(); let rocket = rocket::ignite().mount("/", routes![return_private_cookie]);
let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value"));
let response = req.dispatch(); let client = Client::new(rocket).unwrap();
let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value"));
assert_eq!(response.status(), Status::NotFound); let response = req.dispatch();
assert_eq!(response.status(), Status::NotFound);
}
} }
} }

View File

@ -110,18 +110,15 @@ if [ "$1" = "--contrib" ]; then
popd > /dev/null 2>&1 popd > /dev/null 2>&1
elif [ "$1" = "--core" ]; then elif [ "$1" = "--core" ]; then
FEATURES=( FEATURES=(
private-cookies private-cookies # this is already tested since it's the default feature
tls tls
) )
pushd "${CORE_ROOT}" > /dev/null 2>&1 pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1
echo ":: Building and testing core [no features]..." echo ":: Building and testing core [no features]..."
CARGO_INCREMENTAL=0 cargo test --no-default-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 for feature in "${FEATURES[@]}"; do
echo ":: Building and testing core [${feature}]..." echo ":: Building and testing core [${feature}]..."
CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}"

View File

@ -503,15 +503,18 @@ fn logout(mut cookies: Cookies) -> Flash<Redirect> {
[`Cookies::add()`]: @api/rocket/http/enum.Cookies.html#method.add [`Cookies::add()`]: @api/rocket/http/enum.Cookies.html#method.add
Private Cookies can be omitted at build time by excluding the feature Support for private cookies, which depends on the [`ring`] library, can be
`private-cookies`. You can do this by setting the `default-features` omitted at build time by disabling Rocket's default features, in-turn disabling
directive to `false` in your `Cargo.toml`: the default `private-cookies` feature. To do so, modify your `Cargo.toml` file
so that you depend on `rocket` as follows:
```toml ```toml
[dependencies.rocket] [dependencies]
default-features = false rocket = { version = "0.4.0-rc.1", default-features = false }
``` ```
[`ring`]: https://github.com/briansmith/ring
### Secret Key ### Secret Key
To encrypt private cookies, Rocket uses the 256-bit key specified in the To encrypt private cookies, Rocket uses the 256-bit key specified in the