mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-28 01:32:57 +01:00
port identity middleware
This commit is contained in:
parent
134863d5c8
commit
12f0c78091
@ -33,10 +33,10 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["ssl", "tls", "rust-tls"] #, "session"]
|
features = ["ssl", "tls", "rust-tls", "session"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["brotli", "flate2-c"]
|
default = ["brotli", "flate2-c", "session"]
|
||||||
|
|
||||||
# brotli encoding, requires c compiler
|
# brotli encoding, requires c compiler
|
||||||
brotli = ["brotli2"]
|
brotli = ["brotli2"]
|
||||||
@ -48,7 +48,7 @@ flate2-c = ["flate2/miniz-sys"]
|
|||||||
flate2-rust = ["flate2/rust_backend"]
|
flate2-rust = ["flate2/rust_backend"]
|
||||||
|
|
||||||
# sessions feature, session require "ring" crate and c compiler
|
# sessions feature, session require "ring" crate and c compiler
|
||||||
# session = ["actix-session"]
|
session = ["cookie/secure"]
|
||||||
|
|
||||||
# tls
|
# tls
|
||||||
tls = ["native-tls", "actix-server/ssl"]
|
tls = ["native-tls", "actix-server/ssl"]
|
||||||
@ -72,6 +72,7 @@ actix-server = { git = "https://github.com/actix/actix-net.git" }
|
|||||||
actix-server-config = { git = "https://github.com/actix/actix-net.git" }
|
actix-server-config = { git = "https://github.com/actix/actix-net.git" }
|
||||||
|
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
|
cookie = { version="0.11", features=["percent-encode"] }
|
||||||
derive_more = "0.14"
|
derive_more = "0.14"
|
||||||
encoding = "0.2"
|
encoding = "0.2"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
|
141
errhandlers.rs
141
errhandlers.rs
@ -1,141 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,26 +14,26 @@
|
|||||||
//! *HttpRequest* implements *RequestIdentity* trait.
|
//! *HttpRequest* implements *RequestIdentity* trait.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use actix_web::middleware::identity::RequestIdentity;
|
//! use actix_web::middleware::identity::Identity;
|
||||||
//! use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
|
//! use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
|
||||||
//! use actix_web::*;
|
//! use actix_web::*;
|
||||||
//!
|
//!
|
||||||
//! fn index(req: HttpRequest) -> Result<String> {
|
//! fn index(id: Identity) -> String {
|
||||||
//! // access request identity
|
//! // access request identity
|
||||||
//! if let Some(id) = req.identity() {
|
//! if let Some(id) = id.identity() {
|
||||||
//! Ok(format!("Welcome! {}", id))
|
//! format!("Welcome! {}", id)
|
||||||
//! } else {
|
//! } else {
|
||||||
//! Ok("Welcome Anonymous!".to_owned())
|
//! "Welcome Anonymous!".to_owned()
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn login(mut req: HttpRequest) -> HttpResponse {
|
//! fn login(id: Idenity) -> HttpResponse {
|
||||||
//! req.remember("User1".to_owned()); // <- remember identity
|
//! id.remember("User1".to_owned()); // <- remember identity
|
||||||
//! HttpResponse::Ok().finish()
|
//! HttpResponse::Ok().finish()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn logout(mut req: HttpRequest) -> HttpResponse {
|
//! fn logout(id: Identity) -> HttpResponse {
|
||||||
//! req.forget(); // <- remove identity
|
//! id.forget(); // <- remove identity
|
||||||
//! HttpResponse::Ok().finish()
|
//! HttpResponse::Ok().finish()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
@ -42,118 +42,144 @@
|
|||||||
//! // <- create identity middleware
|
//! // <- create identity middleware
|
||||||
//! CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend
|
//! CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend
|
||||||
//! .name("auth-cookie")
|
//! .name("auth-cookie")
|
||||||
//! .secure(false),
|
//! .secure(false))
|
||||||
|
//! .service(web::resource("/index.html").to(index)
|
||||||
|
//! .service(web::resource("/login.html").to(login)
|
||||||
|
//! .service(web::resource("/logout.html").to(logout)
|
||||||
//! ));
|
//! ));
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use actix_service::{Service, Transform};
|
||||||
use cookie::{Cookie, CookieJar, Key, SameSite};
|
use cookie::{Cookie, CookieJar, Key, SameSite};
|
||||||
use futures::future::{err as FutErr, ok as FutOk, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::Future;
|
use futures::{Future, IntoFuture, Poll};
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use http::header::{self, HeaderValue};
|
use crate::http::header::{self, HeaderValue};
|
||||||
use httprequest::HttpRequest;
|
use crate::request::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse};
|
||||||
use middleware::{Middleware, Response, Started};
|
use crate::FromRequest;
|
||||||
|
use crate::HttpMessage;
|
||||||
|
|
||||||
/// The helper trait to obtain your identity from a request.
|
/// The extractor type to obtain your identity from a request.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::middleware::identity::RequestIdentity;
|
|
||||||
/// use actix_web::*;
|
/// use actix_web::*;
|
||||||
|
/// use actix_web::middleware::identity::Identity;
|
||||||
///
|
///
|
||||||
/// fn index(req: HttpRequest) -> Result<String> {
|
/// fn index(id: Identity) -> Result<String> {
|
||||||
/// // access request identity
|
/// // access request identity
|
||||||
/// if let Some(id) = req.identity() {
|
/// if let Some(id) = id.identity() {
|
||||||
/// Ok(format!("Welcome! {}", id))
|
/// Ok(format!("Welcome! {}", id))
|
||||||
/// } else {
|
/// } else {
|
||||||
/// Ok("Welcome Anonymous!".to_owned())
|
/// Ok("Welcome Anonymous!".to_owned())
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn login(mut req: HttpRequest) -> HttpResponse {
|
/// fn login(id: Identity) -> HttpResponse {
|
||||||
/// req.remember("User1".to_owned()); // <- remember identity
|
/// id.remember("User1".to_owned()); // <- remember identity
|
||||||
/// HttpResponse::Ok().finish()
|
/// HttpResponse::Ok().finish()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn logout(mut req: HttpRequest) -> HttpResponse {
|
/// fn logout(id: Identity) -> HttpResponse {
|
||||||
/// req.forget(); // <- remove identity
|
/// id.forget(); // <- remove identity
|
||||||
/// HttpResponse::Ok().finish()
|
/// HttpResponse::Ok().finish()
|
||||||
/// }
|
/// }
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
pub trait RequestIdentity {
|
#[derive(Clone)]
|
||||||
|
pub struct Identity(HttpRequest);
|
||||||
|
|
||||||
|
impl Identity {
|
||||||
/// Return the claimed identity of the user associated request or
|
/// Return the claimed identity of the user associated request or
|
||||||
/// ``None`` if no identity can be found associated with the request.
|
/// ``None`` if no identity can be found associated with the request.
|
||||||
fn identity(&self) -> Option<String>;
|
pub fn identity(&self) -> Option<String> {
|
||||||
|
if let Some(id) = self.0.extensions().get::<IdentityItem>() {
|
||||||
/// Remember identity.
|
id.id.clone()
|
||||||
fn remember(&self, identity: String);
|
} else {
|
||||||
|
|
||||||
/// 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
|
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.
|
/// Remember identity.
|
||||||
fn remember(&mut self, key: String);
|
pub fn remember(&self, identity: String) {
|
||||||
|
if let Some(id) = self.0.extensions_mut().get_mut::<IdentityItem>() {
|
||||||
|
id.id = Some(identity);
|
||||||
|
id.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This method is used to 'forget' the current identity on subsequent
|
/// This method is used to 'forget' the current identity on subsequent
|
||||||
/// requests.
|
/// requests.
|
||||||
fn forget(&mut self);
|
pub fn forget(&self) {
|
||||||
|
if let Some(id) = self.0.extensions_mut().get_mut::<IdentityItem>() {
|
||||||
|
id.id = None;
|
||||||
|
id.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Write session to storage backend.
|
struct IdentityItem {
|
||||||
fn write(&mut self, resp: HttpResponse) -> Result<Response>;
|
id: Option<String>,
|
||||||
|
changed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extractor implementation for Identity type.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use actix_web::*;
|
||||||
|
/// use actix_web::middleware::identity::Identity;
|
||||||
|
///
|
||||||
|
/// fn index(id: Identity) -> String {
|
||||||
|
/// // access request identity
|
||||||
|
/// if let Some(id) = id.identity() {
|
||||||
|
/// format!("Welcome! {}", id)
|
||||||
|
/// } else {
|
||||||
|
/// "Welcome Anonymous!".to_owned()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// # fn main() {}
|
||||||
|
/// ```
|
||||||
|
impl<P> FromRequest<P> for Identity {
|
||||||
|
type Error = Error;
|
||||||
|
type Future = Result<Identity, Error>;
|
||||||
|
type Config = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
|
||||||
|
Ok(Identity(req.clone()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identity policy definition.
|
/// Identity policy definition.
|
||||||
pub trait IdentityPolicy<S>: Sized + 'static {
|
pub trait IdentityPolicy: Sized + 'static {
|
||||||
/// The associated identity
|
/// The return type of the middleware
|
||||||
type Identity: Identity;
|
type Future: IntoFuture<Item = Option<String>, Error = Error>;
|
||||||
|
|
||||||
/// The return type of the middleware
|
/// The return type of the middleware
|
||||||
type Future: Future<Item = Self::Identity, Error = Error>;
|
type ResponseFuture: IntoFuture<Item = (), Error = Error>;
|
||||||
|
|
||||||
/// Parse the session from request and load data from a service identity.
|
/// Parse the session from request and load data from a service identity.
|
||||||
fn from_request(&self, request: &HttpRequest<S>) -> Self::Future;
|
fn from_request<P>(&self, request: &mut ServiceRequest<P>) -> Self::Future;
|
||||||
|
|
||||||
|
/// Write changes to response
|
||||||
|
fn to_response<B>(
|
||||||
|
&self,
|
||||||
|
identity: Option<String>,
|
||||||
|
changed: bool,
|
||||||
|
response: &mut ServiceResponse<B>,
|
||||||
|
) -> Self::ResponseFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request identity middleware
|
/// Request identity middleware
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate actix_web;
|
|
||||||
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
|
|
||||||
/// use actix_web::App;
|
/// use actix_web::App;
|
||||||
|
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().middleware(IdentityService::new(
|
/// let app = App::new().middleware(IdentityService::new(
|
||||||
@ -165,68 +191,97 @@ pub trait IdentityPolicy<S>: Sized + 'static {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct IdentityService<T> {
|
pub struct IdentityService<T> {
|
||||||
backend: T,
|
backend: Rc<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IdentityService<T> {
|
impl<T> IdentityService<T> {
|
||||||
/// Create new identity service with specified backend.
|
/// Create new identity service with specified backend.
|
||||||
pub fn new(backend: T) -> Self {
|
pub fn new(backend: T) -> Self {
|
||||||
IdentityService { backend }
|
IdentityService {
|
||||||
|
backend: Rc::new(backend),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IdentityBox(Box<Identity>);
|
impl<S, T, P, B> Transform<S> for IdentityService<T>
|
||||||
|
where
|
||||||
|
P: 'static,
|
||||||
|
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>> + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
T: IdentityPolicy,
|
||||||
|
B: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest<P>;
|
||||||
|
type Response = ServiceResponse<B>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type InitError = ();
|
||||||
|
type Transform = IdentityServiceMiddleware<S, T>;
|
||||||
|
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||||
|
|
||||||
impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
ok(IdentityServiceMiddleware {
|
||||||
let req = req.clone();
|
backend: self.backend.clone(),
|
||||||
let fut = self.backend.from_request(&req).then(move |res| match res {
|
service: Rc::new(RefCell::new(service)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IdentityServiceMiddleware<S, T> {
|
||||||
|
backend: Rc<T>,
|
||||||
|
service: Rc<RefCell<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, T, P, B> Service for IdentityServiceMiddleware<S, T>
|
||||||
|
where
|
||||||
|
P: 'static,
|
||||||
|
B: 'static,
|
||||||
|
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>> + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
T: IdentityPolicy,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest<P>;
|
||||||
|
type Response = ServiceResponse<B>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
|
self.service.borrow_mut().poll_ready()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future {
|
||||||
|
let srv = self.service.clone();
|
||||||
|
let backend = self.backend.clone();
|
||||||
|
|
||||||
|
Box::new(
|
||||||
|
self.backend.from_request(&mut req).into_future().then(
|
||||||
|
move |res| match res {
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
req.extensions_mut().insert(IdentityBox(Box::new(id)));
|
req.extensions_mut()
|
||||||
FutOk(None)
|
.insert(IdentityItem { id, changed: false });
|
||||||
}
|
|
||||||
Err(err) => FutErr(err),
|
|
||||||
});
|
|
||||||
Ok(Started::Future(Box::new(fut)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
Either::A(srv.borrow_mut().call(req).and_then(move |mut res| {
|
||||||
if let Some(ref mut id) = req.extensions_mut().get_mut::<IdentityBox>() {
|
let id =
|
||||||
id.0.as_mut().write(resp)
|
res.request().extensions_mut().remove::<IdentityItem>();
|
||||||
|
|
||||||
|
if let Some(id) = id {
|
||||||
|
return Either::A(
|
||||||
|
backend
|
||||||
|
.to_response(id.id, id.changed, &mut res)
|
||||||
|
.into_future()
|
||||||
|
.then(move |t| match t {
|
||||||
|
Ok(_) => Ok(res),
|
||||||
|
Err(e) => Ok(res.error_response(e)),
|
||||||
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
Ok(Response::Done(resp))
|
Either::B(ok(res))
|
||||||
}
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
Err(err) => Either::B(ok(req.error_response(err))),
|
||||||
|
},
|
||||||
#[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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +308,11 @@ impl CookieIdentityInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cookie(&self, resp: &mut HttpResponse, id: Option<String>) -> Result<()> {
|
fn set_cookie<B>(
|
||||||
|
&self,
|
||||||
|
resp: &mut ServiceResponse<B>,
|
||||||
|
id: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
let some = id.is_some();
|
let some = id.is_some();
|
||||||
{
|
{
|
||||||
let id = id.unwrap_or_else(String::new);
|
let id = id.unwrap_or_else(String::new);
|
||||||
@ -291,7 +350,7 @@ impl CookieIdentityInner {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load<S>(&self, req: &HttpRequest<S>) -> Option<String> {
|
fn load<T>(&self, req: &ServiceRequest<T>) -> Option<String> {
|
||||||
if let Ok(cookies) = req.cookies() {
|
if let Ok(cookies) = req.cookies() {
|
||||||
for cookie in cookies.iter() {
|
for cookie in cookies.iter() {
|
||||||
if cookie.name() == self.name {
|
if cookie.name() == self.name {
|
||||||
@ -384,16 +443,23 @@ impl CookieIdentityPolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
|
impl IdentityPolicy for CookieIdentityPolicy {
|
||||||
type Identity = CookieIdentity;
|
type Future = Result<Option<String>, Error>;
|
||||||
type Future = FutureResult<CookieIdentity, Error>;
|
type ResponseFuture = Result<(), Error>;
|
||||||
|
|
||||||
fn from_request(&self, req: &HttpRequest<S>) -> Self::Future {
|
fn from_request<P>(&self, req: &mut ServiceRequest<P>) -> Self::Future {
|
||||||
let identity = self.0.load(req);
|
Ok(self.0.load(req))
|
||||||
FutOk(CookieIdentity {
|
}
|
||||||
identity,
|
|
||||||
changed: false,
|
fn to_response<B>(
|
||||||
inner: Rc::clone(&self.0),
|
&self,
|
||||||
})
|
id: Option<String>,
|
||||||
|
changed: bool,
|
||||||
|
res: &mut ServiceResponse<B>,
|
||||||
|
) -> Self::ResponseFuture {
|
||||||
|
if changed {
|
||||||
|
let _ = self.0.set_cookie(res, id);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,8 +6,11 @@ pub use self::compress::Compress;
|
|||||||
mod defaultheaders;
|
mod defaultheaders;
|
||||||
pub use self::defaultheaders::DefaultHeaders;
|
pub use self::defaultheaders::DefaultHeaders;
|
||||||
|
|
||||||
#[cfg(feature = "session")]
|
// #[cfg(feature = "session")]
|
||||||
pub use actix_session as session;
|
// pub use actix_session as session;
|
||||||
|
|
||||||
mod logger;
|
mod logger;
|
||||||
pub use self::logger::Logger;
|
pub use self::logger::Logger;
|
||||||
|
|
||||||
|
#[cfg(feature = "session")]
|
||||||
|
pub mod identity;
|
||||||
|
@ -82,8 +82,9 @@ impl<P> ServiceRequest<P> {
|
|||||||
|
|
||||||
/// Create service response for error
|
/// Create service response for error
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse {
|
pub fn error_response<B, E: Into<Error>>(self, err: E) -> ServiceResponse<B> {
|
||||||
ServiceResponse::new(self.req, err.into().into())
|
let res: Response = err.into().into();
|
||||||
|
ServiceResponse::new(self.req, res.into_body())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method returns reference to the request head
|
/// This method returns reference to the request head
|
||||||
@ -335,6 +336,12 @@ impl<B> ServiceResponse<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create service response for error
|
||||||
|
#[inline]
|
||||||
|
pub fn error_response<E: Into<Error>>(self, err: E) -> Self {
|
||||||
|
Self::from_err(err, self.request)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get reference to original request
|
/// Get reference to original request
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn request(&self) -> &HttpRequest {
|
pub fn request(&self) -> &HttpRequest {
|
||||||
|
Loading…
Reference in New Issue
Block a user