Skip to content

Commit

Permalink
Merge branch 'master' into alternative-protobuf-implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
t2m4k1 committed Aug 14, 2022
2 parents 1780f7e + e61dbae commit d660ebf
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 107 deletions.
83 changes: 55 additions & 28 deletions actix-session/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! Configuration options to tune the behaviour of [`SessionMiddleware`].

use actix_web::cookie::{time::Duration, Key, SameSite};
use derive_more::From;

use crate::{storage::SessionStore, SessionMiddleware};

/// Determines what type of session cookie should be used and how its lifecycle should be managed.
///
/// Used by [`SessionMiddlewareBuilder::session_lifecycle`].
#[derive(Debug, Clone)]
#[derive(Debug, Clone, From)]
#[non_exhaustive]
pub enum SessionLifecycle {
/// The session cookie will expire when the current browser session ends.
Expand All @@ -27,25 +28,16 @@ pub enum SessionLifecycle {
PersistentSession(PersistentSession),
}

impl From<BrowserSession> for SessionLifecycle {
fn from(session: BrowserSession) -> Self {
Self::BrowserSession(session)
}
}

impl From<PersistentSession> for SessionLifecycle {
fn from(session: PersistentSession) -> Self {
Self::PersistentSession(session)
}
}

/// A [session lifecycle](SessionLifecycle) strategy where the session cookie expires when the
/// browser's current session ends.
///
/// When does a browser session end? It depends on the browser. Chrome, for example, will often
/// continue running in the background when the browser is closed—session cookies are not deleted
/// and they will still be available when the browser is opened again. Check the documentation of
/// the browsers you are targeting for up-to-date information.
///
/// Due to its `Into<SessionLifecycle>` implementation, a `BrowserSession` can be passed directly
/// to [`SessionMiddlewareBuilder::session_lifecycle()`].
#[derive(Debug, Clone)]
pub struct BrowserSession {
state_ttl: Duration,
Expand Down Expand Up @@ -103,6 +95,26 @@ impl Default for BrowserSession {
/// Persistent cookies have a pre-determined expiration, specified via the `Max-Age` or `Expires`
/// attribute. They do not disappear when the current browser session ends.
///
/// Due to its `Into<SessionLifecycle>` implementation, a `PersistentSession` can be passed directly
/// to [`SessionMiddlewareBuilder::session_lifecycle()`].
///
/// # Examples
/// ```
/// use actix_web::cookie::time::Duration;
/// use actix_session::SessionMiddleware;
/// use actix_session::config::{PersistentSession, TtlExtensionPolicy};
///
/// const SECS_IN_WEEK: i64 = 60 * 60 * 24 * 7;
///
/// // a session lifecycle with a time-to-live (expiry) of 1 week and default extension policy
/// PersistentSession::default().session_ttl(Duration::seconds(SECS_IN_WEEK));
///
/// // a session lifecycle with the default time-to-live (expiry) and a custom extension policy
/// PersistentSession::default()
/// // this policy causes the session state's TTL to be refreshed on every request
/// .session_ttl_extension_policy(TtlExtensionPolicy::OnEveryRequest);
/// ```
///
/// [persistent]: https://www.whitehatsec.com/glossary/content/persistent-session-cookie
#[derive(Debug, Clone)]
pub struct PersistentSession {
Expand All @@ -113,10 +125,10 @@ pub struct PersistentSession {
impl PersistentSession {
/// Specifies how long the session cookie should live.
///
/// Defaults to 1 day if left unspecified.
///
/// The session TTL is also used as the TTL for the session state in the storage backend.
///
/// Defaults to 1 day.
///
/// A persistent session can live more than the specified TTL if the TTL is extended.
/// See [`session_ttl_extension_policy`](Self::session_ttl_extension_policy) for more details.
pub fn session_ttl(mut self, session_ttl: Duration) -> Self {
Expand All @@ -127,7 +139,7 @@ impl PersistentSession {
/// Determines under what circumstances the TTL of your session should be extended.
/// See [`TtlExtensionPolicy`] for more details.
///
/// Defaults to [`TtlExtensionPolicy::OnStateChanges`] if left unspecified.
/// Defaults to [`TtlExtensionPolicy::OnStateChanges`].
pub fn session_ttl_extension_policy(
mut self,
ttl_extension_policy: TtlExtensionPolicy,
Expand All @@ -148,23 +160,23 @@ impl Default for PersistentSession {

/// Configuration for which events should trigger an extension of the time-to-live for your session.
///
/// If you are using a [`BrowserSession`], `TtlExtensionPolicy` controls how often the TTL of
/// the session state should be refreshed. The browser is in control of the lifecycle of the
/// session cookie.
/// If you are using a [`BrowserSession`], `TtlExtensionPolicy` controls how often the TTL of the
/// session state should be refreshed. The browser is in control of the lifecycle of the session
/// cookie.
///
/// If you are using a [`PersistentSession`], `TtlExtensionPolicy` controls both the expiration
/// of the session cookie and the TTL of the session state.
/// If you are using a [`PersistentSession`], `TtlExtensionPolicy` controls both the expiration of
/// the session cookie and the TTL of the session state on the storage backend.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TtlExtensionPolicy {
/// The TTL is refreshed every time the server receives a request associated with a session.
///
/// # Performance impact
/// Refreshing the TTL on every request is not free.
/// It implies a refresh of the TTL on the session state. This translates into a request over
/// the network if you are using a remote system as storage backend (e.g. Redis).
/// This impacts both the total load on your storage backend (i.e. number of
/// queries it has to handle) and the latency of the requests served by your server.
/// Refreshing the TTL on every request is not free. It implies a refresh of the TTL on the
/// session state. This translates into a request over the network if you are using a remote
/// system as storage backend (e.g. Redis). This impacts both the total load on your storage
/// backend (i.e. number of queries it has to handle) and the latency of the requests served by
/// your server.
OnEveryRequest,

/// The TTL is refreshed every time the session state changes or the session key is renewed.
Expand Down Expand Up @@ -197,8 +209,7 @@ pub(crate) const fn default_ttl_extension_policy() -> TtlExtensionPolicy {
TtlExtensionPolicy::OnStateChanges
}

/// A fluent builder to construct a [`SessionMiddleware`] instance with custom configuration
/// parameters.
/// A fluent, customized [`SessionMiddleware`] builder.
#[must_use]
pub struct SessionMiddlewareBuilder<Store: SessionStore> {
storage_backend: Store,
Expand Down Expand Up @@ -236,6 +247,22 @@ impl<Store: SessionStore> SessionMiddlewareBuilder<Store> {
/// Check out [`SessionLifecycle`]'s documentation for more details on the available options.
///
/// Default is [`SessionLifecycle::BrowserSession`].
///
/// # Examples
/// ```
/// use actix_web::cookie::{Key, time::Duration};
/// use actix_session::{SessionMiddleware, config::PersistentSession};
/// use actix_session::storage::CookieSessionStore;
///
/// const SECS_IN_WEEK: i64 = 60 * 60 * 24 * 7;
///
/// // creates a session middleware with a time-to-live (expiry) of 1 week
/// SessionMiddleware::builder(CookieSessionStore::default(), Key::from(&[0; 64]))
/// .session_lifecycle(
/// PersistentSession::default().session_ttl(Duration::seconds(SECS_IN_WEEK))
/// )
/// .build();
/// ```
pub fn session_lifecycle<S: Into<SessionLifecycle>>(mut self, session_lifecycle: S) -> Self {
match session_lifecycle.into() {
SessionLifecycle::BrowserSession(BrowserSession {
Expand Down
24 changes: 12 additions & 12 deletions actix-session/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ use crate::{
/// [`SessionStore`]);
/// - a secret key, to sign or encrypt the content of client-side session cookie.
///
/// # How did we choose defaults?
/// You should not regret adding `actix-session` to your dependencies and going to production using
/// the default configuration. That is why, when in doubt, we opt to use the most secure option for
/// each configuration parameter.
///
/// We expose knobs to change the default to suit your needs—i.e., if you know what you are doing,
/// we will not stop you. But being a subject-matter expert should not be a requirement to deploy
/// reasonably secure implementation of sessions.
///
/// # Examples
/// ```no_run
/// use actix_web::{web, App, HttpServer, HttpResponse, Error};
/// use actix_session::{Session, SessionMiddleware, storage::RedisActorSessionStore};
Expand Down Expand Up @@ -103,16 +113,6 @@ use crate::{
/// .await
/// }
/// ```
///
/// ## How did we choose defaults?
///
/// You should not regret adding `actix-session` to your dependencies and going to production using
/// the default configuration. That is why, when in doubt, we opt to use the most secure option for
/// each configuration parameter.
///
/// We expose knobs to change the default to suit your needs—i.e., if you know what you are doing,
/// we will not stop you. But being a subject-matter expert should not be a requirement to deploy
/// reasonably secure implementation of sessions.
#[derive(Clone)]
pub struct SessionMiddleware<Store: SessionStore> {
storage_backend: Rc<Store>,
Expand All @@ -125,7 +125,7 @@ impl<Store: SessionStore> SessionMiddleware<Store> {
///
/// To create a new instance of [`SessionMiddleware`] you need to provide:
/// - an instance of the session storage backend you wish to use (i.e. an implementation of
/// [`SessionStore]);
/// [`SessionStore`]);
/// - a secret key, to sign or encrypt the content of client-side session cookie.
pub fn new(store: Store, key: Key) -> Self {
Self::builder(store, key).build()
Expand All @@ -135,7 +135,7 @@ impl<Store: SessionStore> SessionMiddleware<Store> {
///
/// It takes as input the two required inputs to create a new instance of [`SessionMiddleware`]:
/// - an instance of the session storage backend you wish to use (i.e. an implementation of
/// [`SessionStore]);
/// [`SessionStore`]);
/// - a secret key, to sign or encrypt the content of client-side session cookie.
pub fn builder(store: Store, key: Key) -> SessionMiddlewareBuilder<Store> {
SessionMiddlewareBuilder::new(store, config::default_configuration(key))
Expand Down
4 changes: 4 additions & 0 deletions actix-settings/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

## Unreleased - 2022-xx-xx

- Rename `AtError => Error`.
- Remove `AtResult` type alias.

## 0.6.0 - 2022-07-31

- Update Actix Web dependencies to v4 ecosystem.
- Rename `actix.ssl` settings object to `actix.tls`.
- `NoSettings` is now marked `#[non_exhaustive]`.

## 0.5.2 - 2022-07-31

- Adopted into @actix org from <https://github.com/jjpe/actix-settings>.
39 changes: 18 additions & 21 deletions actix-settings/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ use std::{env::VarError, io, num::ParseIntError, path::PathBuf, str::ParseBoolEr

use toml::de::Error as TomlError;

/// Convenience type alias for `Result<T, AtError>`.
pub type AtResult<T> = std::result::Result<T, AtError>;

/// Errors that can be returned from methods in this crate.
#[derive(Debug, Clone)]
pub enum AtError {
pub enum Error {
/// Environment variable does not exists or is invalid.
EnvVarError(VarError),

Expand Down Expand Up @@ -42,7 +39,7 @@ pub enum AtError {

macro_rules! InvalidValue {
(expected: $expected:expr, got: $got:expr,) => {
crate::AtError::InvalidValue {
crate::Error::InvalidValue {
expected: $expected,
got: $got.to_string(),
file: file!(),
Expand All @@ -52,56 +49,56 @@ macro_rules! InvalidValue {
};
}

impl From<io::Error> for AtError {
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::IoError(ioe::IoError::from(err))
}
}

impl From<ioe::IoError> for AtError {
impl From<ioe::IoError> for Error {
fn from(err: ioe::IoError) -> Self {
Self::IoError(err)
}
}

impl From<ParseBoolError> for AtError {
impl From<ParseBoolError> for Error {
fn from(err: ParseBoolError) -> Self {
Self::ParseBoolError(err)
}
}

impl From<ParseIntError> for AtError {
impl From<ParseIntError> for Error {
fn from(err: ParseIntError) -> Self {
Self::ParseIntError(err)
}
}

impl From<TomlError> for AtError {
impl From<TomlError> for Error {
fn from(err: TomlError) -> Self {
Self::TomlError(err)
}
}

impl From<VarError> for AtError {
impl From<VarError> for Error {
fn from(err: VarError) -> Self {
Self::EnvVarError(err)
}
}

impl From<AtError> for io::Error {
fn from(err: AtError) -> Self {
impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err {
AtError::EnvVarError(var_error) => {
Error::EnvVarError(var_error) => {
let msg = format!("Env var error: {}", var_error);
io::Error::new(io::ErrorKind::InvalidInput, msg)
}

AtError::FileExists(path_buf) => {
Error::FileExists(path_buf) => {
let msg = format!("File exists: {}", path_buf.display());
io::Error::new(io::ErrorKind::AlreadyExists, msg)
}

AtError::InvalidValue {
Error::InvalidValue {
expected,
ref got,
file,
Expand All @@ -115,24 +112,24 @@ impl From<AtError> for io::Error {
io::Error::new(io::ErrorKind::InvalidInput, msg)
}

AtError::IoError(io_error) => io_error.into(),
Error::IoError(io_error) => io_error.into(),

AtError::ParseBoolError(parse_bool_error) => {
Error::ParseBoolError(parse_bool_error) => {
let msg = format!("Failed to parse boolean: {}", parse_bool_error);
io::Error::new(io::ErrorKind::InvalidInput, msg)
}

AtError::ParseIntError(parse_int_error) => {
Error::ParseIntError(parse_int_error) => {
let msg = format!("Failed to parse integer: {}", parse_int_error);
io::Error::new(io::ErrorKind::InvalidInput, msg)
}

AtError::ParseAddressError(string) => {
Error::ParseAddressError(string) => {
let msg = format!("Failed to parse address: {}", string);
io::Error::new(io::ErrorKind::InvalidInput, msg)
}

AtError::TomlError(toml_error) => {
Error::TomlError(toml_error) => {
let msg = format!("TOML error: {}", toml_error);
io::Error::new(io::ErrorKind::InvalidInput, msg)
}
Expand Down
Loading

0 comments on commit d660ebf

Please sign in to comment.