From 134863d5c828fad574d75fa40347a7cfc379a5b4 Mon Sep 17 00:00:00 2001
From: Nikolay Kim <fafhrd91@gmail.com>
Date: Sat, 9 Mar 2019 18:04:40 -0800
Subject: [PATCH] move middlewares

---
 errhandlers.rs | 141 +++++++++++++++++
 identity.rs    | 399 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 540 insertions(+)
 create mode 100644 errhandlers.rs
 create mode 100644 identity.rs

diff --git a/errhandlers.rs b/errhandlers.rs
new file mode 100644
index 00000000..c7d19d33
--- /dev/null
+++ b/errhandlers.rs
@@ -0,0 +1,141 @@
+use std::collections::HashMap;
+
+use error::Result;
+use http::StatusCode;
+use httprequest::HttpRequest;
+use httpresponse::HttpResponse;
+use middleware::{Middleware, Response};
+
+type ErrorHandler<S> = Fn(&HttpRequest<S>, HttpResponse) -> Result<Response>;
+
+/// `Middleware` for allowing custom handlers for responses.
+///
+/// You can use `ErrorHandlers::handler()` method  to register a custom error
+/// handler for specific status code. You can modify existing response or
+/// create completely new one.
+///
+/// ## Example
+///
+/// ```rust
+/// # extern crate actix_web;
+/// use actix_web::middleware::{ErrorHandlers, Response};
+/// use actix_web::{http, App, HttpRequest, HttpResponse, Result};
+///
+/// fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
+///     let mut builder = resp.into_builder();
+///     builder.header(http::header::CONTENT_TYPE, "application/json");
+///     Ok(Response::Done(builder.into()))
+/// }
+///
+/// fn main() {
+///     let app = App::new()
+///         .middleware(
+///             ErrorHandlers::new()
+///                 .handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500),
+///         )
+///         .resource("/test", |r| {
+///             r.method(http::Method::GET).f(|_| HttpResponse::Ok());
+///             r.method(http::Method::HEAD)
+///                 .f(|_| HttpResponse::MethodNotAllowed());
+///         })
+///         .finish();
+/// }
+/// ```
+pub struct ErrorHandlers<S> {
+    handlers: HashMap<StatusCode, Box<ErrorHandler<S>>>,
+}
+
+impl<S> Default for ErrorHandlers<S> {
+    fn default() -> Self {
+        ErrorHandlers {
+            handlers: HashMap::new(),
+        }
+    }
+}
+
+impl<S> ErrorHandlers<S> {
+    /// Construct new `ErrorHandlers` instance
+    pub fn new() -> Self {
+        ErrorHandlers::default()
+    }
+
+    /// Register error handler for specified status code
+    pub fn handler<F>(mut self, status: StatusCode, handler: F) -> Self
+    where
+        F: Fn(&HttpRequest<S>, HttpResponse) -> Result<Response> + 'static,
+    {
+        self.handlers.insert(status, Box::new(handler));
+        self
+    }
+}
+
+impl<S: 'static> Middleware<S> for ErrorHandlers<S> {
+    fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
+        if let Some(handler) = self.handlers.get(&resp.status()) {
+            handler(req, resp)
+        } else {
+            Ok(Response::Done(resp))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use error::{Error, ErrorInternalServerError};
+    use http::header::CONTENT_TYPE;
+    use http::StatusCode;
+    use httpmessage::HttpMessage;
+    use middleware::Started;
+    use test::{self, TestRequest};
+
+    fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
+        let mut builder = resp.into_builder();
+        builder.header(CONTENT_TYPE, "0001");
+        Ok(Response::Done(builder.into()))
+    }
+
+    #[test]
+    fn test_handler() {
+        let mw =
+            ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
+
+        let mut req = TestRequest::default().finish();
+        let resp = HttpResponse::InternalServerError().finish();
+        let resp = match mw.response(&mut req, resp) {
+            Ok(Response::Done(resp)) => resp,
+            _ => panic!(),
+        };
+        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
+
+        let resp = HttpResponse::Ok().finish();
+        let resp = match mw.response(&mut req, resp) {
+            Ok(Response::Done(resp)) => resp,
+            _ => panic!(),
+        };
+        assert!(!resp.headers().contains_key(CONTENT_TYPE));
+    }
+
+    struct MiddlewareOne;
+
+    impl<S> Middleware<S> for MiddlewareOne {
+        fn start(&self, _: &HttpRequest<S>) -> Result<Started, Error> {
+            Err(ErrorInternalServerError("middleware error"))
+        }
+    }
+
+    #[test]
+    fn test_middleware_start_error() {
+        let mut srv = test::TestServer::new(move |app| {
+            app.middleware(
+                ErrorHandlers::new()
+                    .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500),
+            ).middleware(MiddlewareOne)
+            .handler(|_| HttpResponse::Ok())
+        });
+
+        let request = srv.get().finish().unwrap();
+        let response = srv.execute(request.send()).unwrap();
+        assert_eq!(response.headers().get(CONTENT_TYPE).unwrap(), "0001");
+    }
+}
diff --git a/identity.rs b/identity.rs
new file mode 100644
index 00000000..a664ba1f
--- /dev/null
+++ b/identity.rs
@@ -0,0 +1,399 @@
+//! Request identity service for Actix applications.
+//!
+//! [**IdentityService**](struct.IdentityService.html) middleware can be
+//! used with different policies types to store identity information.
+//!
+//! By default, only cookie identity policy is implemented. Other backend
+//! implementations can be added separately.
+//!
+//! [**CookieIdentityPolicy**](struct.CookieIdentityPolicy.html)
+//! uses cookies as identity storage.
+//!
+//! To access current request identity
+//! [**RequestIdentity**](trait.RequestIdentity.html) should be used.
+//! *HttpRequest* implements *RequestIdentity* trait.
+//!
+//! ```rust
+//! use actix_web::middleware::identity::RequestIdentity;
+//! use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
+//! use actix_web::*;
+//!
+//! fn index(req: HttpRequest) -> Result<String> {
+//!     // access request identity
+//!     if let Some(id) = req.identity() {
+//!         Ok(format!("Welcome! {}", id))
+//!     } else {
+//!         Ok("Welcome Anonymous!".to_owned())
+//!     }
+//! }
+//!
+//! fn login(mut req: HttpRequest) -> HttpResponse {
+//!     req.remember("User1".to_owned()); // <- remember identity
+//!     HttpResponse::Ok().finish()
+//! }
+//!
+//! fn logout(mut req: HttpRequest) -> HttpResponse {
+//!     req.forget(); // <- remove identity
+//!     HttpResponse::Ok().finish()
+//! }
+//!
+//! fn main() {
+//!     let app = App::new().middleware(IdentityService::new(
+//!         // <- create identity middleware
+//!         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
+//!               .name("auth-cookie")
+//!               .secure(false),
+//!     ));
+//! }
+//! ```
+use std::rc::Rc;
+
+use cookie::{Cookie, CookieJar, Key, SameSite};
+use futures::future::{err as FutErr, ok as FutOk, FutureResult};
+use futures::Future;
+use time::Duration;
+
+use error::{Error, Result};
+use http::header::{self, HeaderValue};
+use httprequest::HttpRequest;
+use httpresponse::HttpResponse;
+use middleware::{Middleware, Response, Started};
+
+/// The helper trait to obtain your identity from a request.
+///
+/// ```rust
+/// use actix_web::middleware::identity::RequestIdentity;
+/// use actix_web::*;
+///
+/// fn index(req: HttpRequest) -> Result<String> {
+///     // access request identity
+///     if let Some(id) = req.identity() {
+///         Ok(format!("Welcome! {}", id))
+///     } else {
+///         Ok("Welcome Anonymous!".to_owned())
+///     }
+/// }
+///
+/// fn login(mut req: HttpRequest) -> HttpResponse {
+///     req.remember("User1".to_owned()); // <- remember identity
+///     HttpResponse::Ok().finish()
+/// }
+///
+/// fn logout(mut req: HttpRequest) -> HttpResponse {
+///     req.forget(); // <- remove identity
+///     HttpResponse::Ok().finish()
+/// }
+/// # fn main() {}
+/// ```
+pub trait RequestIdentity {
+    /// Return the claimed identity of the user associated request or
+    /// ``None`` if no identity can be found associated with the request.
+    fn identity(&self) -> Option<String>;
+
+    /// Remember identity.
+    fn remember(&self, identity: String);
+
+    /// This method is used to 'forget' the current identity on subsequent
+    /// requests.
+    fn forget(&self);
+}
+
+impl<S> RequestIdentity for HttpRequest<S> {
+    fn identity(&self) -> Option<String> {
+        if let Some(id) = self.extensions().get::<IdentityBox>() {
+            return id.0.identity().map(|s| s.to_owned());
+        }
+        None
+    }
+
+    fn remember(&self, identity: String) {
+        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
+            return id.0.as_mut().remember(identity);
+        }
+    }
+
+    fn forget(&self) {
+        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
+            return id.0.forget();
+        }
+    }
+}
+
+/// An identity
+pub trait Identity: 'static {
+    /// Return the claimed identity of the user associated request or
+    /// ``None`` if no identity can be found associated with the request.
+    fn identity(&self) -> Option<&str>;
+
+    /// Remember identity.
+    fn remember(&mut self, key: String);
+
+    /// This method is used to 'forget' the current identity on subsequent
+    /// requests.
+    fn forget(&mut self);
+
+    /// Write session to storage backend.
+    fn write(&mut self, resp: HttpResponse) -> Result<Response>;
+}
+
+/// Identity policy definition.
+pub trait IdentityPolicy<S>: Sized + 'static {
+    /// The associated identity
+    type Identity: Identity;
+
+    /// The return type of the middleware
+    type Future: Future<Item = Self::Identity, Error = Error>;
+
+    /// Parse the session from request and load data from a service identity.
+    fn from_request(&self, request: &HttpRequest<S>) -> Self::Future;
+}
+
+/// Request identity middleware
+///
+/// ```rust
+/// # extern crate actix_web;
+/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
+/// use actix_web::App;
+///
+/// fn main() {
+///     let app = App::new().middleware(IdentityService::new(
+///         // <- create identity middleware
+///         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
+///               .name("auth-cookie")
+///               .secure(false),
+///     ));
+/// }
+/// ```
+pub struct IdentityService<T> {
+    backend: T,
+}
+
+impl<T> IdentityService<T> {
+    /// Create new identity service with specified backend.
+    pub fn new(backend: T) -> Self {
+        IdentityService { backend }
+    }
+}
+
+struct IdentityBox(Box<Identity>);
+
+impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
+    fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
+        let req = req.clone();
+        let fut = self.backend.from_request(&req).then(move |res| match res {
+            Ok(id) => {
+                req.extensions_mut().insert(IdentityBox(Box::new(id)));
+                FutOk(None)
+            }
+            Err(err) => FutErr(err),
+        });
+        Ok(Started::Future(Box::new(fut)))
+    }
+
+    fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
+        if let Some(ref mut id) = req.extensions_mut().get_mut::<IdentityBox>() {
+            id.0.as_mut().write(resp)
+        } else {
+            Ok(Response::Done(resp))
+        }
+    }
+}
+
+#[doc(hidden)]
+/// Identity that uses private cookies as identity storage.
+pub struct CookieIdentity {
+    changed: bool,
+    identity: Option<String>,
+    inner: Rc<CookieIdentityInner>,
+}
+
+impl Identity for CookieIdentity {
+    fn identity(&self) -> Option<&str> {
+        self.identity.as_ref().map(|s| s.as_ref())
+    }
+
+    fn remember(&mut self, value: String) {
+        self.changed = true;
+        self.identity = Some(value);
+    }
+
+    fn forget(&mut self) {
+        self.changed = true;
+        self.identity = None;
+    }
+
+    fn write(&mut self, mut resp: HttpResponse) -> Result<Response> {
+        if self.changed {
+            let _ = self.inner.set_cookie(&mut resp, self.identity.take());
+        }
+        Ok(Response::Done(resp))
+    }
+}
+
+struct CookieIdentityInner {
+    key: Key,
+    name: String,
+    path: String,
+    domain: Option<String>,
+    secure: bool,
+    max_age: Option<Duration>,
+    same_site: Option<SameSite>,
+}
+
+impl CookieIdentityInner {
+    fn new(key: &[u8]) -> CookieIdentityInner {
+        CookieIdentityInner {
+            key: Key::from_master(key),
+            name: "actix-identity".to_owned(),
+            path: "/".to_owned(),
+            domain: None,
+            secure: true,
+            max_age: None,
+            same_site: None,
+        }
+    }
+
+    fn set_cookie(&self, resp: &mut HttpResponse, id: Option<String>) -> Result<()> {
+        let some = id.is_some();
+        {
+            let id = id.unwrap_or_else(String::new);
+            let mut cookie = Cookie::new(self.name.clone(), id);
+            cookie.set_path(self.path.clone());
+            cookie.set_secure(self.secure);
+            cookie.set_http_only(true);
+
+            if let Some(ref domain) = self.domain {
+                cookie.set_domain(domain.clone());
+            }
+
+            if let Some(max_age) = self.max_age {
+                cookie.set_max_age(max_age);
+            }
+
+            if let Some(same_site) = self.same_site {
+                cookie.set_same_site(same_site);
+            }
+
+            let mut jar = CookieJar::new();
+            if some {
+                jar.private(&self.key).add(cookie);
+            } else {
+                jar.add_original(cookie.clone());
+                jar.private(&self.key).remove(cookie);
+            }
+
+            for cookie in jar.delta() {
+                let val = HeaderValue::from_str(&cookie.to_string())?;
+                resp.headers_mut().append(header::SET_COOKIE, val);
+            }
+        }
+
+        Ok(())
+    }
+
+    fn load<S>(&self, req: &HttpRequest<S>) -> Option<String> {
+        if let Ok(cookies) = req.cookies() {
+            for cookie in cookies.iter() {
+                if cookie.name() == self.name {
+                    let mut jar = CookieJar::new();
+                    jar.add_original(cookie.clone());
+
+                    let cookie_opt = jar.private(&self.key).get(&self.name);
+                    if let Some(cookie) = cookie_opt {
+                        return Some(cookie.value().into());
+                    }
+                }
+            }
+        }
+        None
+    }
+}
+
+/// Use cookies for request identity storage.
+///
+/// The constructors take a key as an argument.
+/// This is the private key for cookie - when this value is changed,
+/// all identities are lost. The constructors will panic if the key is less
+/// than 32 bytes in length.
+///
+/// # Example
+///
+/// ```rust
+/// # extern crate actix_web;
+/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
+/// use actix_web::App;
+///
+/// fn main() {
+///     let app = App::new().middleware(IdentityService::new(
+///         // <- create identity middleware
+///         CookieIdentityPolicy::new(&[0; 32])  // <- construct cookie policy
+///                .domain("www.rust-lang.org")
+///                .name("actix_auth")
+///                .path("/")
+///                .secure(true),
+///     ));
+/// }
+/// ```
+pub struct CookieIdentityPolicy(Rc<CookieIdentityInner>);
+
+impl CookieIdentityPolicy {
+    /// Construct new `CookieIdentityPolicy` instance.
+    ///
+    /// Panics if key length is less than 32 bytes.
+    pub fn new(key: &[u8]) -> CookieIdentityPolicy {
+        CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key)))
+    }
+
+    /// Sets the `path` field in the session cookie being built.
+    pub fn path<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
+        Rc::get_mut(&mut self.0).unwrap().path = value.into();
+        self
+    }
+
+    /// Sets the `name` field in the session cookie being built.
+    pub fn name<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
+        Rc::get_mut(&mut self.0).unwrap().name = value.into();
+        self
+    }
+
+    /// Sets the `domain` field in the session cookie being built.
+    pub fn domain<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
+        Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into());
+        self
+    }
+
+    /// Sets the `secure` field in the session cookie being built.
+    ///
+    /// If the `secure` field is set, a cookie will only be transmitted when the
+    /// connection is secure - i.e. `https`
+    pub fn secure(mut self, value: bool) -> CookieIdentityPolicy {
+        Rc::get_mut(&mut self.0).unwrap().secure = value;
+        self
+    }
+
+    /// Sets the `max-age` field in the session cookie being built.
+    pub fn max_age(mut self, value: Duration) -> CookieIdentityPolicy {
+        Rc::get_mut(&mut self.0).unwrap().max_age = Some(value);
+        self
+    }
+
+    /// Sets the `same_site` field in the session cookie being built.
+    pub fn same_site(mut self, same_site: SameSite) -> Self {
+        Rc::get_mut(&mut self.0).unwrap().same_site = Some(same_site);
+        self
+    }
+}
+
+impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
+    type Identity = CookieIdentity;
+    type Future = FutureResult<CookieIdentity, Error>;
+
+    fn from_request(&self, req: &HttpRequest<S>) -> Self::Future {
+        let identity = self.0.load(req);
+        FutOk(CookieIdentity {
+            identity,
+            changed: false,
+            inner: Rc::clone(&self.0),
+        })
+    }
+}