diff --git a/FiraSans-Medium.woff2 b/FiraSans-Medium.woff2 new file mode 100644 index 000000000..7a1e5fc54 Binary files /dev/null and b/FiraSans-Medium.woff2 differ diff --git a/FiraSans-Regular.woff2 b/FiraSans-Regular.woff2 new file mode 100644 index 000000000..e766e06cc Binary files /dev/null and b/FiraSans-Regular.woff2 differ diff --git a/actix_cors/all.html b/actix_cors/all.html new file mode 100644 index 000000000..8f2217408 --- /dev/null +++ b/actix_cors/all.html @@ -0,0 +1,5 @@ +
Redirecting to ../../actix_cors/struct.Cors.html...
+ + + \ No newline at end of file diff --git a/actix_cors/enum.CorsError.html b/actix_cors/enum.CorsError.html new file mode 100644 index 000000000..8c0cc968c --- /dev/null +++ b/actix_cors/enum.CorsError.html @@ -0,0 +1,52 @@ +Errors that can occur when processing CORS guarded requests.
+Allowed origin argument must not be wildcard (*
).
Request header Origin
is required but was not provided.
Request header Access-Control-Request-Method
is required but is missing.
Request header Access-Control-Request-Method
has an invalid value.
Request header Access-Control-Request-Headers
has an invalid value.
Origin is not allowed to make this request.
+Request method is not allowed.
+One or more request headers are not allowed.
+impl Clone for CorsError
[src]impl Debug for CorsError
[src]impl Display for CorsError
[src]impl Error for CorsError
[src]pub fn source(&self) -> Option<&(dyn Error + 'static)>
1.30.0[src]pub fn backtrace(&self) -> Option<&Backtrace>
[src]pub fn description(&self) -> &str
1.0.0[src]pub fn cause(&self) -> Option<&dyn Error>
1.0.0[src]impl ResponseError for CorsError
[src]fn status_code(&self) -> StatusCode
[src]fn error_response(&self) -> HttpResponse
[src]impl RefUnwindSafe for CorsError
impl Send for CorsError
impl Sync for CorsError
impl Unpin for CorsError
impl UnwindSafe for CorsError
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redirecting to ../../actix_cors/enum.CorsError.html...
+ + + \ No newline at end of file diff --git a/actix_cors/index.html b/actix_cors/index.html new file mode 100644 index 000000000..48aa31475 --- /dev/null +++ b/actix_cors/index.html @@ -0,0 +1,45 @@ +Cross-Origin Resource Sharing (CORS) controls for Actix Web.
+This middleware can be applied to both applications and resources. Once built, a
+Cors
builder can be used as an argument for Actix Web’s App::wrap()
,
+Scope::wrap()
, or Resource::wrap()
methods.
This CORS middleware automatically handles OPTIONS
preflight requests.
+use actix_cors::Cors; +use actix_web::{get, http, web, App, HttpRequest, HttpResponse, HttpServer}; + +#[get("/index.html")] +async fn index(req: HttpRequest) -> &'static str { + "<p>Hello World!</p>" +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + let cors = Cors::default() + .allowed_origin("https://www.rust-lang.org/") + .allowed_origin_fn(|origin, _req_head| { + origin.as_bytes().ends_with(b".rust-lang.org") + }) + .allowed_methods(vec!["GET", "POST"]) + .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) + .allowed_header(http::header::CONTENT_TYPE) + .max_age(3600); + + App::new() + .wrap(cors) + .service(index) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await; + + Ok(()) +}
Cors | Builder for CORS middleware. + |
CorsError | Errors that can occur when processing CORS guarded requests. + |
Builder for CORS middleware.
+To construct a CORS middleware, call Cors::default()
to create a blank, restrictive builder.
+Then use any of the builder methods to customize CORS behavior.
The alternative Cors::permissive()
constructor is available for local development, allowing
+all origins and headers, etc. The permissive constructor should not be used in production.
Errors surface in the middleware initialization phase. This means that, if you have logs enabled
+in Actix Web (using env_logger
or other crate that exposes logs from the log
crate), error
+messages will outline what is wrong with the CORS configuration in the server logs and the
+server will fail to start up or serve requests.
+use actix_cors::Cors; +use actix_web::http::header; + +let cors = Cors::default() + .allowed_origin("https://www.rust-lang.org") + .allowed_methods(vec!["GET", "POST"]) + .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + .allowed_header(header::CONTENT_TYPE) + .max_age(3600); + +// `cors` can now be used in `App::wrap`.
impl Cors
[src]pub fn permissive() -> Self
[src]A very permissive set of default for quick development. Not recommended for production use.
+All origins, methods, request headers and exposed headers allowed. Credentials supported. +Max age 1 hour. Does not send wildcard.
+pub fn allow_any_origin(self) -> Cors
[src]Resets allowed origin list to a state where any origin is accepted.
+See Cors::allowed_origin
for more info on allowed origins.
pub fn allowed_origin(self, origin: &str) -> Cors
[src]Add an origin that is allowed to make requests.
+By default, requests from all origins are accepted by CORS logic. This method allows to
+specify a finite set of origins to verify the value of the Origin
request header.
These are origin-or-null
types in the Fetch Standard.
When this list is set, the client’s Origin
request header will be checked in a
+case-sensitive manner.
When all origins are allowed and send_wildcard
is set, *
will be sent in the
+Access-Control-Allow-Origin
response header. If send_wildcard
is not set, the client’s
+Origin
request header will be echoed back in the Access-Control-Allow-Origin
+response header.
If the origin of the request doesn’t match any allowed origins and at least one
+allowed_origin_fn
function is set, these functions will be used to determinate
+allowed origins.
*
). Cors::send_wildcard
should be used instead.pub fn allowed_origin_fn<F>(self, f: F) -> Cors where
F: Fn(&HeaderValue, &RequestHead) -> bool + 'static,
[src]Determinate allowed origins by processing requests which didn’t match any origins specified
+in the allowed_origin
.
The function will receive two parameters, the Origin header value, and the RequestHead
of
+each request, which can be used to determine whether to allow the request or not.
If the function returns true
, the client’s Origin
request header will be echoed back
+into the Access-Control-Allow-Origin
response header.
pub fn allow_any_method(self) -> Cors
[src]Resets allowed methods list to all methods.
+See Cors::allowed_methods
for more info on allowed methods.
pub fn allowed_methods<U, M>(self, methods: U) -> Cors where
U: IntoIterator<Item = M>,
M: TryInto<Method>,
<M as TryInto<Method>>::Error: Into<HttpError>,
[src]Set a list of methods which allowed origins can perform.
+These will be sent in the Access-Control-Allow-Methods
response header as specified in
+the Fetch Standard CORS protocol.
Defaults to [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]
pub fn allow_any_header(self) -> Cors
[src]Resets allowed request header list to a state where any header is accepted.
+See Cors::allowed_headers
for more info on allowed request headers.
pub fn allowed_header<H>(self, header: H) -> Cors where
H: TryInto<HeaderName>,
<H as TryInto<HeaderName>>::Error: Into<HttpError>,
[src]Add an allowed request header.
+See Cors::allowed_headers
for more info on allowed request headers.
pub fn allowed_headers<U, H>(self, headers: U) -> Cors where
U: IntoIterator<Item = H>,
H: TryInto<HeaderName>,
<H as TryInto<HeaderName>>::Error: Into<HttpError>,
[src]Set a list of request header field names which can be used when this resource is accessed by +allowed origins.
+If All
is set, whatever is requested by the client in Access-Control-Request-Headers
+will be echoed back in the Access-Control-Allow-Headers
header as specified in
+the Fetch Standard CORS protocol.
Defaults to All
.
pub fn expose_any_header(self) -> Cors
[src]Resets exposed response header list to a state where any header is accepted.
+See Cors::expose_headers
for more info on exposed response headers.
pub fn expose_headers<U, H>(self, headers: U) -> Cors where
U: IntoIterator<Item = H>,
H: TryInto<HeaderName>,
<H as TryInto<HeaderName>>::Error: Into<HttpError>,
[src]Set a list of headers which are safe to expose to the API of a CORS API specification.
+This corresponds to the Access-Control-Expose-Headers
response header as specified in
+the Fetch Standard CORS protocol.
This defaults to an empty set.
+pub fn max_age(self, max_age: impl Into<Option<usize>>) -> Cors
[src]Set a maximum time (in seconds) for which this CORS request maybe cached.
+This value is set as the Access-Control-Max-Age
header as specified in
+the Fetch Standard CORS protocol.
Pass a number (of seconds) or use None to disable sending max age header.
+pub fn send_wildcard(self) -> Cors
[src]Set to use wildcard origins.
+If send wildcard is set and the allowed_origins
parameter is All
, a wildcard
+Access-Control-Allow-Origin
response header is sent, rather than the request’s
+Origin
header.
This CANNOT be used in conjunction with allowed_origins
set to All
and
+allow_credentials
set to true
. Depending on the mode of usage, this will either result
+in an CorsError::CredentialsWithWildcardOrigin
error during actix launch or runtime.
Defaults to false
.
pub fn supports_credentials(self) -> Cors
[src]Allows users to make authenticated requests
+If true, injects the Access-Control-Allow-Credentials
header in responses. This allows
+cookies and credentials to be submitted across domains as specified in
+the Fetch Standard CORS protocol.
This option cannot be used in conjunction with an allowed_origin
set to All
and
+send_wildcards
set to true
.
Defaults to false
.
A server initialization error will occur if credentials are allowed, but the Origin is set
+to send wildcards (*
); this is not allowed by the CORS protocol.
pub fn disable_vary_header(self) -> Cors
[src]Disable Vary
header support.
When enabled the header Vary: Origin
will be returned as per the Fetch Standard
+implementation guidelines.
Setting this header when the Access-Control-Allow-Origin
is dynamically generated
+(eg. when there is more than one allowed origin, and an Origin other than ‘*’ is returned)
+informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached.
By default, Vary
header support is enabled.
pub fn disable_preflight(self) -> Cors
[src]Disable support for preflight requests.
+When enabled CORS middleware automatically handles OPTIONS
requests.
+This is useful for application level middleware.
By default preflight support is enabled.
+impl Debug for Cors
[src]impl Default for Cors
[src]fn default() -> Cors
[src]A restrictive (security paranoid) set of defaults.
+No allowed origins, methods, request headers or exposed headers. Credentials +not supported. No max age (will use browser’s default).
+impl<S, B> Transform<S, ServiceRequest> for Cors where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
[src]type Response = ServiceResponse<B>
Responses produced by the service.
+type Error = Error
Errors produced by the service.
+type InitError = ()
Errors produced while building a transform service.
+type Transform = CorsMiddleware<S>
The TransformService
value created by this factory
type Future = Ready<Result<Self::Transform, Self::InitError>>
The future response value.
+fn new_transform(&self, service: S) -> Self::Future
[src]impl !RefUnwindSafe for Cors
impl !Send for Cors
impl !Sync for Cors
impl Unpin for Cors
impl !UnwindSafe for Cors
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Request identity service for Actix applications.
+IdentityService 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 +uses cookies as identity storage.
+To access current request identity +Identity extractor should be used.
+ ++use actix_web::*; +use actix_identity::{Identity, CookieIdentityPolicy, IdentityService}; + +async fn index(id: Identity) -> String { + // access request identity + if let Some(id) = id.identity() { + format!("Welcome! {}", id) + } else { + "Welcome Anonymous!".to_owned() + } +} + +async fn login(id: Identity) -> HttpResponse { + id.remember("User1".to_owned()); // <- remember identity + HttpResponse::Ok().finish() +} + +async fn logout(id: Identity) -> HttpResponse { + id.forget(); // <- remove identity + HttpResponse::Ok().finish() +} + +fn main() { + let app = App::new().wrap(IdentityService::new( + // <- create identity middleware + CookieIdentityPolicy::new(&[0; 32]) // <- create cookie identity policy + .name("auth-cookie") + .secure(false))) + .service(web::resource("/index.html").to(index)) + .service(web::resource("/login.html").to(login)) + .service(web::resource("/logout.html").to(logout)); +}
CookieIdentityPolicy | Use cookies for request identity storage. + |
Identity | The extractor type to obtain your identity from a request. + |
IdentityService | Request identity middleware + |
IdentityPolicy | Identity policy definition. + |
RequestIdentity | Helper trait that allows to get Identity. + |
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.
++use actix_web::App; +use actix_identity::{CookieIdentityPolicy, IdentityService}; + +let app = App::new().wrap(IdentityService::new( + // <- create identity middleware + CookieIdentityPolicy::new(&[0; 32]) // <- construct cookie policy + .domain("www.rust-lang.org") + .name("actix_auth") + .path("/") + .secure(true), +));
impl CookieIdentityPolicy
[src]pub fn new(key: &[u8]) -> CookieIdentityPolicy
[src]Construct new CookieIdentityPolicy
instance.
Panics if key length is less than 32 bytes.
+pub fn path<S: Into<String>>(self, value: S) -> CookieIdentityPolicy
[src]Sets the path
field in the session cookie being built.
pub fn name<S: Into<String>>(self, value: S) -> CookieIdentityPolicy
[src]Sets the name
field in the session cookie being built.
pub fn domain<S: Into<String>>(self, value: S) -> CookieIdentityPolicy
[src]Sets the domain
field in the session cookie being built.
pub fn secure(self, value: bool) -> CookieIdentityPolicy
[src]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 max_age(self, seconds: i64) -> CookieIdentityPolicy
[src]Sets the max-age
field in the session cookie being built with given number of seconds.
pub fn max_age_time(self, value: Duration) -> CookieIdentityPolicy
[src]Sets the max-age
field in the session cookie being built with time::Duration
.
pub fn http_only(self, http_only: bool) -> Self
[src]Sets the http_only
field in the session cookie being built.
pub fn same_site(self, same_site: SameSite) -> Self
[src]Sets the same_site
field in the session cookie being built.
pub fn visit_deadline(self, value: Duration) -> CookieIdentityPolicy
[src]Accepts only users whose cookie has been seen before the given deadline
+By default visit deadline is disabled.
+pub fn login_deadline(self, value: Duration) -> CookieIdentityPolicy
[src]Accepts only users which has been authenticated before the given deadline
+By default login deadline is disabled.
+impl IdentityPolicy for CookieIdentityPolicy
[src]type Future = Ready<Result<Option<String>, Error>>
The return type of the middleware
+type ResponseFuture = Ready<Result<(), Error>>
The return type of the middleware
+fn from_request(&self, req: &mut ServiceRequest) -> Self::Future
[src]fn to_response<B>(
&self,
id: Option<String>,
changed: bool,
res: &mut ServiceResponse<B>
) -> Self::ResponseFuture
[src]impl !RefUnwindSafe for CookieIdentityPolicy
impl !Send for CookieIdentityPolicy
impl !Sync for CookieIdentityPolicy
impl Unpin for CookieIdentityPolicy
impl UnwindSafe for CookieIdentityPolicy
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
The extractor type to obtain your identity from a request.
+ ++use actix_web::*; +use actix_identity::Identity; + +fn index(id: Identity) -> Result<String> { + // access request identity + if let Some(id) = id.identity() { + Ok(format!("Welcome! {}", id)) + } else { + Ok("Welcome Anonymous!".to_owned()) + } +} + +fn login(id: Identity) -> HttpResponse { + id.remember("User1".to_owned()); // <- remember identity + HttpResponse::Ok().finish() +} + +fn logout(id: Identity) -> HttpResponse { + id.forget(); // <- remove identity + HttpResponse::Ok().finish() +}
impl Identity
[src]pub fn identity(&self) -> Option<String>
[src]Return the claimed identity of the user associated request or
+None
if no identity can be found associated with the request.
pub fn remember(&self, identity: String)
[src]Remember identity.
+pub fn forget(&self)
[src]This method is used to ‘forget’ the current identity on subsequent +requests.
+impl Clone for Identity
[src]impl FromRequest for Identity
[src]Extractor implementation for Identity type.
+ ++use actix_identity::Identity; + +fn index(id: Identity) -> String { + // access request identity + if let Some(id) = id.identity() { + format!("Welcome! {}", id) + } else { + "Welcome Anonymous!".to_owned() + } +}
type Config = ()
Configuration for this extractor.
+type Error = Error
The associated error which can be returned.
+type Future = Ready<Result<Identity, Error>>
Future that resolves to a Self.
+fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future
[src]pub fn extract(req: &HttpRequest) -> Self::Future
pub fn configure<F>(f: F) -> Self::Config where
F: FnOnce(Self::Config) -> Self::Config,
impl !RefUnwindSafe for Identity
impl !Send for Identity
impl !Sync for Identity
impl Unpin for Identity
impl !UnwindSafe for Identity
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Request identity middleware
+ ++use actix_web::App; +use actix_identity::{CookieIdentityPolicy, IdentityService}; + +let app = App::new().wrap(IdentityService::new( + // <- create identity middleware + CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend + .name("auth-cookie") + .secure(false), +));
impl<T> IdentityService<T>
[src]impl<S, T, B> Transform<S, ServiceRequest> for IdentityService<T> where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
T: IdentityPolicy,
B: 'static,
[src]type Response = ServiceResponse<B>
Responses produced by the service.
+type Error = Error
Errors produced by the service.
+type InitError = ()
Errors produced while building a transform service.
+type Transform = IdentityServiceMiddleware<S, T>
The TransformService
value created by this factory
type Future = Ready<Result<Self::Transform, Self::InitError>>
The future response value.
+fn new_transform(&self, service: S) -> Self::Future
[src]impl<T> !RefUnwindSafe for IdentityService<T>
impl<T> !Send for IdentityService<T>
impl<T> !Sync for IdentityService<T>
impl<T> Unpin for IdentityService<T>
impl<T> UnwindSafe for IdentityService<T> where
T: RefUnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Identity policy definition.
+type Future: Future<Output = Result<Option<String>, Error>>
[src]The return type of the middleware
+type ResponseFuture: Future<Output = Result<(), Error>>
[src]The return type of the middleware
+fn from_request(&self, request: &mut ServiceRequest) -> Self::Future
[src]Parse the session from request and load data from a service identity.
+fn to_response<B>(
&self,
identity: Option<String>,
changed: bool,
response: &mut ServiceResponse<B>
) -> Self::ResponseFuture
[src]Write changes to response
+impl IdentityPolicy for CookieIdentityPolicy
[src]type Future = Ready<Result<Option<String>, Error>>
type ResponseFuture = Ready<Result<(), Error>>
fn from_request(&self, req: &mut ServiceRequest) -> Self::Future
[src]fn to_response<B>(
&self,
id: Option<String>,
changed: bool,
res: &mut ServiceResponse<B>
) -> Self::ResponseFuture
[src]Helper trait that allows to get Identity.
+It could be used in middleware but identity policy must be set before any other middleware that needs identity
+RequestIdentity is implemented both for ServiceRequest
and HttpRequest
.
fn get_identity(&self) -> Option<String>
[src]impl<T> RequestIdentity for T where
T: HttpMessage,
[src]fn get_identity(&self) -> Option<String>
[src]Payload size is bigger than 256k
+Content type error
+Serialize(ProtoBufEncodeError)
Serialize error
+Deserialize(ProtoBufDecodeError)
Deserialize error
+Payload error
+impl Debug for ProtoBufPayloadError
[src]impl Display for ProtoBufPayloadError
[src]impl From<DecodeError> for ProtoBufPayloadError
[src]fn from(err: ProtoBufDecodeError) -> ProtoBufPayloadError
[src]impl From<PayloadError> for ProtoBufPayloadError
[src]fn from(err: PayloadError) -> ProtoBufPayloadError
[src]impl ResponseError for ProtoBufPayloadError
[src]fn error_response(&self) -> HttpResponse
[src]pub fn status_code(&self) -> StatusCode
impl !RefUnwindSafe for ProtoBufPayloadError
impl Send for ProtoBufPayloadError
impl Sync for ProtoBufPayloadError
impl Unpin for ProtoBufPayloadError
impl !UnwindSafe for ProtoBufPayloadError
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
ProtoBuf | |
ProtoBufConfig | |
ProtoBufMessage |
ProtoBufPayloadError |
ProtoBufResponseBuilder |
impl<T: Message> Debug for ProtoBuf<T> where
T: Debug,
[src]impl<T: Message> Deref for ProtoBuf<T>
[src]impl<T: Message> DerefMut for ProtoBuf<T>
[src]impl<T: Message> Display for ProtoBuf<T> where
T: Display,
[src]impl<T> FromRequest for ProtoBuf<T> where
T: Message + Default + 'static,
[src]type Config = ProtoBufConfig
Configuration for this extractor.
+type Error = Error
The associated error which can be returned.
+type Future = LocalBoxFuture<'static, Result<Self, Error>>
Future that resolves to a Self.
+fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future
[src]pub fn extract(req: &HttpRequest) -> Self::Future
pub fn configure<F>(f: F) -> Self::Config where
F: FnOnce(Self::Config) -> Self::Config,
impl<T: Message + Default> Responder for ProtoBuf<T>
[src]fn respond_to(self, _: &HttpRequest) -> HttpResponse
[src]pub fn with_status(self, status: StatusCode) -> CustomResponder<Self>
pub fn with_header<H>(self, header: H) -> CustomResponder<Self> where
H: IntoHeaderPair,
impl<T> RefUnwindSafe for ProtoBuf<T> where
T: RefUnwindSafe,
impl<T> Send for ProtoBuf<T>
impl<T> Sync for ProtoBuf<T>
impl<T> Unpin for ProtoBuf<T> where
T: Unpin,
impl<T> UnwindSafe for ProtoBuf<T> where
T: UnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl ProtoBufConfig
[src]pub fn limit(&mut self, limit: usize) -> &mut Self
[src]Change max size of payload. By default max size is 256Kb
+impl Default for ProtoBufConfig
[src]impl RefUnwindSafe for ProtoBufConfig
impl Send for ProtoBufConfig
impl Sync for ProtoBufConfig
impl Unpin for ProtoBufConfig
impl UnwindSafe for ProtoBufConfig
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl<T: Message + Default> ProtoBufMessage<T>
[src]pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self
[src]Create ProtoBufMessage
for request.
pub fn limit(self, limit: usize) -> Self
[src]Change max size of payload. By default max size is 256Kb
+impl<T: Message + Default + 'static> Future for ProtoBufMessage<T>
[src]impl<T> !RefUnwindSafe for ProtoBufMessage<T>
impl<T> !Send for ProtoBufMessage<T>
impl<T> !Sync for ProtoBufMessage<T>
impl<T> Unpin for ProtoBufMessage<T>
impl<T> !UnwindSafe for ProtoBufMessage<T>
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> FutureExt for T where
T: Future + ?Sized,
pub fn map<U, F>(self, f: F) -> Map<Self, F> where
F: FnOnce(Self::Output) -> U,
pub fn map_into<U>(self) -> MapInto<Self, U> where
Self::Output: Into<U>,
pub fn then<Fut, F>(self, f: F) -> Then<Self, Fut, F> where
Fut: Future,
F: FnOnce(Self::Output) -> Fut,
pub fn left_future<B>(self) -> Either<Self, B> where
B: Future<Output = Self::Output>,
pub fn right_future<A>(self) -> Either<A, Self> where
A: Future<Output = Self::Output>,
pub fn into_stream(self) -> IntoStream<Self>
pub fn flatten(self) -> Flatten<Self> where
Self::Output: Future,
pub fn flatten_stream(self) -> FlattenStream<Self> where
Self::Output: Stream,
pub fn fuse(self) -> Fuse<Self>
pub fn inspect<F>(self, f: F) -> Inspect<Self, F> where
F: FnOnce(&Self::Output),
pub fn catch_unwind(self) -> CatchUnwind<Self> where
Self: UnwindSafe,
pub fn shared(self) -> Shared<Self> where
Self::Output: Clone,
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send, Global>> where
Self: Send + 'a,
pub fn boxed_local<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a, Global>> where
Self: 'a,
pub fn unit_error(self) -> UnitError<Self>
pub fn never_error(self) -> NeverError<Self>
pub fn poll_unpin(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output> where
Self: Unpin,
pub fn now_or_never(self) -> Option<Self::Output>
impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<F> IntoFuture for F where
F: Future,
[src]type Output = <F as Future>::Output
into_future
)The output that the future will produce on completion.
+type Future = F
into_future
)Which kind of future are we turning this into?
+pub fn into_future(self) -> <F as IntoFuture>::Future
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<F, T, E> TryFuture for F where
F: Future<Output = Result<T, E>> + ?Sized,
type Ok = T
The type of successful values yielded by this future
+type Error = E
The type of failures yielded by this future
+pub fn try_poll(
self: Pin<&mut F>,
cx: &mut Context<'_>
) -> Poll<<F as Future>::Output>
impl<Fut> TryFutureExt for Fut where
Fut: TryFuture + ?Sized,
pub fn flatten_sink<Item>(self) -> FlattenSink<Self, Self::Ok> where
Self::Ok: Sink<Item>,
<Self::Ok as Sink<Item>>::Error == Self::Error,
pub fn map_ok<T, F>(self, f: F) -> MapOk<Self, F> where
F: FnOnce(Self::Ok) -> T,
pub fn map_ok_or_else<T, E, F>(self, e: E, f: F) -> MapOkOrElse<Self, F, E> where
E: FnOnce(Self::Error) -> T,
F: FnOnce(Self::Ok) -> T,
pub fn map_err<E, F>(self, f: F) -> MapErr<Self, F> where
F: FnOnce(Self::Error) -> E,
pub fn err_into<E>(self) -> ErrInto<Self, E> where
Self::Error: Into<E>,
pub fn ok_into<U>(self) -> OkInto<Self, U> where
Self::Ok: Into<U>,
pub fn and_then<Fut, F>(self, f: F) -> AndThen<Self, Fut, F> where
Fut: TryFuture<Error = Self::Error>,
F: FnOnce(Self::Ok) -> Fut,
pub fn or_else<Fut, F>(self, f: F) -> OrElse<Self, Fut, F> where
Fut: TryFuture<Ok = Self::Ok>,
F: FnOnce(Self::Error) -> Fut,
pub fn inspect_ok<F>(self, f: F) -> InspectOk<Self, F> where
F: FnOnce(&Self::Ok),
pub fn inspect_err<F>(self, f: F) -> InspectErr<Self, F> where
F: FnOnce(&Self::Error),
pub fn try_flatten(self) -> TryFlatten<Self, Self::Ok> where
Self::Ok: TryFuture,
<Self::Ok as TryFuture>::Error == Self::Error,
pub fn try_flatten_stream(self) -> TryFlattenStream<Self> where
Self::Ok: TryStream,
<Self::Ok as TryStream>::Error == Self::Error,
pub fn unwrap_or_else<F>(self, f: F) -> UnwrapOrElse<Self, F> where
F: FnOnce(Self::Error) -> Self::Ok,
pub fn into_future(self) -> IntoFuture<Self>
pub fn try_poll_unpin(
&mut self,
cx: &mut Context<'_>
) -> Poll<Result<Self::Ok, Self::Error>> where
Self: Unpin,
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl ProtoBufResponseBuilder for HttpResponseBuilder
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl Clone for RespValue
pub fn clone(&self) -> RespValue
pub fn clone_from(&mut self, source: &Self)
1.0.0[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl Debug for RespValue
impl Eq for RespValue
impl<'a> From<&'a [u8]> for RespValue
impl Eq for RespValue
impl<'a> From<&'a [u8]> for RespValue
impl<'a> From<&'a String> for RespValue
impl<'a> From<&'a str> for RespValue
impl<'a> From<Arc<str>> for RespValue
impl<'a> From<String> for RespValue
impl<'a> From<Vec<u8, Global>> for RespValue
impl<'a> From<Arc<str>> for RespValue
impl<'a> From<String> for RespValue
impl<'a> From<Vec<u8, Global>> for RespValue
impl<'a> From<usize> for RespValue
impl FromResp for RespValue
pub fn from_resp_int(resp: RespValue) -> Result<RespValue, Error>
pub fn from_resp(resp: RespValue) -> Result<Self, Error>
impl PartialEq<RespValue> for RespValue
impl StructuralEq for RespValue
impl StructuralPartialEq for RespValue
impl RefUnwindSafe for RespValue
[src]impl Send for RespValue
[src]impl Sync for RespValue
[src]impl Unpin for RespValue
[src]impl UnwindSafe for RespValue
[src]impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]impl StructuralEq for RespValue
impl StructuralPartialEq for RespValue
impl RefUnwindSafe for RespValue
impl Send for RespValue
impl Sync for RespValue
impl Unpin for RespValue
impl UnwindSafe for RespValue
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
-pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
-pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
-pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redis communication actor
+Redis communication actor
impl RedisActor
[src]pub fn start<S: Into<String>>(addr: S) -> Addr<RedisActor>
[src]Start new Supervisor
with RedisActor
.
impl Actor for RedisActor
[src]type Context = Context<Self>
Actor execution context type
fn started(&mut self, ctx: &mut Context<Self>)
[src]pub fn start(self) -> Addr<Self> where
Self: Actor<Context = Context<Self>>,
pub fn start_default() -> Addr<Self> where
Self: Actor<Context = Context<Self>> + Default,
pub fn start_in_arbiter<F>(wrk: &ArbiterHandle, f: F) -> Addr<Self> where
Self: Actor<Context = Context<Self>>,
F: FnOnce(&mut Context<Self>) -> Self + Send + 'static,
pub fn start_in_arbiter<F>(wrk: &ArbiterHandle, f: F) -> Addr<Self> where
Self: Actor<Context = Context<Self>>,
F: FnOnce(&mut Context<Self>) -> Self + Send + 'static,
pub fn create<F>(f: F) -> Addr<Self> where
Self: Actor<Context = Context<Self>>,
F: FnOnce(&mut Context<Self>) -> Self,
impl Handler<Command> for RedisActor
[src]type Result = ResponseFuture<Result<RespValue, Error>>
The type of value that this handler will return. Read more
fn handle(&mut self, msg: Command, _: &mut Self::Context) -> Self::Result
[src]impl Supervised for RedisActor
[src]fn restarting(&mut self, _: &mut Self::Context)
[src]impl WriteHandler<Error> for RedisActor
[src]impl !RefUnwindSafe for RedisActor
[src]impl !Send for RedisActor
[src]impl !Sync for RedisActor
[src]impl Unpin for RedisActor
[src]impl !UnwindSafe for RedisActor
[src]impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]impl !RefUnwindSafe for RedisActor
impl !Send for RedisActor
impl !Sync for RedisActor
impl Unpin for RedisActor
impl !UnwindSafe for RedisActor
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
-pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
-pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Use redis as session storage.
+Use redis as session storage.
You need to pass an address of the redis server and random value to the
constructor of RedisSession
. This is private key for cookie
session, When this value is changed, all session data is lost.
https
.
type InitError = ()
Errors produced while building a transform service.
type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>
The future response value.
fn new_transform(&self, service: S) -> Self::Future
[src]impl !RefUnwindSafe for RedisSession
[src]impl !Send for RedisSession
[src]impl !Sync for RedisSession
[src]impl Unpin for RedisSession
[src]impl !UnwindSafe for RedisSession
[src]impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]impl !RefUnwindSafe for RedisSession
impl !Send for RedisSession
impl !Sync for RedisSession
impl Unpin for RedisSession
impl !UnwindSafe for RedisSession
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
-pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
-pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redirecting to ../../actix_session/struct.CookieSession.html...
+ + + \ No newline at end of file diff --git a/actix_session/enum.SessionStatus.html b/actix_session/enum.SessionStatus.html new file mode 100644 index 000000000..9a03e784e --- /dev/null +++ b/actix_session/enum.SessionStatus.html @@ -0,0 +1,37 @@ +impl Clone for SessionStatus
[src]fn clone(&self) -> SessionStatus
[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl Debug for SessionStatus
[src]impl Default for SessionStatus
[src]fn default() -> SessionStatus
[src]impl PartialEq<SessionStatus> for SessionStatus
[src]fn eq(&self, other: &SessionStatus) -> bool
[src]#[must_use]pub fn ne(&self, other: &Rhs) -> bool
1.0.0[src]impl StructuralPartialEq for SessionStatus
[src]impl RefUnwindSafe for SessionStatus
impl Send for SessionStatus
impl Sync for SessionStatus
impl Unpin for SessionStatus
impl UnwindSafe for SessionStatus
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Sessions for Actix Web.
+Provides a general solution for session management. Session middleware could provide different +implementations which could be accessed via general session API.
+This crate provides a general solution for session management and includes a cookie backend. +Other backend implementations can be built to use persistent or key-value stores, for example.
+In general, some session middleware, such as a CookieSession
is initialized and applied.
+To access session data, the Session
extractor must be used. This extractor allows reading
+modifying session data.
+use actix_web::{web, App, HttpServer, HttpResponse, Error}; +use actix_session::{Session, CookieSession}; + +fn index(session: Session) -> Result<&'static str, Error> { + // access session data + if let Some(count) = session.get::<i32>("counter")? { + println!("SESSION value: {}", count); + session.set("counter", count + 1)?; + } else { + session.set("counter", 1)?; + } + + Ok("Welcome!") +} + +#[actix_rt::main] +async fn main() -> std::io::Result<()> { + HttpServer::new( + || App::new() + // create cookie based session middleware + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .default_service(web::to(|| HttpResponse::Ok()))) + .bind(("127.0.0.1", 8080))? + .run() + .await +}
CookieSession | Use cookies for session storage. + |
Session | The high-level interface you use to modify session data. + |
SessionStatus |
UserSession | Extraction of a |
Use cookies for session storage.
+CookieSession
creates sessions which are limited to storing
+fewer than 4000 bytes of data (as the payload must fit into a single
+cookie). An Internal Server Error is generated if the session contains more
+than 4000 bytes.
A cookie may have a security policy of signed or private. Each has a
+respective CookieSession
constructor.
A signed cookie is stored on the client as plaintext alongside +a signature such that the cookie may be viewed but not modified by the +client.
+A private cookie is stored on the client as encrypted text +such that it may neither be viewed nor modified by the client.
+The constructors take a key as an argument. +This is the private key for cookie session - when this value is changed, +all session data is lost. The constructors will panic if the key is less +than 32 bytes in length.
+The backend relies on cookie
crate to create and read cookies.
+By default all cookies are percent encoded, but certain symbols may
+cause troubles when reading cookie, if they are not properly percent encoded.
+use actix_session::CookieSession; +use actix_web::{web, App, HttpResponse, HttpServer}; + +let app = App::new().wrap( + CookieSession::signed(&[0; 32]) + .domain("www.rust-lang.org") + .name("actix_session") + .path("/") + .secure(true)) + .service(web::resource("/").to(|| HttpResponse::Ok()));
impl CookieSession
[src]pub fn signed(key: &[u8]) -> CookieSession
[src]Construct new signed CookieSession
instance.
Panics if key length is less than 32 bytes.
+pub fn private(key: &[u8]) -> CookieSession
[src]Construct new private CookieSession
instance.
Panics if key length is less than 32 bytes.
+pub fn path<S: Into<String>>(self, value: S) -> CookieSession
[src]Sets the path
field in the session cookie being built.
pub fn name<S: Into<String>>(self, value: S) -> CookieSession
[src]Sets the name
field in the session cookie being built.
pub fn domain<S: Into<String>>(self, value: S) -> CookieSession
[src]Sets the domain
field in the session cookie being built.
pub fn lazy(self, value: bool) -> CookieSession
[src]When true, prevents adding session cookies to responses until
+the session contains data. Default is false
.
Useful when trying to comply with laws that require consent for setting cookies.
+pub fn secure(self, value: bool) -> CookieSession
[src]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 http_only(self, value: bool) -> CookieSession
[src]Sets the http_only
field in the session cookie being built.
pub fn same_site(self, value: SameSite) -> CookieSession
[src]Sets the same_site
field in the session cookie being built.
pub fn max_age(self, seconds: i64) -> CookieSession
[src]Sets the max-age
field in the session cookie being built.
pub fn max_age_time(self, value: Duration) -> CookieSession
[src]Sets the max-age
field in the session cookie being built.
pub fn expires_in(self, seconds: i64) -> CookieSession
[src]Sets the expires
field in the session cookie being built.
pub fn expires_in_time(self, value: Duration) -> CookieSession
[src]Sets the expires
field in the session cookie being built.
impl<S, B: 'static> Transform<S, ServiceRequest> for CookieSession where
S: Service<ServiceRequest, Response = ServiceResponse<B>>,
S::Future: 'static,
S::Error: 'static,
[src]type Response = ServiceResponse<B>
Responses produced by the service.
+type Error = S::Error
Errors produced by the service.
+type InitError = ()
Errors produced while building a transform service.
+type Transform = CookieSessionMiddleware<S>
The TransformService
value created by this factory
type Future = Ready<Result<Self::Transform, Self::InitError>>
The future response value.
+fn new_transform(&self, service: S) -> Self::Future
[src]impl !RefUnwindSafe for CookieSession
impl !Send for CookieSession
impl !Sync for CookieSession
impl Unpin for CookieSession
impl UnwindSafe for CookieSession
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
The high-level interface you use to modify session data.
+Session object is obtained with UserSession::get_session
. The UserSession
trait is
+implemented for HttpRequest
, ServiceRequest
, and RequestHead
.
+use actix_session::Session; +use actix_web::Result; + +async fn index(session: Session) -> Result<&'static str> { + // access session data + if let Some(count) = session.get::<i32>("counter")? { + session.set("counter", count + 1)?; + } else { + session.set("counter", 1)?; + } + + Ok("Welcome!") +}
impl Session
[src]pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, Error>
[src]Get a value
from the session.
pub fn set<T: Serialize>(&self, key: &str, value: T) -> Result<(), Error>
[src]Set a value
from the session.
pub fn remove(&self, key: &str)
[src]Remove value from the session.
+pub fn clear(&self)
[src]Clear the session.
+pub fn purge(&self)
[src]Removes session, both client and server side.
+pub fn renew(&self)
[src]Renews the session key, assigning existing session state to new key.
+pub fn set_session(
data: impl IntoIterator<Item = (String, String)>,
req: &mut ServiceRequest
)
[src]Adds the given key-value pairs to the session on the request.
+Values that match keys already existing on the session will be overwritten. Values should +already be JSON serialized.
++let mut req = test::TestRequest::default().to_srv_request(); + +Session::set_session( + vec![("counter".to_string(), serde_json::to_string(&0).unwrap())], + &mut req, +);
pub fn get_changes<B>(
res: &mut ServiceResponse<B>
) -> (SessionStatus, Option<impl Iterator<Item = (String, String)>>)
[src]impl FromRequest for Session
[src]Extractor implementation for Session type.
+ ++use actix_session::Session; + +fn index(session: Session) -> Result<&'static str> { + // access session data + if let Some(count) = session.get::<i32>("counter")? { + session.set("counter", count + 1)?; + } else { + session.set("counter", 1)?; + } + + Ok("Welcome!") +}
type Error = Error
The associated error which can be returned.
+type Future = Ready<Result<Session, Error>>
Future that resolves to a Self.
+type Config = ()
Configuration for this extractor.
+fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future
[src]pub fn extract(req: &HttpRequest) -> Self::Future
pub fn configure<F>(f: F) -> Self::Config where
F: FnOnce(Self::Config) -> Self::Config,
impl !RefUnwindSafe for Session
impl !Send for Session
impl !Sync for Session
impl Unpin for Session
impl !UnwindSafe for Session
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Extraction of a Session
object.
fn get_session(&self) -> Session
[src]impl UserSession for HttpRequest
[src]fn get_session(&self) -> Session
[src]impl UserSession for ServiceRequest
[src]fn get_session(&self) -> Session
[src]impl UserSession for RequestHead
[src]fn get_session(&self) -> Session
[src]Extractor for the “Basic” HTTP Authentication Scheme
+BasicAuth | Extractor for HTTP Basic auth. + |
Config |
|
Extractor for HTTP Basic auth.
++use actix_web::Result; +use actix_web_httpauth::extractors::basic::BasicAuth; + +async fn index(auth: BasicAuth) -> String { + format!("Hello, {}!", auth.user_id()) +}
If authentication fails, this extractor fetches the Config
instance
+from the app data in order to properly form the WWW-Authenticate
+response header.
+use actix_web::{web, App}; +use actix_web_httpauth::extractors::basic::{BasicAuth, Config}; + +async fn index(auth: BasicAuth) -> String { + format!("Hello, {}!", auth.user_id()) +} + +fn main() { + let app = App::new() + .data(Config::default().realm("Restricted area")) + .service(web::resource("/index.html").route(web::get().to(index))); +}
impl BasicAuth
[src]pub fn user_id(&self) -> &Cow<'static, str>
[src]Returns client’s user-ID.
+pub fn password(&self) -> Option<&Cow<'static, str>>
[src]Returns client’s password.
+impl AuthExtractor for BasicAuth
[src]type Error = AuthenticationError<Challenge>
The associated error which can be returned.
+type Future = Ready<Result<Self, Self::Error>>
Future that resolves into extracted credentials type.
+fn from_service_request(req: &ServiceRequest) -> Self::Future
[src]impl Clone for BasicAuth
[src]impl Debug for BasicAuth
[src]impl FromRequest for BasicAuth
[src]type Future = Ready<Result<Self, Self::Error>>
Future that resolves to a Self.
+type Config = Config
Configuration for this extractor.
+type Error = AuthenticationError<Challenge>
The associated error which can be returned.
+fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future
[src]pub fn extract(req: &HttpRequest) -> Self::Future
pub fn configure<F>(f: F) -> Self::Config where
F: FnOnce(Self::Config) -> Self::Config,
impl RefUnwindSafe for BasicAuth
impl Send for BasicAuth
impl Sync for BasicAuth
impl Unpin for BasicAuth
impl UnwindSafe for BasicAuth
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
BasicAuth
extractor configuration,
+used for WWW-Authenticate
header later.
impl Config
[src]pub fn realm<T>(self, value: T) -> Config where
T: Into<Cow<'static, str>>,
[src]Set challenge realm
attribute.
The “realm” attribute indicates the scope of protection in the manner +described in HTTP/1.1 RFC2617.
+impl AsRef<Basic> for Config
[src]impl AuthExtractorConfig for Config
[src]impl Clone for Config
[src]impl Debug for Config
[src]impl Default for Config
[src]impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnwindSafe for Config
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Bearer authorization error types, described in RFC 6750
+The request is missing a required parameter, includes an unsupported +parameter or parameter value, repeats the same parameter, uses more +than one method for including an access token, or is otherwise +malformed.
+The access token provided is expired, revoked, malformed, or invalid +for other reasons.
+The request requires higher privileges than provided by the access +token.
+impl Error
[src]pub fn status_code(&self) -> StatusCode
[src]Returns HTTP status code suitable for current error type.
+impl Clone for Error
[src]impl Copy for Error
[src]impl Debug for Error
[src]impl Display for Error
[src]impl Eq for Error
[src]impl Hash for Error
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl Ord for Error
[src]fn cmp(&self, other: &Error) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Error> for Error
[src]impl PartialOrd<Error> for Error
[src]fn partial_cmp(&self, other: &Error) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl StructuralEq for Error
[src]impl StructuralPartialEq for Error
[src]impl RefUnwindSafe for Error
impl Send for Error
impl Sync for Error
impl Unpin for Error
impl UnwindSafe for Error
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Extractor for the “Bearer” HTTP Authentication Scheme
+BearerAuth | Extractor for HTTP Bearer auth + |
Config | BearerAuth extractor configuration. + |
Error | Bearer authorization error types, described in RFC 6750 + |
Extractor for HTTP Bearer auth
++use actix_web_httpauth::extractors::bearer::BearerAuth; + +async fn index(auth: BearerAuth) -> String { + format!("Hello, user with token {}!", auth.token()) +}
If authentication fails, this extractor fetches the Config
instance
+from the [app data] in order to properly form the WWW-Authenticate
+response header.
+use actix_web::{web, App}; +use actix_web_httpauth::extractors::bearer::{BearerAuth, Config}; + +async fn index(auth: BearerAuth) -> String { + format!("Hello, {}!", auth.token()) +} + +fn main() { + let app = App::new() + .data( + Config::default() + .realm("Restricted area") + .scope("email photo"), + ) + .service(web::resource("/index.html").route(web::get().to(index))); +}
impl BearerAuth
[src]impl AuthExtractor for BearerAuth
[src]type Future = Ready<Result<Self, Self::Error>>
Future that resolves into extracted credentials type.
+type Error = AuthenticationError<Bearer>
The associated error which can be returned.
+fn from_service_request(req: &ServiceRequest) -> Self::Future
[src]impl Clone for BearerAuth
[src]fn clone(&self) -> BearerAuth
[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl Debug for BearerAuth
[src]impl FromRequest for BearerAuth
[src]type Config = Config
Configuration for this extractor.
+type Future = Ready<Result<Self, Self::Error>>
Future that resolves to a Self.
+type Error = AuthenticationError<Bearer>
The associated error which can be returned.
+fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future
[src]pub fn extract(req: &HttpRequest) -> Self::Future
pub fn configure<F>(f: F) -> Self::Config where
F: FnOnce(Self::Config) -> Self::Config,
impl RefUnwindSafe for BearerAuth
impl Send for BearerAuth
impl Sync for BearerAuth
impl Unpin for BearerAuth
impl UnwindSafe for BearerAuth
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
BearerAuth extractor configuration.
+impl Config
[src]pub fn scope<T: Into<Cow<'static, str>>>(self, value: T) -> Config
[src]Set challenge scope
attribute.
The "scope"
attribute is a space-delimited list of case-sensitive
+scope values indicating the required scope of the access token for
+accessing the requested resource.
pub fn realm<T: Into<Cow<'static, str>>>(self, value: T) -> Config
[src]Set challenge realm
attribute.
The “realm” attribute indicates the scope of protection in the manner +described in HTTP/1.1 RFC2617.
+impl AsRef<Bearer> for Config
[src]impl AuthExtractorConfig for Config
[src]impl Clone for Config
[src]impl Debug for Config
[src]impl Default for Config
[src]impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnwindSafe for Config
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redirecting to ../../../actix_web_httpauth/extractors/trait.AuthExtractorConfig.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/extractors/errors/struct.AuthenticationError.html b/actix_web_httpauth/extractors/errors/struct.AuthenticationError.html new file mode 100644 index 000000000..ab93b3583 --- /dev/null +++ b/actix_web_httpauth/extractors/errors/struct.AuthenticationError.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../actix_web_httpauth/extractors/struct.AuthenticationError.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/extractors/index.html b/actix_web_httpauth/extractors/index.html new file mode 100644 index 000000000..29a39b0b9 --- /dev/null +++ b/actix_web_httpauth/extractors/index.html @@ -0,0 +1,16 @@ +Type-safe authentication information extractors
+basic | Extractor for the “Basic” HTTP Authentication Scheme + |
bearer | Extractor for the “Bearer” HTTP Authentication Scheme + |
AuthenticationError | Authentication error returned by authentication extractors. + |
AuthExtractor | Trait implemented by types that can extract +HTTP authentication scheme credentials from the request. + |
AuthExtractorConfig | Trait implemented for types that provides configuration +for the authentication extractors. + |
Authentication error returned by authentication extractors.
+Different extractors may extend AuthenticationError
implementation
+in order to provide access to inner challenge fields.
impl AuthenticationError<Bearer>
[src]Extended error customization for HTTP Bearer
auth.
pub fn with_error(self, kind: Error) -> Self
[src]Attach Error
to the current Authentication error.
Error status code will be changed to the one provided by the kind
+Error.
pub fn with_error_description<T>(self, desc: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Attach error description to the current Authentication error.
+pub fn with_error_uri<T>(self, uri: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Attach error URI to the current Authentication error.
+It is up to implementor to provide properly formed absolute URI.
+impl<C: Challenge> AuthenticationError<C>
[src]pub fn new(challenge: C) -> AuthenticationError<C>
[src]Creates new authentication error from the provided challenge
.
By default returned error will resolve into the HTTP 401
status code.
pub fn challenge_mut(&mut self) -> &mut C
[src]Returns mutable reference to the inner challenge instance.
+pub fn status_code_mut(&mut self) -> &mut StatusCode
[src]Returns mutable reference to the inner status code.
+Can be used to override returned status code, but by default +this lib tries to stick to the RFC, so it might be unreasonable.
+impl<C: Debug + Challenge> Debug for AuthenticationError<C>
[src]impl<C: Challenge> Display for AuthenticationError<C>
[src]impl<C: 'static + Challenge> Error for AuthenticationError<C>
[src]pub fn source(&self) -> Option<&(dyn Error + 'static)>
1.30.0[src]pub fn backtrace(&self) -> Option<&Backtrace>
[src]pub fn description(&self) -> &str
1.0.0[src]pub fn cause(&self) -> Option<&dyn Error>
1.0.0[src]impl<T> From<T> for AuthenticationError<<T as AuthExtractorConfig>::Inner> where
T: AuthExtractorConfig,
[src]impl<C: 'static + Challenge> ResponseError for AuthenticationError<C>
[src]fn error_response(&self) -> HttpResponse
[src]fn status_code(&self) -> StatusCode
[src]impl<C> RefUnwindSafe for AuthenticationError<C> where
C: RefUnwindSafe,
impl<C> Send for AuthenticationError<C>
impl<C> Sync for AuthenticationError<C>
impl<C> Unpin for AuthenticationError<C> where
C: Unpin,
impl<C> UnwindSafe for AuthenticationError<C> where
C: UnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Trait implemented by types that can extract +HTTP authentication scheme credentials from the request.
+It is very similar to actix’ FromRequest
trait,
+except it operates with a ServiceRequest
struct instead,
+therefore it can be used in the middlewares.
You will not need it unless you want to implement your own +authentication scheme.
+type Error: Into<Error>
[src]The associated error which can be returned.
+type Future: Future<Output = Result<Self, Self::Error>>
[src]Future that resolves into extracted credentials type.
+fn from_service_request(req: &ServiceRequest) -> Self::Future
[src]Parse the authentication credentials from the actix’ ServiceRequest
.
impl AuthExtractor for BasicAuth
[src]type Error = AuthenticationError<Challenge>
type Future = Ready<Result<Self, Self::Error>>
fn from_service_request(req: &ServiceRequest) -> Self::Future
[src]impl AuthExtractor for BearerAuth
[src]type Future = Ready<Result<Self, Self::Error>>
type Error = AuthenticationError<Bearer>
fn from_service_request(req: &ServiceRequest) -> Self::Future
[src]Trait implemented for types that provides configuration +for the authentication extractors.
+fn into_inner(self) -> Self::Inner
[src]Convert the config instance into a HTTP challenge.
+impl AuthExtractorConfig for actix_web_httpauth::extractors::basic::Config
[src]type Inner = Challenge
fn into_inner(self) -> Self::Inner
[src]impl AuthExtractorConfig for actix_web_httpauth::extractors::bearer::Config
[src]type Inner = Bearer
fn into_inner(self) -> Self::Inner
[src]Possible errors while parsing Authorization
header.
Should not be used directly unless you are implementing +your own authentication scheme.
+Header value is malformed
+Authentication scheme is missing
+MissingField(&'static str)
Required authentication field is missing
+ToStrError(ToStrError)
Unable to convert header into the str
+Malformed base64 string
+Utf8Error(Utf8Error)
Malformed UTF-8 string
+impl Debug for ParseError
[src]impl Display for ParseError
[src]impl Error for ParseError
[src]fn source(&self) -> Option<&(dyn Error + 'static)>
[src]pub fn backtrace(&self) -> Option<&Backtrace>
[src]pub fn description(&self) -> &str
1.0.0[src]pub fn cause(&self) -> Option<&dyn Error>
1.0.0[src]impl From<DecodeError> for ParseError
[src]impl From<ToStrError> for ParseError
[src]fn from(e: ToStrError) -> Self
[src]impl From<Utf8Error> for ParseError
[src]impl RefUnwindSafe for ParseError
impl Send for ParseError
impl Sync for ParseError
impl Unpin for ParseError
impl UnwindSafe for ParseError
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redirecting to ../../../../actix_web_httpauth/headers/authorization/enum.ParseError.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/header/struct.Authorization.html b/actix_web_httpauth/headers/authorization/header/struct.Authorization.html new file mode 100644 index 000000000..7e2c31cdb --- /dev/null +++ b/actix_web_httpauth/headers/authorization/header/struct.Authorization.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../actix_web_httpauth/headers/authorization/struct.Authorization.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/index.html b/actix_web_httpauth/headers/authorization/index.html new file mode 100644 index 000000000..ca110545c --- /dev/null +++ b/actix_web_httpauth/headers/authorization/index.html @@ -0,0 +1,15 @@ +Authorization
header and various auth schemes
Authorization |
|
Basic | Credentials for |
Bearer | Credentials for |
ParseError | Possible errors while parsing |
Scheme | Authentication scheme for |
Redirecting to ../../../../../actix_web_httpauth/headers/authorization/struct.Basic.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/scheme/bearer/struct.Bearer.html b/actix_web_httpauth/headers/authorization/scheme/bearer/struct.Bearer.html new file mode 100644 index 000000000..2e9dead17 --- /dev/null +++ b/actix_web_httpauth/headers/authorization/scheme/bearer/struct.Bearer.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../actix_web_httpauth/headers/authorization/struct.Bearer.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/scheme/trait.Scheme.html b/actix_web_httpauth/headers/authorization/scheme/trait.Scheme.html new file mode 100644 index 000000000..11a90ef8d --- /dev/null +++ b/actix_web_httpauth/headers/authorization/scheme/trait.Scheme.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../actix_web_httpauth/headers/authorization/trait.Scheme.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/sidebar-items.js b/actix_web_httpauth/headers/authorization/sidebar-items.js new file mode 100644 index 000000000..69e4942b4 --- /dev/null +++ b/actix_web_httpauth/headers/authorization/sidebar-items.js @@ -0,0 +1 @@ +initSidebarItems({"enum":[["ParseError","Possible errors while parsing `Authorization` header."]],"struct":[["Authorization","`Authorization` header, defined in RFC 7235"],["Basic","Credentials for `Basic` authentication scheme, defined in RFC 7617"],["Bearer","Credentials for `Bearer` authentication scheme, defined in RFC6750"]],"trait":[["Scheme","Authentication scheme for `Authorization` header."]]}); \ No newline at end of file diff --git a/actix_web_httpauth/headers/authorization/struct.Authorization.html b/actix_web_httpauth/headers/authorization/struct.Authorization.html new file mode 100644 index 000000000..99881b176 --- /dev/null +++ b/actix_web_httpauth/headers/authorization/struct.Authorization.html @@ -0,0 +1,71 @@ +Authorization
header, defined in RFC 7235
The “Authorization” header field allows a user agent to authenticate +itself with an origin server – usually, but not necessarily, after +receiving a 401 (Unauthorized) response. Its value consists of +credentials containing the authentication information of the user +agent for the realm of the resource being requested.
+Authorization
header is generic over authentication
+scheme.
+fn handler(req: HttpRequest) -> Result<String> { + let auth = Authorization::<Basic>::parse(&req)?; + + Ok(format!("Hello, {}!", auth.as_ref().user_id())) +}
impl<S> Authorization<S> where
S: Scheme,
[src]pub fn into_scheme(self) -> S
[src]Consumes Authorization
header and returns inner Scheme
+implementation.
impl<S> AsMut<S> for Authorization<S> where
S: Scheme,
[src]impl<S> AsRef<S> for Authorization<S> where
S: Scheme,
[src]impl<S: Clone + Scheme> Clone for Authorization<S>
[src]fn clone(&self) -> Authorization<S>
[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl<S: Debug + Scheme> Debug for Authorization<S>
[src]impl<S: Default + Scheme> Default for Authorization<S>
[src]fn default() -> Authorization<S>
[src]impl<S: Scheme> Display for Authorization<S>
[src]impl<S: Eq + Scheme> Eq for Authorization<S>
[src]impl<S> From<S> for Authorization<S> where
S: Scheme,
[src]fn from(scheme: S) -> Authorization<S>
[src]impl<S: Hash + Scheme> Hash for Authorization<S>
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl<S: Scheme> Header for Authorization<S>
[src]impl<S: Scheme> IntoHeaderValue for Authorization<S>
[src]type Error = <S as IntoHeaderValue>::Error
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl<S: Ord + Scheme> Ord for Authorization<S>
[src]fn cmp(&self, other: &Authorization<S>) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl<S: PartialEq + Scheme> PartialEq<Authorization<S>> for Authorization<S>
[src]fn eq(&self, other: &Authorization<S>) -> bool
[src]fn ne(&self, other: &Authorization<S>) -> bool
[src]impl<S: PartialOrd + Scheme> PartialOrd<Authorization<S>> for Authorization<S>
[src]fn partial_cmp(&self, other: &Authorization<S>) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl<S: Scheme> StructuralEq for Authorization<S>
[src]impl<S: Scheme> StructuralPartialEq for Authorization<S>
[src]impl<S> RefUnwindSafe for Authorization<S> where
S: RefUnwindSafe,
impl<S> Send for Authorization<S>
impl<S> Sync for Authorization<S>
impl<S> Unpin for Authorization<S> where
S: Unpin,
impl<S> UnwindSafe for Authorization<S> where
S: UnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<!> for T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> IntoHeaderPair for T where
T: Header,
type Error = <T as IntoHeaderValue>::Error
pub fn try_into_header_pair(
self
) -> Result<(HeaderName, HeaderValue), <T as IntoHeaderPair>::Error>
impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Credentials for Basic
authentication scheme, defined in RFC 7617
impl Basic
[src]pub fn new<U, P>(user_id: U, password: Option<P>) -> Basic where
U: Into<Cow<'static, str>>,
P: Into<Cow<'static, str>>,
[src]Creates Basic
credentials with provided user_id
and optional
+password
.
+let credentials = Basic::new("Alladin", Some("open sesame"));
pub fn user_id(&self) -> &Cow<'static, str>
[src]Returns client’s user-ID.
+pub fn password(&self) -> Option<&Cow<'static, str>>
[src]Returns client’s password if provided.
+impl Clone for Basic
[src]impl Debug for Basic
[src]impl Display for Basic
[src]impl Eq for Basic
[src]impl IntoHeaderValue for Basic
[src]type Error = InvalidHeaderValue
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl Ord for Basic
[src]fn cmp(&self, other: &Basic) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Basic> for Basic
[src]impl PartialOrd<Basic> for Basic
[src]fn partial_cmp(&self, other: &Basic) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl Scheme for Basic
[src]fn parse(header: &HeaderValue) -> Result<Self, ParseError>
[src]impl StructuralEq for Basic
[src]impl StructuralPartialEq for Basic
[src]impl RefUnwindSafe for Basic
impl Send for Basic
impl Sync for Basic
impl Unpin for Basic
impl UnwindSafe for Basic
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Credentials for Bearer
authentication scheme, defined in RFC6750
Should be used in combination with
+Authorization
header.
impl Bearer
[src]pub fn new<T>(token: T) -> Bearer where
T: Into<Cow<'static, str>>,
[src]Creates new Bearer
credentials with the token provided.
+let credentials = Bearer::new("mF_9.B5f-4.1JqM");
pub fn token(&self) -> &Cow<'static, str>
[src]Gets reference to the credentials token.
+impl Clone for Bearer
[src]impl Debug for Bearer
[src]impl Display for Bearer
[src]impl Eq for Bearer
[src]impl IntoHeaderValue for Bearer
[src]type Error = InvalidHeaderValue
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl Ord for Bearer
[src]fn cmp(&self, other: &Bearer) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Bearer> for Bearer
[src]impl PartialOrd<Bearer> for Bearer
[src]fn partial_cmp(&self, other: &Bearer) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl Scheme for Bearer
[src]fn parse(header: &HeaderValue) -> Result<Self, ParseError>
[src]impl StructuralEq for Bearer
[src]impl StructuralPartialEq for Bearer
[src]impl RefUnwindSafe for Bearer
impl Send for Bearer
impl Sync for Bearer
impl Unpin for Bearer
impl UnwindSafe for Bearer
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Authentication scheme for Authorization
+header.
fn parse(header: &HeaderValue) -> Result<Self, ParseError>
[src]Try to parse the authentication scheme from the Authorization
header.
impl Scheme for Basic
[src]fn parse(header: &HeaderValue) -> Result<Self, ParseError>
[src]impl Scheme for Bearer
[src]fn parse(header: &HeaderValue) -> Result<Self, ParseError>
[src]Typed HTTP headers
+authorization |
|
www_authenticate |
|
Challenge for the “Basic” HTTP Authentication Scheme
+Basic | Challenge for |
Challenge for WWW-Authenticate
header with HTTP Basic auth scheme,
+described in RFC 7617
+use actix_web_httpauth::headers::www_authenticate::basic::Basic; +use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; + +fn index(_req: HttpRequest) -> HttpResponse { + let challenge = Basic::with_realm("Restricted area"); + + HttpResponse::Unauthorized() + .insert_header(WwwAuthenticate(challenge)) + .finish() +}
impl Basic
[src]pub fn new() -> Basic
[src]pub fn with_realm<T>(value: T) -> Basic where
T: Into<Cow<'static, str>>,
[src]Creates new Basic
challenge from the provided realm
field value.
+let challenge = Basic::with_realm("Restricted area");
+let my_realm = "Earth realm".to_string(); +let challenge = Basic::with_realm(my_realm);
impl AsRef<Basic> for Config
[src]impl Clone for Basic
[src]impl Debug for Basic
[src]impl Default for Basic
[src]impl Display for Basic
[src]impl Eq for Basic
[src]impl Hash for Basic
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl IntoHeaderValue for Basic
[src]type Error = InvalidHeaderValue
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl Ord for Basic
[src]fn cmp(&self, other: &Basic) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Basic> for Basic
[src]impl PartialOrd<Basic> for Basic
[src]fn partial_cmp(&self, other: &Basic) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl StructuralEq for Basic
[src]impl StructuralPartialEq for Basic
[src]impl RefUnwindSafe for Basic
impl Send for Basic
impl Sync for Basic
impl Unpin for Basic
impl UnwindSafe for Basic
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Bearer authorization error types, described in RFC 6750
+The request is missing a required parameter, includes an unsupported +parameter or parameter value, repeats the same parameter, uses more +than one method for including an access token, or is otherwise +malformed.
+The access token provided is expired, revoked, malformed, or invalid +for other reasons.
+The request requires higher privileges than provided by the access +token.
+impl Error
[src]pub fn status_code(&self) -> StatusCode
[src]Returns HTTP status code suitable for current error type.
+impl Clone for Error
[src]impl Copy for Error
[src]impl Debug for Error
[src]impl Display for Error
[src]impl Eq for Error
[src]impl Hash for Error
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl Ord for Error
[src]fn cmp(&self, other: &Error) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Error> for Error
[src]impl PartialOrd<Error> for Error
[src]fn partial_cmp(&self, other: &Error) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl StructuralEq for Error
[src]impl StructuralPartialEq for Error
[src]impl RefUnwindSafe for Error
impl Send for Error
impl Sync for Error
impl Unpin for Error
impl UnwindSafe for Error
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Challenge for the “Bearer” HTTP Authentication Scheme
+Bearer | Challenge for |
BearerBuilder | Builder for the |
Error | Bearer authorization error types, described in RFC 6750 + |
Challenge for WWW-Authenticate
header with HTTP Bearer auth scheme,
+described in RFC 6750
+use actix_web_httpauth::headers::www_authenticate::bearer::{ + Bearer, Error, +}; +use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; + +fn index(_req: HttpRequest) -> HttpResponse { + let challenge = Bearer::build() + .realm("example") + .scope("openid profile email") + .error(Error::InvalidToken) + .error_description("The access token expired") + .error_uri("http://example.org") + .finish(); + + HttpResponse::Unauthorized() + .insert_header(WwwAuthenticate(challenge)) + .finish() +}
impl Bearer
[src]pub fn build() -> BearerBuilder
[src]Creates the builder for Bearer
challenge.
+let challenge = Bearer::build() + .realm("Restricted area") + .scope("openid profile email") + .finish();
impl AsRef<Bearer> for Config
[src]impl Clone for Bearer
[src]impl Debug for Bearer
[src]impl Default for Bearer
[src]impl Display for Bearer
[src]impl Eq for Bearer
[src]impl Hash for Bearer
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl IntoHeaderValue for Bearer
[src]type Error = InvalidHeaderValue
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl Ord for Bearer
[src]fn cmp(&self, other: &Bearer) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl PartialEq<Bearer> for Bearer
[src]impl PartialOrd<Bearer> for Bearer
[src]fn partial_cmp(&self, other: &Bearer) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl StructuralEq for Bearer
[src]impl StructuralPartialEq for Bearer
[src]impl RefUnwindSafe for Bearer
impl Send for Bearer
impl Sync for Bearer
impl Unpin for Bearer
impl UnwindSafe for Bearer
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T> ToString for T where
T: Display + ?Sized,
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Builder for the Bearer
challenge.
It is up to implementor to fill all required fields,
+neither this Builder
or Bearer
does not provide any validation.
impl BearerBuilder
[src]pub fn scope<T>(self, value: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Provides the scope
attribute, as defined in RFC6749, Section 3.3
pub fn realm<T>(self, value: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Provides the realm
attribute, as defined in RFC2617
pub fn error(self, value: Error) -> Self
[src]Provides the error
attribute, as defined in RFC6750, Section 3.1
pub fn error_description<T>(self, value: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Provides the error_description
attribute, as defined in RFC6750, Section 3
pub fn error_uri<T>(self, value: T) -> Self where
T: Into<Cow<'static, str>>,
[src]Provides the error_uri
attribute, as defined in RFC6750, Section 3
It is up to implementor to provide properly-formed absolute URI.
+pub fn finish(self) -> Bearer
[src]Consumes the builder and returns built Bearer
instance.
impl Debug for BearerBuilder
[src]impl Default for BearerBuilder
[src]fn default() -> BearerBuilder
[src]impl RefUnwindSafe for BearerBuilder
impl Send for BearerBuilder
impl Sync for BearerBuilder
impl Unpin for BearerBuilder
impl UnwindSafe for BearerBuilder
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Redirecting to ../../../../../actix_web_httpauth/headers/www_authenticate/basic/index.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/basic/struct.Basic.html b/actix_web_httpauth/headers/www_authenticate/challenge/basic/struct.Basic.html new file mode 100644 index 000000000..62cd49f03 --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/basic/struct.Basic.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../actix_web_httpauth/headers/www_authenticate/basic/struct.Basic.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/bearer/builder/struct.BearerBuilder.html b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/builder/struct.BearerBuilder.html new file mode 100644 index 000000000..ad16ff0df --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/builder/struct.BearerBuilder.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../../actix_web_httpauth/headers/www_authenticate/bearer/struct.BearerBuilder.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/bearer/challenge/struct.Bearer.html b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/challenge/struct.Bearer.html new file mode 100644 index 000000000..29f95b245 --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/challenge/struct.Bearer.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../../actix_web_httpauth/headers/www_authenticate/bearer/struct.Bearer.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/bearer/errors/enum.Error.html b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/errors/enum.Error.html new file mode 100644 index 000000000..a4c048dfc --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/errors/enum.Error.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../../actix_web_httpauth/extractors/bearer/enum.Error.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/bearer/index.html b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/index.html new file mode 100644 index 000000000..5570fdcda --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/bearer/index.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../../actix_web_httpauth/headers/www_authenticate/bearer/index.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/challenge/trait.Challenge.html b/actix_web_httpauth/headers/www_authenticate/challenge/trait.Challenge.html new file mode 100644 index 000000000..24e29a275 --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/challenge/trait.Challenge.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../actix_web_httpauth/headers/www_authenticate/trait.Challenge.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/header/struct.WwwAuthenticate.html b/actix_web_httpauth/headers/www_authenticate/header/struct.WwwAuthenticate.html new file mode 100644 index 000000000..a9fe30952 --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/header/struct.WwwAuthenticate.html @@ -0,0 +1,10 @@ + + + + + + +Redirecting to ../../../../actix_web_httpauth/headers/www_authenticate/struct.WwwAuthenticate.html...
+ + + \ No newline at end of file diff --git a/actix_web_httpauth/headers/www_authenticate/index.html b/actix_web_httpauth/headers/www_authenticate/index.html new file mode 100644 index 000000000..af0793d98 --- /dev/null +++ b/actix_web_httpauth/headers/www_authenticate/index.html @@ -0,0 +1,13 @@ +WWW-Authenticate
header and various auth challenges
basic | Challenge for the “Basic” HTTP Authentication Scheme + |
bearer | Challenge for the “Bearer” HTTP Authentication Scheme + |
WwwAuthenticate |
|
Challenge | Authentication challenge for |
WWW-Authenticate
header, described in RFC 7235
This header is generic over Challenge trait, +see Basic and +Bearer challenges for details.
+impl<C: Clone + Challenge> Clone for WwwAuthenticate<C>
[src]fn clone(&self) -> WwwAuthenticate<C>
[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl<C: Debug + Challenge> Debug for WwwAuthenticate<C>
[src]impl<C: Default + Challenge> Default for WwwAuthenticate<C>
[src]fn default() -> WwwAuthenticate<C>
[src]impl<C: Eq + Challenge> Eq for WwwAuthenticate<C>
[src]impl<C: Hash + Challenge> Hash for WwwAuthenticate<C>
[src]fn hash<__H: Hasher>(&self, state: &mut __H)
[src]pub fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]impl<C: Challenge> Header for WwwAuthenticate<C>
[src]impl<C: Challenge> IntoHeaderValue for WwwAuthenticate<C>
[src]type Error = <C as IntoHeaderValue>::Error
The type returned in the event of a conversion error.
+fn try_into_value(self) -> Result<HeaderValue, Self::Error>
[src]impl<C: Ord + Challenge> Ord for WwwAuthenticate<C>
[src]fn cmp(&self, other: &WwwAuthenticate<C>) -> Ordering
[src]#[must_use]pub fn max(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn min(self, other: Self) -> Self
1.21.0[src]#[must_use]pub fn clamp(self, min: Self, max: Self) -> Self
1.50.0[src]impl<C: PartialEq + Challenge> PartialEq<WwwAuthenticate<C>> for WwwAuthenticate<C>
[src]fn eq(&self, other: &WwwAuthenticate<C>) -> bool
[src]fn ne(&self, other: &WwwAuthenticate<C>) -> bool
[src]impl<C: PartialOrd + Challenge> PartialOrd<WwwAuthenticate<C>> for WwwAuthenticate<C>
[src]fn partial_cmp(&self, other: &WwwAuthenticate<C>) -> Option<Ordering>
[src]#[must_use]pub fn lt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn le(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn gt(&self, other: &Rhs) -> bool
1.0.0[src]#[must_use]pub fn ge(&self, other: &Rhs) -> bool
1.0.0[src]impl<C: Challenge> StructuralEq for WwwAuthenticate<C>
[src]impl<C: Challenge> StructuralPartialEq for WwwAuthenticate<C>
[src]impl<C> RefUnwindSafe for WwwAuthenticate<C> where
C: RefUnwindSafe,
impl<C> Send for WwwAuthenticate<C>
impl<C> Sync for WwwAuthenticate<C>
impl<C> Unpin for WwwAuthenticate<C> where
C: Unpin,
impl<C> UnwindSafe for WwwAuthenticate<C> where
C: UnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> CallHasher for T where
T: Hash + ?Sized,
pub default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64 where
B: BuildHasher,
H: Hash + ?Sized,
impl<Q, K> Equivalent<K> for Q where
K: Borrow<Q> + ?Sized,
Q: Eq + ?Sized,
[src]pub fn equivalent(&self, key: &K) -> bool
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> IntoHeaderPair for T where
T: Header,
type Error = <T as IntoHeaderValue>::Error
pub fn try_into_header_pair(
self
) -> Result<(HeaderName, HeaderValue), <T as IntoHeaderPair>::Error>
impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
Authentication challenge for WWW-Authenticate
header.
fn to_bytes(&self) -> Bytes
[src]Converts the challenge into a bytes suitable for HTTP transmission.
+HTTP authentication schemes for actix-web.
+Provides:
+extractors | Type-safe authentication information extractors + |
headers | Typed HTTP headers + |
middleware | HTTP Authentication middleware. + |
HTTP Authentication middleware.
+HttpAuthentication | Middleware for checking HTTP authentication. + |
Middleware for checking HTTP authentication.
+If there is no Authorization
header in the request, this middleware returns an error
+immediately, without calling the F
callback.
Otherwise, it will pass both the request and the parsed credentials into it. In case of
+successful validation F
callback is required to return the ServiceRequest
back.
impl<T, F, O> HttpAuthentication<T, F> where
T: AuthExtractor,
F: Fn(ServiceRequest, T) -> O,
O: Future<Output = Result<ServiceRequest, Error>>,
[src]pub fn with_fn(process_fn: F) -> HttpAuthentication<T, F>
[src]Construct HttpAuthentication
middleware with the provided auth extractor T
and
+validation callback F
.
impl<F, O> HttpAuthentication<BasicAuth, F> where
F: Fn(ServiceRequest, BasicAuth) -> O,
O: Future<Output = Result<ServiceRequest, Error>>,
[src]pub fn basic(process_fn: F) -> Self
[src]Construct HttpAuthentication
middleware for the HTTP “Basic” authentication scheme.
+// In this example validator returns immediately, but since it is required to return +// anything that implements `IntoFuture` trait, it can be extended to query database or to +// do something else in a async manner. +async fn validator( + req: ServiceRequest, + credentials: BasicAuth, +) -> Result<ServiceRequest, Error> { + // All users are great and more than welcome! + Ok(req) +} + +let middleware = HttpAuthentication::basic(validator);
impl<F, O> HttpAuthentication<BearerAuth, F> where
F: Fn(ServiceRequest, BearerAuth) -> O,
O: Future<Output = Result<ServiceRequest, Error>>,
[src]pub fn bearer(process_fn: F) -> Self
[src]Construct HttpAuthentication
middleware for the HTTP “Bearer” authentication scheme.
+async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> { + if credentials.token() == "mF_9.B5f-4.1JqM" { + Ok(req) + } else { + let config = req.app_data::<Config>() + .map(|data| data.clone()) + .unwrap_or_else(Default::default) + .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13"); + + Err(AuthenticationError::from(config).into()) + } +} + +let middleware = HttpAuthentication::bearer(validator);
impl<T: Clone, F: Clone> Clone for HttpAuthentication<T, F> where
T: AuthExtractor,
[src]fn clone(&self) -> HttpAuthentication<T, F>
[src]pub fn clone_from(&mut self, source: &Self)
1.0.0[src]impl<T: Debug, F: Debug> Debug for HttpAuthentication<T, F> where
T: AuthExtractor,
[src]impl<S, B, T, F, O> Transform<S, ServiceRequest> for HttpAuthentication<T, F> where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
F: Fn(ServiceRequest, T) -> O + 'static,
O: Future<Output = Result<ServiceRequest, Error>> + 'static,
T: AuthExtractor + 'static,
[src]type Response = ServiceResponse<B>
Responses produced by the service.
+type Error = Error
Errors produced by the service.
+type Transform = AuthenticationMiddleware<S, F, T>
The TransformService
value created by this factory
type InitError = ()
Errors produced while building a transform service.
+type Future = Ready<Result<Self::Transform, Self::InitError>>
The future response value.
+fn new_transform(&self, service: S) -> Self::Future
[src]impl<T, F> RefUnwindSafe for HttpAuthentication<T, F> where
F: RefUnwindSafe,
T: RefUnwindSafe,
impl<T, F> Send for HttpAuthentication<T, F> where
F: Send + Sync,
T: Send,
impl<T, F> Sync for HttpAuthentication<T, F> where
F: Send + Sync,
T: Sync,
impl<T, F> Unpin for HttpAuthentication<T, F> where
T: Unpin,
impl<T, F> UnwindSafe for HttpAuthentication<T, F> where
F: RefUnwindSafe,
T: UnwindSafe,
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
"+""+(item.is_alias===true?(""+item.alias+" - see "):"")+item.displayPath+""+name+" | "+""+""+item.desc+" |
"+code.outerHTML+" |
fn:
) to \
+if(!String.prototype.startsWith){String.prototype.startsWith=function(searchString,position){position=position||0;return this.indexOf(searchString,position)===position}}if(!String.prototype.endsWith){String.prototype.endsWith=function(suffix,length){var l=length||this.length;return this.indexOf(suffix,l-suffix.length)!==-1}}if(!DOMTokenList.prototype.add){DOMTokenList.prototype.add=function(className){if(className&&!hasClass(this,className)){if(this.className&&this.className.length>0){this.className+=" "+className}else{this.className=className}}}}if(!DOMTokenList.prototype.remove){DOMTokenList.prototype.remove=function(className){if(className&&this.className){this.className=(" "+this.className+" ").replace(" "+className+" "," ").trim()}}}(function(){var rustdocVars=document.getElementById("rustdoc-vars");if(rustdocVars){window.rootPath=rustdocVars.attributes["data-root-path"].value;window.currentCrate=rustdocVars.attributes["data-current-crate"].value;window.searchJS=rustdocVars.attributes["data-search-js"].value}var sidebarVars=document.getElementById("sidebar-vars");if(sidebarVars){window.sidebarCurrent={name:sidebarVars.attributes["data-name"].value,ty:sidebarVars.attributes["data-ty"].value,relpath:sidebarVars.attributes["data-relpath"].value,}}}());function getVirtualKey(ev){if("key"in ev&&typeof ev.key!="undefined"){return ev.key}var c=ev.charCode||ev.keyCode;if(c==27){return"Escape"}return String.fromCharCode(c)}function getSearchInput(){return document.getElementsByClassName("search-input")[0]}function getSearchElement(){return document.getElementById("search")}function getThemesElement(){return document.getElementById("theme-choices")}function getThemePickerElement(){return document.getElementById("theme-picker")}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function focusSearchBar(){getSearchInput().focus()}function defocusSearchBar(){getSearchInput().blur()}(function(){"use strict";var itemTypes=["mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","primitive","associatedtype","constant","associatedconstant","union","foreigntype","keyword","existential","attr","derive","traitalias"];var disableShortcuts=getSettingValue("disable-shortcuts")==="true";var search_input=getSearchInput();var searchTimeout=null;var toggleAllDocsId="toggle-all-docs";var currentTab=0;var mouseMovedAfterSearch=true;var titleBeforeSearch=document.title;var searchTitle=null;function clearInputTimeout(){if(searchTimeout!==null){clearTimeout(searchTimeout);searchTimeout=null}}function getPageId(){if(window.location.hash){var tmp=window.location.hash.replace(/^#/,"");if(tmp.length>0){return tmp}}return null}function showSidebar(){var elems=document.getElementsByClassName("sidebar-elems")[0];if(elems){addClass(elems,"show-it")}var sidebar=document.getElementsByClassName("sidebar")[0];if(sidebar){addClass(sidebar,"mobile");var filler=document.getElementById("sidebar-filler");if(!filler){var div=document.createElement("div");div.id="sidebar-filler";sidebar.appendChild(div)}}}function hideSidebar(){var elems=document.getElementsByClassName("sidebar-elems")[0];if(elems){removeClass(elems,"show-it")}var sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"mobile");var filler=document.getElementById("sidebar-filler");if(filler){filler.remove()}document.getElementsByTagName("body")[0].style.marginTop=""}function showSearchResults(search){if(search===null||typeof search==='undefined'){search=getSearchElement()}addClass(main,"hidden");removeClass(search,"hidden");mouseMovedAfterSearch=false;document.title=searchTitle}function hideSearchResults(search){if(search===null||typeof search==='undefined'){search=getSearchElement()}addClass(search,"hidden");removeClass(main,"hidden");document.title=titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState("",window.currentCrate+" - Rust",getNakedUrl()+window.location.hash)}}var TY_PRIMITIVE=itemTypes.indexOf("primitive");var TY_KEYWORD=itemTypes.indexOf("keyword");function getQueryStringParams(){var params={};window.location.search.substring(1).split("&").map(function(s){var pair=s.split("=");params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function isHidden(elem){return elem.offsetHeight===0}var main=document.getElementById("main");var savedHash="";function handleHashes(ev){var elem;var search=getSearchElement();if(ev!==null&&search&&!hasClass(search,"hidden")&&ev.newURL){hideSearchResults(search);var hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(hash,"",getNakedUrl()+window.location.search+"#"+hash)}elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}if(savedHash!==window.location.hash){savedHash=window.location.hash;if(savedHash.length===0){return}elem=document.getElementById(savedHash.slice(1));if(!elem||!isHidden(elem)){return}var parent=elem.parentNode;if(parent&&hasClass(parent,"impl-items")){onEachLazy(parent.getElementsByClassName("collapsed"),function(e){if(e.parentNode===parent){e.click();return true}});if(isHidden(elem)){if(hasClass(parent.lastElementChild,"collapse-toggle")){parent.lastElementChild.click()}}}}}function highlightSourceLines(match,ev){if(typeof match==="undefined"){hideSidebar();match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/)}if(!match){return}var from=parseInt(match[1],10);var to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to"+""+(item.is_alias===true?(""+item.alias+" - see "):"")+item.displayPath+""+name+" | "+""+""+item.desc+" |
"+code.outerHTML+" |
fn:
) to \
restrict the search to a given item kind.","Accepted kinds are: fn
, mod
, struct
, \
enum
, trait
, type
, macro
, \
and const
.","Search functions by type signature (e.g., vec -> usize
or \
* -> vec
)","Search multiple things at once by splitting your query with comma (e.g., \
str,u8
or String,struct:Vec,test
)","You can look for items with an exact name by putting double quotes around \
- your request: \"string\"
","Look for items inside another one by searching for a path: vec::Vec
",].map(x=>""+x+"
").join("");var div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="\"string\"
","Look for items inside another one by searching for a path: vec::Vec
",].map(function(x){return""+x+"
"}).join("");var div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="pub(crate) async fn index(msg: ProtoBuf<MyObj>) -> Result<HttpResponse>
pub(crate) fn main() -> Result<()>
MyObj |
index | |
main |
number: i32
name: String
impl Clone for MyObj
[src]impl Debug for MyObj
[src]impl Default for MyObj
[src]impl Message for MyObj
[src]fn encode_raw<B>(&self, buf: &mut B) where
B: BufMut,
[src]fn merge_field<B>(
&mut self,
tag: u32,
wire_type: WireType,
buf: &mut B,
ctx: DecodeContext
) -> Result<(), DecodeError> where
B: Buf,
[src]fn encoded_len(&self) -> usize
[src]fn clear(&mut self)
[src]pub fn encode<B>(&self, buf: &mut B) -> Result<(), EncodeError> where
B: BufMut,
[src]pub fn encode_length_delimited<B>(&self, buf: &mut B) -> Result<(), EncodeError> where
B: BufMut,
[src]pub fn decode<B>(buf: B) -> Result<Self, DecodeError> where
Self: Default,
B: Buf,
[src]pub fn decode_length_delimited<B>(buf: B) -> Result<Self, DecodeError> where
Self: Default,
B: Buf,
[src]pub fn merge<B>(&mut self, buf: B) -> Result<(), DecodeError> where
B: Buf,
[src]pub fn merge_length_delimited<B>(&mut self, buf: B) -> Result<(), DecodeError> where
B: Buf,
[src]impl PartialEq<MyObj> for MyObj
[src]impl StructuralPartialEq for MyObj
[src]impl RefUnwindSafe for MyObj
impl Send for MyObj
impl Sync for MyObj
impl Unpin for MyObj
impl UnwindSafe for MyObj
impl<T> Any for T where
T: 'static + ?Sized,
[src]impl<T> Borrow<T> for T where
T: ?Sized,
[src]impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]pub fn borrow_mut(&mut self) -> &mut T
[src]impl<T> From<T> for T
[src]impl<T> Instrument for T
[src]pub fn instrument(self, span: Span) -> Instrumented<Self>
[src]pub fn in_current_span(self) -> Instrumented<Self>
[src]impl<T, U> Into<U> for T where
U: From<T>,
[src]impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T> ToOwned for T where
T: Clone,
[src]type Owned = T
The resulting type after obtaining ownership.
+pub fn to_owned(&self) -> T
[src]pub fn clone_into(&self, target: &mut T)
[src]impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]type Error = Infallible
The type returned in the event of a conversion error.
+pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
+pub fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
SameSite
cookie attribute.",null,null],[13,"Strict","","The \\\"Strict\\\" SameSite
attribute.",0,null],[13,"Lax","","The \\\"Lax\\\" SameSite
attribute.",0,null],[13,"None","","The \\\"None\\\" SameSite
attribute.",0,null],[4,"RespError","","",null,null],[13,"Internal","","A non-specific internal error that prevented an operation …",1,null],[13,"IO","","An IO error occurred",1,null],[13,"RESP","","A RESP parsing/serialising error occurred",1,null],[13,"Remote","","A remote error",1,null],[13,"Connection","","Error creating a connection, or an error with a …",1,null],[13,"Unexpected","","An unexpected error. In this context \\\"unexpected\\\" means …",1,null],[4,"RespValue","","A single RESP value, this owns the data that is …",null,null],[13,"Nil","","",2,null],[13,"Array","","Zero, one or more other RespValue
s.",2,null],[13,"BulkString","","A bulk string. In Redis terminology a string is a …",2,null],[13,"Error","","An error from the Redis server",2,null],[13,"Integer","","Redis documentation defines an integer as being a signed …",2,null],[13,"SimpleString","","",2,null],[3,"Command","","Command for send data to Redis",null,null],[12,"0","","",3,null],[3,"RedisActor","","Redis communication actor",null,null],[3,"RedisSession","","Use redis as session storage.",null,null],[4,"Error","","General purpose actix redis error",null,null],[13,"Redis","","",4,null],[13,"NotConnected","","Receiving message during reconnecting",4,null],[13,"Disconnected","","Cancel all waters when connection get dropped",4,null],[11,"from","","",0,[[]]],[11,"into","","",0,[[]]],[11,"to_owned","","",0,[[]]],[11,"clone_into","","",0,[[]]],[11,"to_string","","",0,[[],["string",3]]],[11,"borrow","","",0,[[]]],[11,"borrow_mut","","",0,[[]]],[11,"try_from","","",0,[[],["result",4]]],[11,"try_into","","",0,[[],["result",4]]],[11,"type_id","","",0,[[],["typeid",3]]],[11,"vzip","","",0,[[]]],[11,"equivalent","","",0,[[]]],[11,"get_hash","","",0,[[]]],[11,"from","","",1,[[]]],[11,"into","","",1,[[]]],[11,"to_string","","",1,[[],["string",3]]],[11,"borrow","","",1,[[]]],[11,"borrow_mut","","",1,[[]]],[11,"try_from","","",1,[[],["result",4]]],[11,"try_into","","",1,[[],["result",4]]],[11,"type_id","","",1,[[],["typeid",3]]],[11,"vzip","","",1,[[]]],[11,"from","","",2,[[]]],[11,"into","","",2,[[]]],[11,"to_owned","","",2,[[]]],[11,"clone_into","","",2,[[]]],[11,"borrow","","",2,[[]]],[11,"borrow_mut","","",2,[[]]],[11,"try_from","","",2,[[],["result",4]]],[11,"try_into","","",2,[[],["result",4]]],[11,"type_id","","",2,[[],["typeid",3]]],[11,"vzip","","",2,[[]]],[11,"equivalent","","",2,[[]]],[11,"from","","",3,[[]]],[11,"into","","",3,[[]]],[11,"borrow","","",3,[[]]],[11,"borrow_mut","","",3,[[]]],[11,"try_from","","",3,[[],["result",4]]],[11,"try_into","","",3,[[],["result",4]]],[11,"type_id","","",3,[[],["typeid",3]]],[11,"vzip","","",3,[[]]],[11,"from","","",5,[[]]],[11,"into","","",5,[[]]],[11,"borrow","","",5,[[]]],[11,"borrow_mut","","",5,[[]]],[11,"try_from","","",5,[[],["result",4]]],[11,"try_into","","",5,[[],["result",4]]],[11,"type_id","","",5,[[],["typeid",3]]],[11,"vzip","","",5,[[]]],[11,"from","","",6,[[]]],[11,"into","","",6,[[]]],[11,"borrow","","",6,[[]]],[11,"borrow_mut","","",6,[[]]],[11,"try_from","","",6,[[],["result",4]]],[11,"try_into","","",6,[[],["result",4]]],[11,"type_id","","",6,[[],["typeid",3]]],[11,"vzip","","",6,[[]]],[11,"from","","",4,[[]]],[11,"into","","",4,[[]]],[11,"to_string","","",4,[[],["string",3]]],[11,"borrow","","",4,[[]]],[11,"borrow_mut","","",4,[[]]],[11,"try_from","","",4,[[],["result",4]]],[11,"try_into","","",4,[[],["result",4]]],[11,"type_id","","",4,[[],["typeid",3]]],[11,"vzip","","",4,[[]]],[11,"from_resp_int","","",2,[[["respvalue",4]],[["result",4],["error",4],["respvalue",4]]]],[11,"fmt","","",1,[[["formatter",3]],[["result",4],["error",3]]]],[11,"from","","",2,[[["string",3]],["respvalue",4]]],[11,"from","","",2,[[],["respvalue",4]]],[11,"from","","",2,[[["string",3]],["respvalue",4]]],[11,"from","","",2,[[],["respvalue",4]]],[11,"from","","",2,[[["global",3],["vec",3]],["respvalue",4]]],[11,"from","","",2,[[["arc",3]],["respvalue",4]]],[11,"from","","",1,[[["trysenderror",3]],["error",4]]],[11,"from","","",1,[[["error",3]],["error",4]]],[11,"from","","",2,[[],["respvalue",4]]],[11,"source","","",1,[[],[["error",8],["option",4]]]],[11,"clone","","",2,[[],["respvalue",4]]],[11,"eq","","",2,[[["respvalue",4]]]],[11,"ne","","",2,[[["respvalue",4]]]],[11,"fmt","","",2,[[["formatter",3]],[["result",4],["error",3]]]],[11,"fmt","","",1,[[["formatter",3]],[["result",4],["error",3]]]],[11,"fmt","","",0,[[["formatter",3]],[["result",4],["error",3]]]],[11,"hash","","",0,[[]]],[11,"clone","","",0,[[],["samesite",4]]],[11,"eq","","",0,[[["samesite",4]]]],[11,"fmt","","",0,[[["formatter",3]],[["result",4],["error",3]]]],[11,"from","","",4,[[["error",4]],["error",4]]],[11,"fmt","","",3,[[["formatter",3]],["result",6]]],[11,"fmt","","",4,[[["formatter",3]],["result",6]]],[11,"fmt","","",4,[[["formatter",3]],["result",6]]],[11,"source","","",4,[[],[["option",4],["error",8]]]],[11,"started","","",5,[[["context",3]]]],[11,"restarting","","",5,[[]]],[11,"handle","","",5,[[["command",3]]]],[11,"handle","","",5,[[["respvalue",4],["resperror",4],["result",4]]]],[11,"error","","",5,[[["error",3]],["running",4]]],[11,"new_transform","","",6,[[]]],[11,"is_strict","","Returns true
if self
is SameSite::Strict
and false
…",0,[[]]],[11,"is_lax","","Returns true
if self
is SameSite::Lax
and false
otherwise.",0,[[]]],[11,"is_none","","Returns true
if self
is SameSite::None
and false
…",0,[[]]],[11,"append","","Convenience function for building dynamic Redis commands …",2,[[],["respvalue",4]]],[11,"push","","Push item to Resp array",2,[[]]],[11,"start","","Start new Supervisor
with RedisActor
.",5,[[["into",8],["string",3]],[["redisactor",3],["addr",3]]]],[11,"new","","Create new redis session backend",6,[[["into",8],["string",3]],["redissession",3]]],[11,"ttl","","Set time to live in seconds for session value.",6,[[]]],[11,"cookie_name","","Set custom cookie name for session ID.",6,[[]]],[11,"cookie_path","","Set custom cookie path.",6,[[]]],[11,"cookie_domain","","Set custom cookie domain.",6,[[]]],[11,"cookie_secure","","Set custom cookie secure.",6,[[]]],[11,"cookie_max_age","","Set custom cookie max-age.",6,[[]]],[11,"cookie_same_site","","Set custom cookie SameSite
attribute.",6,[[["samesite",4]]]],[11,"cookie_http_only","","Set custom cookie HttpOnly
policy.",6,[[]]],[11,"cache_keygen","","Set a custom cache key generation strategy, expecting …",6,[[["box",3],["fn",8]]]]],"p":[[4,"SameSite"],[4,"RespError"],[4,"RespValue"],[3,"Command"],[4,"Error"],[3,"RedisActor"],[3,"RedisSession"]]}\
+"actix_cors":{"doc":"Cross-Origin Resource Sharing (CORS) controls for Actix …","t":[3,4,13,13,13,13,13,13,13,13,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["Cors","CorsError","WildcardOrigin","MissingOrigin","MissingRequestMethod","BadRequestMethod","BadRequestHeaders","OriginNotAllowed","MethodNotAllowed","HeadersNotAllowed","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","clone","default","fmt","fmt","fmt","new_transform","status_code","error_response","permissive","allow_any_origin","allowed_origin","allowed_origin_fn","allow_any_method","allowed_methods","allow_any_header","allowed_header","allowed_headers","expose_any_header","expose_headers","max_age","send_wildcard","supports_credentials","disable_vary_header","disable_preflight"],"q":["actix_cors","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"d":["Builder for CORS middleware.","Errors that can occur when processing CORS guarded …","Allowed origin argument must not be wildcard (*
).","Request header Origin
is required but was not provided.","Request header Access-Control-Request-Method
is required …","Request header Access-Control-Request-Method
has an …","Request header Access-Control-Request-Headers
has an …","Origin is not allowed to make this request.","Request method is not allowed.","One or more request headers are not allowed.","","","","","","","","","","","","","","","","","","","","","A restrictive (security paranoid) set of defaults.","","","","","","","A very permissive set of default for quick development. …","Resets allowed origin list to a state where any origin is …","Add an origin that is allowed to make requests.","Determinate allowed origins by processing requests which …","Resets allowed methods list to all methods.","Set a list of methods which allowed origins can perform.","Resets allowed request header list to a state where any …","Add an allowed request header.","Set a list of request header field names which can be …","Resets exposed response header list to a state where any …","Set a list of headers which are safe to expose to the API …","Set a maximum time (in seconds) for which this CORS …","Set to use wildcard origins.","Allows users to make authenticated requests","Disable Vary
header support.","Disable support for preflight requests."],"i":[0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,1,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],"f":[null,null,null,null,null,null,null,null,null,null,[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[],["corserror",4]],[[],["cors",3]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[]],[[],["statuscode",3]],[[],["httpresponse",3]],[[]],[[],["cors",3]],[[["str",15]],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]],[[],["cors",3]]],"p":[[4,"CorsError"],[3,"Cors"]]},\
+"actix_identity":{"doc":"Request identity service for Actix applications.","t":[3,11,11,11,8,10,8,16,16,10,10,3,11,3,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["Identity","identity","remember","forget","RequestIdentity","get_identity","IdentityPolicy","Future","ResponseFuture","from_request","to_response","IdentityService","new","CookieIdentityPolicy","new","path","name","domain","secure","max_age","max_age_time","http_only","same_site","visit_deadline","login_deadline","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from_request","to_response","clone","new_transform","from_request"],"q":["actix_identity","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"d":["The extractor type to obtain your identity from a request.","Return the claimed identity of the user associated …","Remember identity.","This method is used to ‘forget’ the current identity …","Helper trait that allows to get Identity.","","Identity policy definition.","The return type of the middleware","The return type of the middleware","Parse the session from request and load data from a …","Write changes to response","Request identity middleware","Create new identity service with specified backend.","Use cookies for request identity storage.","Construct new CookieIdentityPolicy
instance.","Sets the path
field in the session cookie being built.","Sets the name
field in the session cookie being built.","Sets the domain
field in the session cookie being built.","Sets the secure
field in the session cookie being built.","Sets the max-age
field in the session cookie being built …","Sets the max-age
field in the session cookie being built …","Sets the http_only
field in the session cookie being …","Sets the same_site
field in the session cookie being …","Accepts only users whose cookie has been seen before the …","Accepts only users which has been authenticated before …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"i":[0,1,1,1,0,2,0,3,3,3,3,0,4,0,5,5,5,5,5,5,5,5,5,5,5,1,1,1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,1,4,1],"f":[null,[[],[["string",3],["option",4]]],[[["string",3]]],[[]],null,[[],[["string",3],["option",4]]],null,null,null,[[["servicerequest",3]]],[[["string",3],["option",4],["bool",15],["serviceresponse",3]]],null,[[]],null,[[],["cookieidentitypolicy",3]],[[["into",8],["string",3]],["cookieidentitypolicy",3]],[[["into",8],["string",3]],["cookieidentitypolicy",3]],[[["into",8],["string",3]],["cookieidentitypolicy",3]],[[["bool",15]],["cookieidentitypolicy",3]],[[["i64",15]],["cookieidentitypolicy",3]],[[["duration",3]],["cookieidentitypolicy",3]],[[["bool",15]]],[[["samesite",4]]],[[["duration",3]],["cookieidentitypolicy",3]],[[["duration",3]],["cookieidentitypolicy",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[["servicerequest",3]]],[[["string",3],["option",4],["bool",15],["serviceresponse",3]]],[[],["identity",3]],[[]],[[["httprequest",3],["payload",4]]]],"p":[[3,"Identity"],[8,"RequestIdentity"],[8,"IdentityPolicy"],[3,"IdentityService"],[3,"CookieIdentityPolicy"]]},\
+"actix_protobuf":{"doc":"","t":[4,13,13,13,13,13,3,12,3,11,3,11,11,8,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["ProtoBufPayloadError","Overflow","ContentType","Serialize","Deserialize","Payload","ProtoBuf","0","ProtoBufConfig","limit","ProtoBufMessage","new","limit","ProtoBufResponseBuilder","protobuf","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","into_future","try_poll","vzip","from","from","default","deref","deref_mut","fmt","fmt","fmt","fmt","poll","from_request","error_response","respond_to"],"q":["actix_protobuf","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"d":["","Payload size is bigger than 256k","Content type error","Serialize error","Deserialize error","Payload error","","","","Change max size of payload. By default max size is 256Kb","","Create ProtoBufMessage
for request.","Change max size of payload. By default max size is 256Kb","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"i":[0,1,1,1,1,1,0,2,0,3,0,4,4,0,5,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,1,1,3,2,2,1,2,1,2,4,2,1,2],"f":[null,null,null,null,null,null,null,null,null,[[["usize",15]]],null,[[["httprequest",3],["payload",4]]],[[["usize",15]]],null,[[["message",8]],[["httpresponse",3],["error",3],["result",4]]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[["pin",3],["context",3]],["poll",4]],[[]],[[["payloaderror",4]],["protobufpayloaderror",4]],[[["protobufdecodeerror",3]],["protobufpayloaderror",4]],[[]],[[]],[[]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["context",3],["pin",3]],["poll",4]],[[["httprequest",3],["payload",4]]],[[],["httpresponse",3]],[[["httprequest",3]],["httpresponse",3]]],"p":[[4,"ProtoBufPayloadError"],[3,"ProtoBuf"],[3,"ProtoBufConfig"],[3,"ProtoBufMessage"],[8,"ProtoBufResponseBuilder"]]},\
+"actix_redis":{"doc":"Redis integration for Actix and session store for Actix …","t":[3,12,3,4,13,13,13,3,4,13,13,13,4,13,13,13,13,13,13,4,13,13,13,13,13,13,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["Command","0","RedisActor","SameSite","Strict","Lax","None","RedisSession","Error","Redis","NotConnected","Disconnected","RespError","Internal","IO","RESP","Remote","Connection","Unexpected","RespValue","Nil","Array","BulkString","Error","Integer","SimpleString","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","equivalent","get_hash","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","equivalent","from_resp_int","fmt","clone","fmt","fmt","source","from","from","from","from","from","from","from","from","from","eq","ne","fmt","clone","fmt","hash","eq","from","fmt","fmt","fmt","source","started","restarting","handle","handle","error","new_transform","start","new","ttl","cookie_name","cookie_path","cookie_domain","cookie_secure","cookie_max_age","cookie_same_site","cookie_http_only","cache_keygen","is_strict","is_lax","is_none","append","push"],"q":["actix_redis","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"d":["Command for send data to Redis","","Redis communication actor","The SameSite
cookie attribute.","The “Strict” SameSite
attribute.","The “Lax” SameSite
attribute.","The “None” SameSite
attribute.","Use redis as session storage.","General purpose actix redis error","","Receiving message during reconnecting","Cancel all waters when connection get dropped","","A non-specific internal error that prevented an operation …","An IO error occurred","A RESP parsing/serialising error occurred","A remote error","Error creating a connection, or an error with a …","An unexpected error. In this context “unexpected” …","A single RESP value, this owns the data that is …","","Zero, one or more other RespValue
s.","A bulk string. In Redis terminology a string is a …","An error from the Redis server","Redis documentation defines an integer as being a signed …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Start new Supervisor
with RedisActor
.","Create new redis session backend","Set time to live in seconds for session value.","Set custom cookie name for session ID.","Set custom cookie path.","Set custom cookie domain.","Set custom cookie secure.","Set custom cookie max-age.","Set custom cookie SameSite
attribute.","Set custom cookie HttpOnly
policy.","Set a custom cache key generation strategy, expecting …","Returns true
if self
is SameSite::Strict
and false
…","Returns true
if self
is SameSite::Lax
and false
otherwise.","Returns true
if self
is SameSite::None
and false
…","Convenience function for building dynamic Redis commands …","Push item to Resp array"],"i":[0,1,0,0,2,2,2,0,0,3,3,3,0,4,4,4,4,4,4,0,5,5,5,5,5,5,1,1,1,1,1,1,1,1,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,4,5,5,4,4,5,5,4,5,5,5,5,4,5,5,5,2,2,2,2,2,3,1,3,3,3,6,6,6,6,6,7,6,7,7,7,7,7,7,7,7,7,7,2,2,2,5,5],"f":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[],["bool",15]],[[["respvalue",4]],[["error",4],["respvalue",4],["result",4]]],[[["formatter",3]],[["result",4],["error",3]]],[[],["respvalue",4]],[[["formatter",3]],[["result",4],["error",3]]],[[["formatter",3]],[["result",4],["error",3]]],[[],[["option",4],["error",8]]],[[["vec",3],["u8",15],["global",3]],["respvalue",4]],[[["usize",15]],["respvalue",4]],[[["trysenderror",3]],["error",4]],[[["string",3]],["respvalue",4]],[[["str",15]],["respvalue",4]],[[["arc",3],["str",15]],["respvalue",4]],[[],["respvalue",4]],[[["error",3]],["error",4]],[[["string",3]],["respvalue",4]],[[["respvalue",4]],["bool",15]],[[["respvalue",4]],["bool",15]],[[["formatter",3]],[["result",4],["error",3]]],[[],["samesite",4]],[[["formatter",3]],[["result",4],["error",3]]],[[]],[[["samesite",4]],["bool",15]],[[["error",4]],["error",4]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[],[["option",4],["error",8]]],[[["context",3]]],[[]],[[["command",3]]],[[["respvalue",4],["result",4],["resperror",4]]],[[["error",3]],["running",4]],[[]],[[["string",3],["into",8]],[["addr",3],["redisactor",3]]],[[["string",3],["into",8]],["redissession",3]],[[["u32",15]]],[[["str",15]]],[[["str",15]]],[[["str",15]]],[[["bool",15]]],[[]],[[["samesite",4]]],[[["bool",15]]],[[["box",3],["fn",8]]],[[],["bool",15]],[[],["bool",15]],[[],["bool",15]],[[],["respvalue",4]],[[]]],"p":[[3,"Command"],[4,"SameSite"],[4,"Error"],[4,"RespError"],[4,"RespValue"],[3,"RedisActor"],[3,"RedisSession"]]},\
+"actix_session":{"doc":"Sessions for Actix Web.","t":[3,3,8,10,4,13,13,13,13,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["CookieSession","Session","UserSession","get_session","SessionStatus","Changed","Purged","Renewed","Unchanged","get","set","remove","clear","purge","renew","set_session","get_changes","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","clone","default","eq","fmt","new_transform","from_request","signed","private","path","name","domain","lazy","secure","http_only","same_site","max_age","max_age_time","expires_in","expires_in_time"],"q":["actix_session","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],"d":["Use cookies for session storage.","The high-level interface you use to modify session data.","Extraction of a [Session
] object.","","","","","","","Get a value
from the session.","Set a value
from the session.","Remove value from the session.","Clear the session.","Removes session, both client and server side.","Renews the session key, assigning existing session state …","Adds the given key-value pairs to the session on the …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Construct new signed CookieSession
instance.","Construct new private CookieSession
instance.","Sets the path
field in the session cookie being built.","Sets the name
field in the session cookie being built.","Sets the domain
field in the session cookie being built.","When true, prevents adding session cookies to responses …","Sets the secure
field in the session cookie being built.","Sets the http_only
field in the session cookie being …","Sets the same_site
field in the session cookie being …","Sets the max-age
field in the session cookie being built.","Sets the max-age
field in the session cookie being built.","Sets the expires
field in the session cookie being built.","Sets the expires
field in the session cookie being built."],"i":[0,0,0,1,0,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,3,4,4,4,4,4,4,4,4,4,4,4,4,4],"f":[null,null,null,[[],["session",3]],null,null,null,null,null,[[["str",15]],[["option",4],["error",3],["result",4]]],[[["serialize",8],["str",15]],[["error",3],["result",4]]],[[["str",15]]],[[]],[[]],[[]],[[["servicerequest",3]]],[[["serviceresponse",3]]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[],["sessionstatus",4]],[[],["sessionstatus",4]],[[["sessionstatus",4]],["bool",15]],[[["formatter",3]],["result",6]],[[]],[[["httprequest",3],["payload",4]]],[[],["cookiesession",3]],[[],["cookiesession",3]],[[["into",8],["string",3]],["cookiesession",3]],[[["into",8],["string",3]],["cookiesession",3]],[[["into",8],["string",3]],["cookiesession",3]],[[["bool",15]],["cookiesession",3]],[[["bool",15]],["cookiesession",3]],[[["bool",15]],["cookiesession",3]],[[["samesite",4]],["cookiesession",3]],[[["i64",15]],["cookiesession",3]],[[["duration",3]],["cookiesession",3]],[[["i64",15]],["cookiesession",3]],[[["duration",3]],["cookiesession",3]]],"p":[[8,"UserSession"],[4,"SessionStatus"],[3,"Session"],[3,"CookieSession"]]},\
+"actix_web_httpauth":{"doc":"HTTP authentication schemes for actix-web.","t":[0,0,3,11,3,11,11,0,4,13,13,13,3,11,11,3,11,8,16,10,3,8,16,16,10,0,0,4,13,13,13,13,13,13,3,3,3,8,10,0,11,0,3,0,3,3,4,13,13,13,8,10,3,12,0,3,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["extractors","basic","Config","realm","BasicAuth","user_id","password","bearer","Error","InvalidRequest","InvalidToken","InsufficientScope","Config","scope","realm","BearerAuth","token","AuthExtractorConfig","Inner","into_inner","AuthenticationError","AuthExtractor","Error","Future","from_service_request","headers","authorization","ParseError","Invalid","MissingScheme","MissingField","ToStrError","Base64DecodeError","Utf8Error","Authorization","Basic","Bearer","Scheme","parse","www_authenticate","status_code","basic","Basic","bearer","BearerBuilder","Bearer","Error","InvalidRequest","InvalidToken","InsufficientScope","Challenge","to_bytes","WwwAuthenticate","0","middleware","HttpAuthentication","with_fn","basic","bearer","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","equivalent","get_hash","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_string","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","try_into_header_pair","equivalent","get_hash","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","equivalent","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","equivalent","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","equivalent","get_hash","vzip","from","into","borrow","borrow_mut","try_from","try_into","type_id","vzip","from","into","to_owned","clone_into","to_string","borrow","borrow_mut","try_from","try_into","type_id","equivalent","get_hash","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","try_into_header_pair","equivalent","get_hash","vzip","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","into_inner","into_inner","from_service_request","from_service_request","parse","parse","as_mut","as_ref","as_ref","as_ref","from","from","from","from","from","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","default","default","default","default","default","default","default","cmp","cmp","cmp","cmp","cmp","cmp","cmp","eq","ne","eq","ne","eq","ne","eq","ne","eq","ne","eq","eq","ne","partial_cmp","partial_cmp","partial_cmp","partial_cmp","partial_cmp","partial_cmp","partial_cmp","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","hash","hash","hash","hash","hash","source","new_transform","from_request","from_request","error_response","status_code","try_into_value","try_into_value","try_into_value","try_into_value","try_into_value","try_into_value","name","parse","name","parse","with_error","with_error_description","with_error_uri","new","challenge_mut","status_code_mut","into_scheme","new","user_id","password","new","token","new","with_realm","scope","realm","error","error_description","error_uri","finish","build"],"q":["actix_web_httpauth","actix_web_httpauth::extractors","actix_web_httpauth::extractors::basic","","","","","actix_web_httpauth::extractors","actix_web_httpauth::extractors::bearer","","","","","","","","","actix_web_httpauth::extractors","","","","","","","","actix_web_httpauth","actix_web_httpauth::headers","actix_web_httpauth::headers::authorization","","","","","","","","","","","","actix_web_httpauth::headers","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::www_authenticate::bearer","","","","","","actix_web_httpauth::headers::www_authenticate","","","","actix_web_httpauth","actix_web_httpauth::middleware","","","","actix_web_httpauth::extractors::basic","","","","","","","","","","","","","","","","","","","","actix_web_httpauth::extractors::bearer","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","actix_web_httpauth::extractors","","","","","","","","","actix_web_httpauth::headers::authorization","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","actix_web_httpauth::headers::www_authenticate::basic","","","","","","","","","","","","","actix_web_httpauth::headers::www_authenticate::bearer","","","","","","","","","","","","","","","","","","","","","actix_web_httpauth::headers::www_authenticate","","","","","","","","","","","","","actix_web_httpauth::middleware","","","","","","","","","","actix_web_httpauth::extractors::basic","actix_web_httpauth::extractors::bearer","actix_web_httpauth::extractors::basic","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::authorization","","","actix_web_httpauth::extractors::basic","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::authorization","actix_web_httpauth::extractors","actix_web_httpauth::headers::authorization","","","","actix_web_httpauth::extractors::basic","","actix_web_httpauth::extractors::bearer","","actix_web_httpauth::headers::authorization","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::middleware","actix_web_httpauth::extractors::basic","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::authorization","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::authorization","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::authorization","","","","","","actix_web_httpauth::headers::www_authenticate::basic","","actix_web_httpauth::headers::www_authenticate::bearer","","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","","actix_web_httpauth::headers::authorization","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::extractors::basic","","actix_web_httpauth::extractors::bearer","","actix_web_httpauth::extractors","actix_web_httpauth::headers::authorization","","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::middleware","actix_web_httpauth::extractors","actix_web_httpauth::headers::authorization","","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::authorization","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::extractors::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::authorization","actix_web_httpauth::middleware","actix_web_httpauth::extractors::basic","actix_web_httpauth::extractors::bearer","actix_web_httpauth::extractors","","actix_web_httpauth::headers::authorization","","","actix_web_httpauth::headers::www_authenticate::basic","actix_web_httpauth::headers::www_authenticate::bearer","actix_web_httpauth::headers::www_authenticate","actix_web_httpauth::headers::authorization","","actix_web_httpauth::headers::www_authenticate","","actix_web_httpauth::extractors","","","","","","actix_web_httpauth::headers::authorization","","","","","","actix_web_httpauth::headers::www_authenticate::basic","","actix_web_httpauth::headers::www_authenticate::bearer","","","","","",""],"d":["Type-safe authentication information extractors","Extractor for the “Basic” HTTP Authentication Scheme","BasicAuth
extractor configuration, used for …","Set challenge realm
attribute.","Extractor for HTTP Basic auth.","Returns client’s user-ID.","Returns client’s password.","Extractor for the “Bearer” HTTP Authentication Scheme","Bearer authorization error types, described in RFC 6750","The request is missing a required parameter, includes an …","The access token provided is expired, revoked, malformed, …","The request requires higher privileges than provided by …","BearerAuth extractor configuration.","Set challenge scope
attribute.","Set challenge realm
attribute.","Extractor for HTTP Bearer auth","Returns bearer token provided by client.","Trait implemented for types that provides configuration …","Associated challenge type.","Convert the config instance into a HTTP challenge.","Authentication error returned by authentication …","Trait implemented by types that can extract HTTP …","The associated error which can be returned.","Future that resolves into extracted credentials type.","Parse the authentication credentials from the actix’ …","Typed HTTP headers","Authorization
header and various auth schemes","Possible errors while parsing Authorization
header.","Header value is malformed","Authentication scheme is missing","Required authentication field is missing","Unable to convert header into the str","Malformed base64 string","Malformed UTF-8 string","Authorization
header, defined in RFC 7235","Credentials for Basic
authentication scheme, defined in …","Credentials for Bearer
authentication scheme, defined in …","Authentication scheme for Authorization
header.","Try to parse the authentication scheme from the …","WWW-Authenticate
header and various auth challenges","Returns HTTP status code suitable for current error type.","Challenge for the “Basic” HTTP Authentication Scheme","Challenge for WWW-Authenticate
header with HTTP Basic …","Challenge for the “Bearer” HTTP Authentication Scheme","Builder for the Bearer
challenge.","Challenge for WWW-Authenticate
header with HTTP Bearer …","Bearer authorization error types, described in RFC 6750","The request is missing a required parameter, includes an …","The access token provided is expired, revoked, malformed, …","The request requires higher privileges than provided by …","Authentication challenge for WWW-Authenticate
header.","Converts the challenge into a bytes suitable for HTTP …","WWW-Authenticate
header, described in RFC 7235","","HTTP Authentication middleware.","Middleware for checking HTTP authentication.","Construct HttpAuthentication
middleware with the provided …","Construct HttpAuthentication
middleware for the HTTP “…","Construct HttpAuthentication
middleware for the HTTP “…","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Attach Error
to the current Authentication error.","Attach error description to the current Authentication …","Attach error URI to the current Authentication error.","Creates new authentication error from the provided …","Returns mutable reference to the inner challenge instance.","Returns mutable reference to the inner status code.","Consumes Authorization
header and returns inner Scheme
…","Creates Basic
credentials with provided user_id
and …","Returns client’s user-ID.","Returns client’s password if provided.","Creates new Bearer
credentials with the token provided.","Gets reference to the credentials token.","Creates new Basic
challenge with an empty realm
field.","Creates new Basic
challenge from the provided realm
field …","Provides the scope
attribute, as defined in RFC6749, …","Provides the realm
attribute, as defined in RFC2617","Provides the error
attribute, as defined in RFC6750, …","Provides the error_description
attribute, as defined in …","Provides the error_uri
attribute, as defined in RFC6750, …","Consumes the builder and returns built Bearer
instance.","Creates the builder for Bearer
challenge."],"i":[0,0,0,1,0,2,2,0,0,3,3,3,0,4,4,0,5,0,6,6,0,0,7,7,7,0,0,0,8,8,8,8,8,8,0,0,0,0,9,0,3,0,0,0,0,0,0,3,3,3,0,10,0,11,0,0,12,12,12,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,13,13,13,13,13,13,13,13,13,8,8,8,8,8,8,8,8,8,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,1,4,2,5,15,16,14,1,4,14,13,8,8,8,14,1,2,4,5,14,15,16,17,19,3,11,12,1,4,14,17,18,19,11,14,15,16,17,19,3,11,14,14,15,15,16,16,17,17,19,19,3,11,11,14,15,16,17,19,3,11,1,2,4,5,13,8,14,15,16,17,18,19,3,11,12,13,8,14,15,16,17,19,3,14,17,19,3,11,8,12,2,5,13,13,14,15,16,17,19,11,14,14,11,11,13,13,13,13,13,13,14,15,15,15,16,16,17,17,18,18,18,18,18,18,19],"f":[null,null,null,[[],["config",3]],null,[[],["cow",4]],[[],[["option",4],["cow",4]]],null,null,null,null,null,null,[[["cow",4],["into",8]],["config",3]],[[["cow",4],["into",8]],["config",3]],null,[[],["str",15]],null,null,[[]],null,null,null,null,[[["servicerequest",3]]],null,null,null,null,null,null,null,null,null,null,null,null,null,[[["headervalue",3]],[["result",4],["parseerror",4]]],null,[[],["statuscode",3]],null,null,null,null,null,null,null,null,null,null,[[],["bytes",3]],null,null,null,null,[[],["httpauthentication",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["result",4]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["bool",15]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["bool",15]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[]],[[]],[[],["string",3]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[],["result",4]],[[],["bool",15]],[[],["u64",15]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[]],[[]],[[["servicerequest",3]]],[[["servicerequest",3]]],[[["headervalue",3]],[["result",4],["parseerror",4]]],[[["headervalue",3]],[["result",4],["parseerror",4]]],[[]],[[],["challenge",3]],[[],["bearer",3]],[[]],[[]],[[["tostrerror",3]]],[[["decodeerror",4]]],[[["utf8error",3]]],[[],["authorization",3]],[[],["config",3]],[[],["basicauth",3]],[[],["config",3]],[[],["bearerauth",3]],[[],["authorization",3]],[[],["basic",3]],[[],["bearer",3]],[[],["basic",3]],[[],["bearer",3]],[[],["error",4]],[[],["wwwauthenticate",3]],[[],["httpauthentication",3]],[[],["config",3]],[[],["config",3]],[[],["authorization",3]],[[],["basic",3]],[[],["bearerbuilder",3]],[[],["bearer",3]],[[],["wwwauthenticate",3]],[[["authorization",3]],["ordering",4]],[[["basic",3]],["ordering",4]],[[["bearer",3]],["ordering",4]],[[["basic",3]],["ordering",4]],[[["bearer",3]],["ordering",4]],[[["error",4]],["ordering",4]],[[["wwwauthenticate",3]],["ordering",4]],[[["authorization",3]],["bool",15]],[[["authorization",3]],["bool",15]],[[["basic",3]],["bool",15]],[[["basic",3]],["bool",15]],[[["bearer",3]],["bool",15]],[[["bearer",3]],["bool",15]],[[["basic",3]],["bool",15]],[[["basic",3]],["bool",15]],[[["bearer",3]],["bool",15]],[[["bearer",3]],["bool",15]],[[["error",4]],["bool",15]],[[["wwwauthenticate",3]],["bool",15]],[[["wwwauthenticate",3]],["bool",15]],[[["authorization",3]],[["ordering",4],["option",4]]],[[["basic",3]],[["ordering",4],["option",4]]],[[["bearer",3]],[["ordering",4],["option",4]]],[[["basic",3]],[["ordering",4],["option",4]]],[[["bearer",3]],[["ordering",4],["option",4]]],[[["error",4]],[["ordering",4],["option",4]]],[[["wwwauthenticate",3]],[["ordering",4],["option",4]]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],["result",6]],[[["formatter",3]],[["result",4],["error",3]]],[[["formatter",3]],[["result",4],["error",3]]],[[["formatter",3]],["result",6]],[[]],[[]],[[]],[[]],[[]],[[],[["option",4],["error",8]]],[[]],[[["httprequest",3],["payload",4]]],[[["httprequest",3],["payload",4]]],[[],["httpresponse",3]],[[],["statuscode",3]],[[],[["result",4],["headervalue",3]]],[[],[["result",4],["headervalue",3]]],[[],[["result",4],["headervalue",3]]],[[],[["result",4],["headervalue",3]]],[[],[["result",4],["headervalue",3]]],[[],[["result",4],["headervalue",3]]],[[],["headername",3]],[[],[["parseerror",4],["result",4]]],[[],["headername",3]],[[],[["parseerror",4],["result",4]]],[[["error",4]]],[[]],[[]],[[],["authenticationerror",3]],[[]],[[],["statuscode",3]],[[]],[[["option",4]],["basic",3]],[[],["cow",4]],[[],[["option",4],["cow",4]]],[[],["bearer",3]],[[],["cow",4]],[[],["basic",3]],[[],["basic",3]],[[]],[[]],[[["error",4]]],[[]],[[]],[[],["bearer",3]],[[],["bearerbuilder",3]]],"p":[[3,"Config"],[3,"BasicAuth"],[4,"Error"],[3,"Config"],[3,"BearerAuth"],[8,"AuthExtractorConfig"],[8,"AuthExtractor"],[4,"ParseError"],[8,"Scheme"],[8,"Challenge"],[3,"WwwAuthenticate"],[3,"HttpAuthentication"],[3,"AuthenticationError"],[3,"Authorization"],[3,"Basic"],[3,"Bearer"],[3,"Basic"],[3,"BearerBuilder"],[3,"Bearer"]]},\
+"prost_example":{"doc":"","t":[3,12,12,5,5,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11],"n":["MyObj","number","name","index","main","from","into","to_owned","clone_into","borrow","borrow_mut","try_from","try_into","type_id","vzip","clone","default","eq","ne","fmt","encode_raw","merge_field","encoded_len","clear"],"q":["prost_example","","","","","","","","","","","","","","","","","","","","","","",""],"d":["","","","","","","","","","","","","","","","","","","","","","","",""],"i":[0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"f":[null,null,null,[[["protobuf",3],["myobj",3]]],[[],["result",6]],[[]],[[]],[[]],[[]],[[]],[[]],[[],["result",4]],[[],["result",4]],[[],["typeid",3]],[[]],[[],["myobj",3]],[[]],[[["myobj",3]],["bool",15]],[[["myobj",3]],["bool",15]],[[["formatter",3]],["result",6]],[[]],[[["u32",15],["wiretype",4],["decodecontext",3]],[["decodeerror",3],["result",4]]],[[],["usize",15]],[[]]],"p":[[3,"MyObj"]]}\
}');
-addSearchOptions(searchIndex);initSearch(searchIndex);
\ No newline at end of file
+initSearch(searchIndex);
\ No newline at end of file
diff --git a/settings.html b/settings.html
index 88efdca1e..21ac3c70c 100644
--- a/settings.html
+++ b/settings.html
@@ -1,6 +1,7 @@
-1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +
+/// An enum signifying that some of type `T` is allowed, or `All` (anything is allowed). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AllOrSome<T> { + /// Everything is allowed. Usually equivalent to the `*` value. + All, + + /// Only some of `T` is allowed + Some(T), +} + +/// Default as `AllOrSome::All`. +impl<T> Default for AllOrSome<T> { + fn default() -> Self { + AllOrSome::All + } +} + +impl<T> AllOrSome<T> { + /// Returns whether this is an `All` variant. + pub fn is_all(&self) -> bool { + matches!(self, AllOrSome::All) + } + + /// Returns whether this is a `Some` variant. + #[allow(dead_code)] + pub fn is_some(&self) -> bool { + !self.is_all() + } + + /// Provides a shared reference to `T` if variant is `Some`. + pub fn as_ref(&self) -> Option<&T> { + match *self { + AllOrSome::All => None, + AllOrSome::Some(ref t) => Some(t), + } + } + + /// Provides a mutable reference to `T` if variant is `Some`. + pub fn as_mut(&mut self) -> Option<&mut T> { + match *self { + AllOrSome::All => None, + AllOrSome::Some(ref mut t) => Some(t), + } + } +} + +#[cfg(test)] +#[test] +fn tests() { + assert!(AllOrSome::<()>::All.is_all()); + assert!(!AllOrSome::<()>::All.is_some()); + + assert!(!AllOrSome::Some(()).is_all()); + assert!(AllOrSome::Some(()).is_some()); +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +
+use std::{collections::HashSet, convert::TryInto, iter::FromIterator, rc::Rc}; + +use actix_web::{ + dev::{RequestHead, Service, ServiceRequest, ServiceResponse, Transform}, + error::{Error, Result}, + http::{self, header::HeaderName, Error as HttpError, HeaderValue, Method, Uri}, + Either, +}; +use futures_util::future::{self, Ready}; +use log::error; +use once_cell::sync::Lazy; +use tinyvec::tiny_vec; + +use crate::{AllOrSome, CorsError, CorsMiddleware, Inner, OriginFn}; + +/// Convenience for getting mut refs to inner. Cleaner than `Rc::get_mut`. +/// Additionally, always causes first error (if any) to be reported during initialization. +fn cors<'a>( + inner: &'a mut Rc<Inner>, + err: &Option<Either<http::Error, CorsError>>, +) -> Option<&'a mut Inner> { + if err.is_some() { + return None; + } + + Rc::get_mut(inner) +} + +static ALL_METHODS_SET: Lazy<HashSet<Method>> = Lazy::new(|| { + HashSet::from_iter(vec![ + Method::GET, + Method::POST, + Method::PUT, + Method::DELETE, + Method::HEAD, + Method::OPTIONS, + Method::CONNECT, + Method::PATCH, + Method::TRACE, + ]) +}); + +/// Builder for CORS middleware. +/// +/// To construct a CORS middleware, call [`Cors::default()`] to create a blank, restrictive builder. +/// Then use any of the builder methods to customize CORS behavior. +/// +/// The alternative [`Cors::permissive()`] constructor is available for local development, allowing +/// all origins and headers, etc. **The permissive constructor should not be used in production.** +/// +/// # Errors +/// Errors surface in the middleware initialization phase. This means that, if you have logs enabled +/// in Actix Web (using `env_logger` or other crate that exposes logs from the `log` crate), error +/// messages will outline what is wrong with the CORS configuration in the server logs and the +/// server will fail to start up or serve requests. +/// +/// # Example +/// ```rust +/// use actix_cors::Cors; +/// use actix_web::http::header; +/// +/// let cors = Cors::default() +/// .allowed_origin("https://www.rust-lang.org") +/// .allowed_methods(vec!["GET", "POST"]) +/// .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) +/// .allowed_header(header::CONTENT_TYPE) +/// .max_age(3600); +/// +/// // `cors` can now be used in `App::wrap`. +/// ``` +#[derive(Debug)] +pub struct Cors { + inner: Rc<Inner>, + error: Option<Either<http::Error, CorsError>>, +} + +impl Cors { + /// A very permissive set of default for quick development. Not recommended for production use. + /// + /// *All* origins, methods, request headers and exposed headers allowed. Credentials supported. + /// Max age 1 hour. Does not send wildcard. + pub fn permissive() -> Self { + let inner = Inner { + allowed_origins: AllOrSome::All, + allowed_origins_fns: tiny_vec![], + + allowed_methods: ALL_METHODS_SET.clone(), + allowed_methods_baked: None, + + allowed_headers: AllOrSome::All, + allowed_headers_baked: None, + + expose_headers: AllOrSome::All, + expose_headers_baked: None, + max_age: Some(3600), + preflight: true, + send_wildcard: false, + supports_credentials: true, + vary_header: true, + }; + + Cors { + inner: Rc::new(inner), + error: None, + } + } + + /// Resets allowed origin list to a state where any origin is accepted. + /// + /// See [`Cors::allowed_origin`] for more info on allowed origins. + pub fn allow_any_origin(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.allowed_origins = AllOrSome::All; + } + + self + } + + /// Add an origin that is allowed to make requests. + /// + /// By default, requests from all origins are accepted by CORS logic. This method allows to + /// specify a finite set of origins to verify the value of the `Origin` request header. + /// + /// These are `origin-or-null` types in the [Fetch Standard]. + /// + /// When this list is set, the client's `Origin` request header will be checked in a + /// case-sensitive manner. + /// + /// When all origins are allowed and `send_wildcard` is set, `*` will be sent in the + /// `Access-Control-Allow-Origin` response header. If `send_wildcard` is not set, the client's + /// `Origin` request header will be echoed back in the `Access-Control-Allow-Origin` + /// response header. + /// + /// If the origin of the request doesn't match any allowed origins and at least one + /// `allowed_origin_fn` function is set, these functions will be used to determinate + /// allowed origins. + /// + /// # Initialization Errors + /// - If supplied origin is not valid uri + /// - If supplied origin is a wildcard (`*`). [`Cors::send_wildcard`] should be used instead. + /// + /// [Fetch Standard]: https://fetch.spec.whatwg.org/#origin-header + pub fn allowed_origin(mut self, origin: &str) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + match TryInto::<Uri>::try_into(origin) { + Ok(_) if origin == "*" => { + error!("Wildcard in `allowed_origin` is not allowed. Use `send_wildcard`."); + self.error = Some(Either::Right(CorsError::WildcardOrigin)); + } + + Ok(_) => { + if cors.allowed_origins.is_all() { + cors.allowed_origins = + AllOrSome::Some(HashSet::with_capacity(8)); + } + + if let Some(origins) = cors.allowed_origins.as_mut() { + // any uri is a valid header value + let hv = origin.try_into().unwrap(); + origins.insert(hv); + } + } + + Err(err) => { + self.error = Some(Either::Left(err.into())); + } + } + } + + self + } + + /// Determinate allowed origins by processing requests which didn't match any origins specified + /// in the `allowed_origin`. + /// + /// The function will receive two parameters, the Origin header value, and the `RequestHead` of + /// each request, which can be used to determine whether to allow the request or not. + /// + /// If the function returns `true`, the client's `Origin` request header will be echoed back + /// into the `Access-Control-Allow-Origin` response header. + pub fn allowed_origin_fn<F>(mut self, f: F) -> Cors + where + F: (Fn(&HeaderValue, &RequestHead) -> bool) + 'static, + { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.allowed_origins_fns.push(OriginFn { + boxed_fn: Rc::new(f), + }); + } + + self + } + + /// Resets allowed methods list to all methods. + /// + /// See [`Cors::allowed_methods`] for more info on allowed methods. + pub fn allow_any_method(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.allowed_methods = ALL_METHODS_SET.clone(); + } + + self + } + + /// Set a list of methods which allowed origins can perform. + /// + /// These will be sent in the `Access-Control-Allow-Methods` response header as specified in + /// the [Fetch Standard CORS protocol]. + /// + /// Defaults to `[GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]` + /// + /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol + pub fn allowed_methods<U, M>(mut self, methods: U) -> Cors + where + U: IntoIterator<Item = M>, + M: TryInto<Method>, + <M as TryInto<Method>>::Error: Into<HttpError>, + { + if let Some(cors) = cors(&mut self.inner, &self.error) { + for m in methods { + match m.try_into() { + Ok(method) => { + cors.allowed_methods.insert(method); + } + + Err(err) => { + self.error = Some(Either::Left(err.into())); + break; + } + } + } + } + + self + } + + /// Resets allowed request header list to a state where any header is accepted. + /// + /// See [`Cors::allowed_headers`] for more info on allowed request headers. + pub fn allow_any_header(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.allowed_headers = AllOrSome::All; + } + + self + } + + /// Add an allowed request header. + /// + /// See [`Cors::allowed_headers`] for more info on allowed request headers. + pub fn allowed_header<H>(mut self, header: H) -> Cors + where + H: TryInto<HeaderName>, + <H as TryInto<HeaderName>>::Error: Into<HttpError>, + { + if let Some(cors) = cors(&mut self.inner, &self.error) { + match header.try_into() { + Ok(method) => { + if cors.allowed_headers.is_all() { + cors.allowed_headers = + AllOrSome::Some(HashSet::with_capacity(8)); + } + + if let AllOrSome::Some(ref mut headers) = cors.allowed_headers { + headers.insert(method); + } + } + + Err(err) => self.error = Some(Either::Left(err.into())), + } + } + + self + } + + /// Set a list of request header field names which can be used when this resource is accessed by + /// allowed origins. + /// + /// If `All` is set, whatever is requested by the client in `Access-Control-Request-Headers` + /// will be echoed back in the `Access-Control-Allow-Headers` header as specified in + /// the [Fetch Standard CORS protocol]. + /// + /// Defaults to `All`. + /// + /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol + pub fn allowed_headers<U, H>(mut self, headers: U) -> Cors + where + U: IntoIterator<Item = H>, + H: TryInto<HeaderName>, + <H as TryInto<HeaderName>>::Error: Into<HttpError>, + { + if let Some(cors) = cors(&mut self.inner, &self.error) { + for h in headers { + match h.try_into() { + Ok(method) => { + if cors.allowed_headers.is_all() { + cors.allowed_headers = + AllOrSome::Some(HashSet::with_capacity(8)); + } + + if let AllOrSome::Some(ref mut headers) = cors.allowed_headers { + headers.insert(method); + } + } + Err(err) => { + self.error = Some(Either::Left(err.into())); + break; + } + } + } + } + + self + } + + /// Resets exposed response header list to a state where any header is accepted. + /// + /// See [`Cors::expose_headers`] for more info on exposed response headers. + pub fn expose_any_header(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.expose_headers = AllOrSome::All; + } + + self + } + + /// Set a list of headers which are safe to expose to the API of a CORS API specification. + /// This corresponds to the `Access-Control-Expose-Headers` response header as specified in + /// the [Fetch Standard CORS protocol]. + /// + /// This defaults to an empty set. + /// + /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol + pub fn expose_headers<U, H>(mut self, headers: U) -> Cors + where + U: IntoIterator<Item = H>, + H: TryInto<HeaderName>, + <H as TryInto<HeaderName>>::Error: Into<HttpError>, + { + for h in headers { + match h.try_into() { + Ok(header) => { + if let Some(cors) = cors(&mut self.inner, &self.error) { + if cors.expose_headers.is_all() { + cors.expose_headers = + AllOrSome::Some(HashSet::with_capacity(8)); + } + if let AllOrSome::Some(ref mut headers) = cors.expose_headers { + headers.insert(header); + } + } + } + Err(err) => { + self.error = Some(Either::Left(err.into())); + break; + } + } + } + + self + } + + /// Set a maximum time (in seconds) for which this CORS request maybe cached. + /// This value is set as the `Access-Control-Max-Age` header as specified in + /// the [Fetch Standard CORS protocol]. + /// + /// Pass a number (of seconds) or use None to disable sending max age header. + /// + /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol + pub fn max_age(mut self, max_age: impl Into<Option<usize>>) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.max_age = max_age.into() + } + + self + } + + /// Set to use wildcard origins. + /// + /// If send wildcard is set and the `allowed_origins` parameter is `All`, a wildcard + /// `Access-Control-Allow-Origin` response header is sent, rather than the request’s + /// `Origin` header. + /// + /// This **CANNOT** be used in conjunction with `allowed_origins` set to `All` and + /// `allow_credentials` set to `true`. Depending on the mode of usage, this will either result + /// in an `CorsError::CredentialsWithWildcardOrigin` error during actix launch or runtime. + /// + /// Defaults to `false`. + pub fn send_wildcard(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.send_wildcard = true + } + + self + } + + /// Allows users to make authenticated requests + /// + /// If true, injects the `Access-Control-Allow-Credentials` header in responses. This allows + /// cookies and credentials to be submitted across domains as specified in + /// the [Fetch Standard CORS protocol]. + /// + /// This option cannot be used in conjunction with an `allowed_origin` set to `All` and + /// `send_wildcards` set to `true`. + /// + /// Defaults to `false`. + /// + /// A server initialization error will occur if credentials are allowed, but the Origin is set + /// to send wildcards (`*`); this is not allowed by the CORS protocol. + /// + /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol + pub fn supports_credentials(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.supports_credentials = true + } + + self + } + + /// Disable `Vary` header support. + /// + /// When enabled the header `Vary: Origin` will be returned as per the Fetch Standard + /// implementation guidelines. + /// + /// Setting this header when the `Access-Control-Allow-Origin` is dynamically generated + /// (eg. when there is more than one allowed origin, and an Origin other than '*' is returned) + /// informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached. + /// + /// By default, `Vary` header support is enabled. + pub fn disable_vary_header(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.vary_header = false + } + + self + } + + /// Disable support for preflight requests. + /// + /// When enabled CORS middleware automatically handles `OPTIONS` requests. + /// This is useful for application level middleware. + /// + /// By default *preflight* support is enabled. + pub fn disable_preflight(mut self) -> Cors { + if let Some(cors) = cors(&mut self.inner, &self.error) { + cors.preflight = false + } + + self + } +} + +impl Default for Cors { + /// A restrictive (security paranoid) set of defaults. + /// + /// *No* allowed origins, methods, request headers or exposed headers. Credentials + /// not supported. No max age (will use browser's default). + fn default() -> Cors { + let inner = Inner { + allowed_origins: AllOrSome::Some(HashSet::with_capacity(8)), + allowed_origins_fns: tiny_vec![], + + allowed_methods: HashSet::with_capacity(8), + allowed_methods_baked: None, + + allowed_headers: AllOrSome::Some(HashSet::with_capacity(8)), + allowed_headers_baked: None, + + expose_headers: AllOrSome::Some(HashSet::with_capacity(8)), + expose_headers_baked: None, + + max_age: None, + preflight: true, + send_wildcard: false, + supports_credentials: false, + vary_header: true, + }; + + Cors { + inner: Rc::new(inner), + error: None, + } + } +} + +impl<S, B> Transform<S, ServiceRequest> for Cors +where + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse<B>; + type Error = Error; + type InitError = (); + type Transform = CorsMiddleware<S>; + type Future = Ready<Result<Self::Transform, Self::InitError>>; + + fn new_transform(&self, service: S) -> Self::Future { + if let Some(ref err) = self.error { + match err { + Either::Left(err) => error!("{}", err), + Either::Right(err) => error!("{}", err), + } + + return future::err(()); + } + + let mut inner = Rc::clone(&self.inner); + + if inner.supports_credentials + && inner.send_wildcard + && inner.allowed_origins.is_all() + { + error!("Illegal combination of CORS options: credentials can not be supported when all \ + origins are allowed and `send_wildcard` is enabled."); + return future::err(()); + } + + // bake allowed headers value if Some and not empty + match inner.allowed_headers.as_ref() { + Some(header_set) if !header_set.is_empty() => { + let allowed_headers_str = intersperse_header_values(header_set); + Rc::make_mut(&mut inner).allowed_headers_baked = + Some(allowed_headers_str); + } + _ => {} + } + + // bake allowed methods value if not empty + if !inner.allowed_methods.is_empty() { + let allowed_methods_str = intersperse_header_values(&inner.allowed_methods); + Rc::make_mut(&mut inner).allowed_methods_baked = Some(allowed_methods_str); + } + + // bake exposed headers value if Some and not empty + match inner.expose_headers.as_ref() { + Some(header_set) if !header_set.is_empty() => { + let expose_headers_str = intersperse_header_values(header_set); + Rc::make_mut(&mut inner).expose_headers_baked = Some(expose_headers_str); + } + _ => {} + } + + future::ok(CorsMiddleware { service, inner }) + } +} + +/// Only call when values are guaranteed to be valid header values and set is not empty. +fn intersperse_header_values<T>(val_set: &HashSet<T>) -> HeaderValue +where + T: AsRef<str>, +{ + val_set + .iter() + .fold(String::with_capacity(32), |mut acc, val| { + acc.push_str(", "); + acc.push_str(val.as_ref()); + acc + }) + // set is not empty so string will always have leading ", " to trim + [2..] + .try_into() + // all method names are valid header values + .unwrap() +} + +#[cfg(test)] +mod test { + use std::convert::{Infallible, TryInto}; + + use actix_web::{ + dev::Transform, + http::{HeaderName, StatusCode}, + test::{self, TestRequest}, + }; + + use super::*; + + #[test] + fn illegal_allow_credentials() { + // using the permissive defaults (all origins allowed) and adding send_wildcard + // and supports_credentials should error on construction + + assert!(Cors::permissive() + .supports_credentials() + .send_wildcard() + .new_transform(test::ok_service()) + .into_inner() + .is_err()); + } + + #[actix_rt::test] + async fn restrictive_defaults() { + let cors = Cors::default() + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::default() + .insert_header(("Origin", "https://www.example.com")) + .to_srv_request(); + + let resp = test::call_service(&cors, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + } + + #[actix_rt::test] + async fn allowed_header_try_from() { + let _cors = Cors::default().allowed_header("Content-Type"); + } + + #[actix_rt::test] + async fn allowed_header_try_into() { + struct ContentType; + + impl TryInto<HeaderName> for ContentType { + type Error = Infallible; + + fn try_into(self) -> Result<HeaderName, Self::Error> { + Ok(HeaderName::from_static("content-type")) + } + } + + let _cors = Cors::default().allowed_header(ContentType); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +
+use actix_web::{http::StatusCode, HttpResponse, ResponseError}; + +use derive_more::{Display, Error}; + +/// Errors that can occur when processing CORS guarded requests. +#[derive(Debug, Clone, Display, Error)] +#[non_exhaustive] +pub enum CorsError { + /// Allowed origin argument must not be wildcard (`*`). + #[display(fmt = "`allowed_origin` argument must not be wildcard (`*`).")] + WildcardOrigin, + + /// Request header `Origin` is required but was not provided. + #[display(fmt = "Request header `Origin` is required but was not provided.")] + MissingOrigin, + + /// Request header `Access-Control-Request-Method` is required but is missing. + #[display( + fmt = "Request header `Access-Control-Request-Method` is required but is missing." + )] + MissingRequestMethod, + + /// Request header `Access-Control-Request-Method` has an invalid value. + #[display( + fmt = "Request header `Access-Control-Request-Method` has an invalid value." + )] + BadRequestMethod, + + /// Request header `Access-Control-Request-Headers` has an invalid value. + #[display( + fmt = "Request header `Access-Control-Request-Headers` has an invalid value." + )] + BadRequestHeaders, + + /// Origin is not allowed to make this request. + #[display(fmt = "Origin is not allowed to make this request.")] + OriginNotAllowed, + + /// Request method is not allowed. + #[display(fmt = "Requested method is not allowed.")] + MethodNotAllowed, + + /// One or more request headers are not allowed. + #[display(fmt = "One or more request headers are not allowed.")] + HeadersNotAllowed, +} + +impl ResponseError for CorsError { + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST + } + + fn error_response(&self) -> HttpResponse { + HttpResponse::with_body(StatusCode::BAD_REQUEST, self.to_string().into()) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +
+use std::{collections::HashSet, convert::TryFrom, convert::TryInto, fmt, rc::Rc}; + +use actix_web::{ + dev::RequestHead, + error::Result, + http::{ + header::{self, HeaderName, HeaderValue}, + Method, + }, +}; +use once_cell::sync::Lazy; +use tinyvec::TinyVec; + +use crate::{AllOrSome, CorsError}; + +#[derive(Clone)] +pub(crate) struct OriginFn { + pub(crate) boxed_fn: Rc<dyn Fn(&HeaderValue, &RequestHead) -> bool>, +} + +impl Default for OriginFn { + /// Dummy default for use in tiny_vec. Do not use. + fn default() -> Self { + let boxed_fn: Rc<dyn Fn(&_, &_) -> _> = Rc::new(|_origin, _req_head| false); + Self { boxed_fn } + } +} + +impl fmt::Debug for OriginFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("origin_fn") + } +} + +/// Try to parse header value as HTTP method. +fn header_value_try_into_method(hdr: &HeaderValue) -> Option<Method> { + hdr.to_str() + .ok() + .and_then(|meth| Method::try_from(meth).ok()) +} + +#[derive(Debug, Clone)] +pub(crate) struct Inner { + pub(crate) allowed_origins: AllOrSome<HashSet<HeaderValue>>, + pub(crate) allowed_origins_fns: TinyVec<[OriginFn; 4]>, + + pub(crate) allowed_methods: HashSet<Method>, + pub(crate) allowed_methods_baked: Option<HeaderValue>, + + pub(crate) allowed_headers: AllOrSome<HashSet<HeaderName>>, + pub(crate) allowed_headers_baked: Option<HeaderValue>, + + /// `All` will echo back `Access-Control-Request-Header` list. + pub(crate) expose_headers: AllOrSome<HashSet<HeaderName>>, + pub(crate) expose_headers_baked: Option<HeaderValue>, + + pub(crate) max_age: Option<usize>, + pub(crate) preflight: bool, + pub(crate) send_wildcard: bool, + pub(crate) supports_credentials: bool, + pub(crate) vary_header: bool, +} + +static EMPTY_ORIGIN_SET: Lazy<HashSet<HeaderValue>> = Lazy::new(HashSet::new); + +impl Inner { + pub(crate) fn validate_origin(&self, req: &RequestHead) -> Result<(), CorsError> { + // return early if all origins are allowed or get ref to allowed origins set + #[allow(clippy::mutable_key_type)] + let allowed_origins = match &self.allowed_origins { + AllOrSome::All if self.allowed_origins_fns.is_empty() => return Ok(()), + AllOrSome::Some(allowed_origins) => allowed_origins, + // only function origin validators are defined + _ => &EMPTY_ORIGIN_SET, + }; + + // get origin header and try to parse as string + match req.headers().get(header::ORIGIN) { + // origin header exists and is a string + Some(origin) => { + if allowed_origins.contains(origin) + || self.validate_origin_fns(origin, req) + { + Ok(()) + } else { + Err(CorsError::OriginNotAllowed) + } + } + + // origin header is missing + // note: with our implementation, the origin header is required for OPTIONS request or + // else this would be unreachable + None => Err(CorsError::MissingOrigin), + } + } + + /// Accepts origin if _ANY_ functions return true. Only called when Origin exists. + fn validate_origin_fns(&self, origin: &HeaderValue, req: &RequestHead) -> bool { + self.allowed_origins_fns + .iter() + .any(|origin_fn| (origin_fn.boxed_fn)(origin, req)) + } + + /// Only called if origin exists and always after it's validated. + pub(crate) fn access_control_allow_origin( + &self, + req: &RequestHead, + ) -> Option<HeaderValue> { + let origin = req.headers().get(header::ORIGIN); + + match self.allowed_origins { + AllOrSome::All => { + if self.send_wildcard { + Some(HeaderValue::from_static("*")) + } else { + // see note below about why `.cloned()` is correct + origin.cloned() + } + } + + AllOrSome::Some(_) => { + // since origin (if it exists) is known to be allowed if this method is called + // then cloning the option is all that is required to be used as an echoed back + // header value (or omitted if None) + origin.cloned() + } + } + } + + /// Use in preflight checks and therefore operates on header list in + /// `Access-Control-Request-Headers` not the actual header set. + pub(crate) fn validate_allowed_method( + &self, + req: &RequestHead, + ) -> Result<(), CorsError> { + // extract access control header and try to parse as method + let request_method = req + .headers() + .get(header::ACCESS_CONTROL_REQUEST_METHOD) + .map(header_value_try_into_method); + + match request_method { + // method valid and allowed + Some(Some(method)) if self.allowed_methods.contains(&method) => Ok(()), + + // method valid but not allowed + Some(Some(_)) => Err(CorsError::MethodNotAllowed), + + // method invalid + Some(_) => Err(CorsError::BadRequestMethod), + + // method missing + None => Err(CorsError::MissingRequestMethod), + } + } + + pub(crate) fn validate_allowed_headers( + &self, + req: &RequestHead, + ) -> Result<(), CorsError> { + // return early if all headers are allowed or get ref to allowed origins set + #[allow(clippy::mutable_key_type)] + let allowed_headers = match &self.allowed_headers { + AllOrSome::All => return Ok(()), + AllOrSome::Some(allowed_headers) => allowed_headers, + }; + + // extract access control header as string + // header format should be comma separated header names + let request_headers = req + .headers() + .get(header::ACCESS_CONTROL_REQUEST_HEADERS) + .map(|hdr| hdr.to_str()); + + match request_headers { + // header list is valid string + Some(Ok(headers)) => { + // the set is ephemeral we take care not to mutate the + // inserted keys so this lint exception is acceptable + #[allow(clippy::mutable_key_type)] + let mut request_headers = HashSet::with_capacity(8); + + // try to convert each header name in the comma-separated list + for hdr in headers.split(',') { + match hdr.trim().try_into() { + Ok(hdr) => request_headers.insert(hdr), + Err(_) => return Err(CorsError::BadRequestHeaders), + }; + } + + // header list must contain 1 or more header name + if request_headers.is_empty() { + return Err(CorsError::BadRequestHeaders); + } + + // request header list must be a subset of allowed headers + if !request_headers.is_subset(allowed_headers) { + return Err(CorsError::HeadersNotAllowed); + } + + Ok(()) + } + + // header list is not a string + Some(Err(_)) => Err(CorsError::BadRequestHeaders), + + // header list missing + None => Ok(()), + } + } +} + +#[cfg(test)] +mod test { + use std::rc::Rc; + + use actix_web::{ + dev::Transform, + http::{header, HeaderValue, Method, StatusCode}, + test::{self, TestRequest}, + }; + + use crate::Cors; + + fn val_as_str(val: &HeaderValue) -> &str { + val.to_str().unwrap() + } + + #[actix_rt::test] + async fn test_validate_not_allowed_origin() { + let cors = Cors::default() + .allowed_origin("https://www.example.com") + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::get() + .insert_header((header::ORIGIN, "https://www.unknown.com")) + .insert_header((header::ACCESS_CONTROL_REQUEST_HEADERS, "DNT")) + .to_srv_request(); + + assert!(cors.inner.validate_origin(req.head()).is_err()); + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); + } + + #[actix_rt::test] + async fn test_preflight() { + let mut cors = Cors::default() + .allow_any_origin() + .send_wildcard() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + .allowed_header(header::CONTENT_TYPE) + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::default() + .method(Method::OPTIONS) + .insert_header(("Origin", "https://www.example.com")) + .insert_header((header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed")) + .to_srv_request(); + + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); + let resp = test::call_service(&cors, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + + let req = TestRequest::default() + .method(Method::OPTIONS) + .insert_header(("Origin", "https://www.example.com")) + .insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "put")) + .to_srv_request(); + + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); + + let req = TestRequest::default() + .method(Method::OPTIONS) + .insert_header(("Origin", "https://www.example.com")) + .insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST")) + .insert_header(( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + )) + .to_srv_request(); + + let resp = test::call_service(&cors, req).await; + assert_eq!( + Some(&b"*"[..]), + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .map(HeaderValue::as_bytes) + ); + assert_eq!( + Some(&b"3600"[..]), + resp.headers() + .get(header::ACCESS_CONTROL_MAX_AGE) + .map(HeaderValue::as_bytes) + ); + + let hdr = resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_HEADERS) + .map(val_as_str) + .unwrap(); + assert!(hdr.contains("authorization")); + assert!(hdr.contains("accept")); + assert!(hdr.contains("content-type")); + + let methods = resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_METHODS) + .unwrap() + .to_str() + .unwrap(); + assert!(methods.contains("POST")); + assert!(methods.contains("GET")); + assert!(methods.contains("OPTIONS")); + + Rc::get_mut(&mut cors.inner).unwrap().preflight = false; + + let req = TestRequest::default() + .method(Method::OPTIONS) + .insert_header(("Origin", "https://www.example.com")) + .insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST")) + .insert_header(( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + )) + .to_srv_request(); + + let resp = test::call_service(&cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +
+//! Cross-Origin Resource Sharing (CORS) controls for Actix Web. +//! +//! This middleware can be applied to both applications and resources. Once built, a +//! [`Cors`] builder can be used as an argument for Actix Web's `App::wrap()`, +//! `Scope::wrap()`, or `Resource::wrap()` methods. +//! +//! This CORS middleware automatically handles `OPTIONS` preflight requests. +//! +//! # Example +//! ```rust,no_run +//! use actix_cors::Cors; +//! use actix_web::{get, http, web, App, HttpRequest, HttpResponse, HttpServer}; +//! +//! #[get("/index.html")] +//! async fn index(req: HttpRequest) -> &'static str { +//! "<p>Hello World!</p>" +//! } +//! +//! #[actix_web::main] +//! async fn main() -> std::io::Result<()> { +//! HttpServer::new(|| { +//! let cors = Cors::default() +//! .allowed_origin("https://www.rust-lang.org/") +//! .allowed_origin_fn(|origin, _req_head| { +//! origin.as_bytes().ends_with(b".rust-lang.org") +//! }) +//! .allowed_methods(vec!["GET", "POST"]) +//! .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) +//! .allowed_header(http::header::CONTENT_TYPE) +//! .max_age(3600); +//! +//! App::new() +//! .wrap(cors) +//! .service(index) +//! }) +//! .bind(("127.0.0.1", 8080))? +//! .run() +//! .await; +//! +//! Ok(()) +//! } +//! ``` + +#![forbid(unsafe_code)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(missing_docs, missing_debug_implementations)] +#![doc(html_logo_url = "https://actix.rs/img/logo.png")] +#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] + +mod all_or_some; +mod builder; +mod error; +mod inner; +mod middleware; + +use all_or_some::AllOrSome; +pub use builder::Cors; +pub use error::CorsError; +use inner::{Inner, OriginFn}; +pub use middleware::CorsMiddleware; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +
+use std::{convert::TryInto, rc::Rc}; + +use actix_web::{ + dev::{Service, ServiceRequest, ServiceResponse}, + error::{Error, Result}, + http::{ + header::{self, HeaderValue}, + Method, + }, + HttpResponse, +}; +use futures_util::future::{ok, Either, FutureExt as _, LocalBoxFuture, Ready}; +use log::debug; + +use crate::Inner; + +/// Service wrapper for Cross-Origin Resource Sharing support. +/// +/// This struct contains the settings for CORS requests to be validated and for responses to +/// be generated. +#[doc(hidden)] +#[derive(Debug, Clone)] +pub struct CorsMiddleware<S> { + pub(crate) service: S, + pub(crate) inner: Rc<Inner>, +} + +impl<S> CorsMiddleware<S> { + fn handle_preflight<B>(inner: &Inner, req: ServiceRequest) -> ServiceResponse<B> { + if let Err(err) = inner + .validate_origin(req.head()) + .and_then(|_| inner.validate_allowed_method(req.head())) + .and_then(|_| inner.validate_allowed_headers(req.head())) + { + return req.error_response(err); + } + + let mut res = HttpResponse::Ok(); + + if let Some(origin) = inner.access_control_allow_origin(req.head()) { + res.insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, origin)); + } + + if let Some(ref allowed_methods) = inner.allowed_methods_baked { + res.insert_header(( + header::ACCESS_CONTROL_ALLOW_METHODS, + allowed_methods.clone(), + )); + } + + if let Some(ref headers) = inner.allowed_headers_baked { + res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone())); + } else if let Some(headers) = + req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS) + { + // all headers allowed, return + res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone())); + } + + if inner.supports_credentials { + res.insert_header(( + header::ACCESS_CONTROL_ALLOW_CREDENTIALS, + HeaderValue::from_static("true"), + )); + } + + if let Some(max_age) = inner.max_age { + res.insert_header((header::ACCESS_CONTROL_MAX_AGE, max_age.to_string())); + } + + let res = res.finish(); + let res = res.into_body(); + req.into_response(res) + } + + fn augment_response<B>( + inner: &Inner, + mut res: ServiceResponse<B>, + ) -> ServiceResponse<B> { + if let Some(origin) = inner.access_control_allow_origin(res.request().head()) { + res.headers_mut() + .insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin); + }; + + if let Some(ref expose) = inner.expose_headers_baked { + res.headers_mut() + .insert(header::ACCESS_CONTROL_EXPOSE_HEADERS, expose.clone()); + } + + if inner.supports_credentials { + res.headers_mut().insert( + header::ACCESS_CONTROL_ALLOW_CREDENTIALS, + HeaderValue::from_static("true"), + ); + } + + if inner.vary_header { + let value = match res.headers_mut().get(header::VARY) { + Some(hdr) => { + let mut val: Vec<u8> = Vec::with_capacity(hdr.len() + 8); + val.extend(hdr.as_bytes()); + val.extend(b", Origin"); + val.try_into().unwrap() + } + None => HeaderValue::from_static("Origin"), + }; + + res.headers_mut().insert(header::VARY, value); + } + + res + } +} + +type CorsMiddlewareServiceFuture<B> = Either< + Ready<Result<ServiceResponse<B>, Error>>, + LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>>, +>; + +impl<S, B> Service<ServiceRequest> for CorsMiddleware<S> +where + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse<B>; + type Error = Error; + type Future = CorsMiddlewareServiceFuture<B>; + + actix_service::forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + if self.inner.preflight && req.method() == Method::OPTIONS { + let inner = Rc::clone(&self.inner); + let res = Self::handle_preflight(&inner, req); + Either::Left(ok(res)) + } else { + let origin = req.headers().get(header::ORIGIN).cloned(); + + if origin.is_some() { + // Only check requests with a origin header. + if let Err(err) = self.inner.validate_origin(req.head()) { + debug!("origin validation failed; inner service is not called"); + return Either::Left(ok(req.error_response(err))); + } + } + + let inner = Rc::clone(&self.inner); + let fut = self.service.call(req); + + let res = async move { + let res = fut.await; + + if origin.is_some() { + let res = res?; + Ok(Self::augment_response(&inner, res)) + } else { + res + } + } + .boxed_local(); + + Either::Right(res) + } + } +} + +#[cfg(test)] +mod tests { + use actix_web::{ + dev::Transform, + test::{self, TestRequest}, + }; + + use super::*; + use crate::Cors; + + #[actix_rt::test] + async fn test_options_no_origin() { + // Tests case where allowed_origins is All but there are validate functions to run incase. + // In this case, origins are only allowed when the DNT header is sent. + + let cors = Cors::default() + .allow_any_origin() + .allowed_origin_fn(|origin, req_head| { + assert_eq!(&origin, req_head.headers.get(header::ORIGIN).unwrap()); + + req_head.headers().contains_key(header::DNT) + }) + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::get() + .insert_header((header::ORIGIN, "http://example.com")) + .to_srv_request(); + let res = cors.call(req).await.unwrap(); + assert_eq!( + None, + res.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .map(HeaderValue::as_bytes) + ); + + let req = TestRequest::get() + .insert_header((header::ORIGIN, "http://example.com")) + .insert_header((header::DNT, "1")) + .to_srv_request(); + let res = cors.call(req).await.unwrap(); + assert_eq!( + Some(&b"http://example.com"[..]), + res.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .map(HeaderValue::as_bytes) + ); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + 157 + 158 + 159 + 160 + 161 + 162 + 163 + 164 + 165 + 166 + 167 + 168 + 169 + 170 + 171 + 172 + 173 + 174 + 175 + 176 + 177 + 178 + 179 + 180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 203 + 204 + 205 + 206 + 207 + 208 + 209 + 210 + 211 + 212 + 213 + 214 + 215 + 216 + 217 + 218 + 219 + 220 + 221 + 222 + 223 + 224 + 225 + 226 + 227 + 228 + 229 + 230 + 231 + 232 + 233 + 234 + 235 + 236 + 237 + 238 + 239 + 240 + 241 + 242 + 243 + 244 + 245 + 246 + 247 + 248 + 249 + 250 + 251 + 252 + 253 + 254 + 255 + 256 + 257 + 258 + 259 + 260 + 261 + 262 + 263 + 264 + 265 + 266 + 267 + 268 + 269 + 270 + 271 + 272 + 273 + 274 + 275 + 276 + 277 + 278 + 279 + 280 + 281 + 282 + 283 + 284 + 285 + 286 + 287 + 288 + 289 + 290 + 291 + 292 + 293 + 294 + 295 + 296 + 297 + 298 + 299 + 300 + 301 + 302 + 303 + 304 + 305 + 306 + 307 + 308 + 309 + 310 + 311 + 312 + 313 + 314 + 315 + 316 + 317 + 318 + 319 + 320 + 321 + 322 + 323 + 324 + 325 + 326 + 327 + 328 + 329 + 330 + 331 + 332 + 333 + 334 + 335 + 336 + 337 + 338 + 339 + 340 + 341 + 342 + 343 + 344 + 345 + 346 + 347 + 348 + 349 + 350 + 351 + 352 + 353 + 354 + 355 + 356 + 357 + 358 + 359 + 360 + 361 + 362 + 363 + 364 + 365 + 366 + 367 + 368 + 369 + 370 + 371 + 372 + 373 + 374 + 375 + 376 + 377 + 378 + 379 + 380 + 381 + 382 + 383 + 384 + 385 + 386 + 387 + 388 + 389 + 390 + 391 + 392 + 393 + 394 + 395 + 396 + 397 + 398 + 399 + 400 + 401 + 402 + 403 + 404 + 405 + 406 + 407 + 408 + 409 + 410 + 411 + 412 + 413 + 414 + 415 + 416 + 417 + 418 + 419 + 420 + 421 + 422 + 423 + 424 + 425 + 426 + 427 + 428 + 429 + 430 + 431 + 432 + 433 + 434 + 435 + 436 + 437 + 438 + 439 + 440 + 441 + 442 + 443 + 444 + 445 + 446 + 447 + 448 + 449 + 450 + 451 + 452 + 453 + 454 + 455 + 456 + 457 + 458 + 459 + 460 + 461 + 462 + 463 + 464 + 465 + 466 + 467 + 468 + 469 + 470 + 471 + 472 + 473 + 474 + 475 + 476 + 477 + 478 + 479 + 480 + 481 + 482 + 483 + 484 + 485 + 486 + 487 + 488 + 489 + 490 + 491 + 492 + 493 + 494 + 495 + 496 + 497 + 498 + 499 + 500 + 501 + 502 + 503 + 504 + 505 + 506 + 507 + 508 + 509 + 510 + 511 + 512 + 513 + 514 + 515 + 516 + 517 + 518 + 519 + 520 + 521 + 522 + 523 + 524 + 525 + 526 + 527 + 528 + 529 + 530 + 531 + 532 + 533 + 534 + 535 + 536 + 537 + 538 + 539 + 540 + 541 + 542 + 543 + 544 + 545 + 546 + 547 + 548 + 549 + 550 + 551 + 552 + 553 + 554 + 555 + 556 + 557 + 558 + 559 + 560 + 561 + 562 + 563 + 564 + 565 + 566 + 567 + 568 + 569 + 570 + 571 + 572 + 573 + 574 + 575 + 576 + 577 + 578 + 579 + 580 + 581 + 582 + 583 + 584 + 585 + 586 + 587 + 588 + 589 + 590 + 591 + 592 + 593 + 594 + 595 + 596 + 597 + 598 + 599 + 600 + 601 + 602 + 603 + 604 + 605 + 606 + 607 + 608 + 609 + 610 + 611 + 612 + 613 + 614 + 615 + 616 + 617 + 618 + 619 + 620 + 621 + 622 + 623 + 624 + 625 + 626 + 627 + 628 + 629 + 630 + 631 + 632 + 633 + 634 + 635 + 636 + 637 + 638 + 639 + 640 + 641 + 642 + 643 + 644 + 645 + 646 + 647 + 648 + 649 + 650 + 651 + 652 + 653 + 654 + 655 + 656 + 657 + 658 + 659 + 660 + 661 + 662 + 663 + 664 + 665 + 666 + 667 + 668 + 669 + 670 + 671 + 672 + 673 + 674 + 675 + 676 + 677 + 678 + 679 + 680 + 681 + 682 + 683 + 684 + 685 + 686 + 687 + 688 + 689 + 690 + 691 + 692 + 693 + 694 + 695 + 696 + 697 + 698 + 699 + 700 + 701 + 702 + 703 + 704 + 705 + 706 + 707 + 708 + 709 + 710 + 711 + 712 + 713 + 714 + 715 + 716 + 717 + 718 + 719 + 720 + 721 + 722 + 723 + 724 + 725 + 726 + 727 + 728 + 729 + 730 + 731 + 732 + 733 + 734 + 735 + 736 + 737 + 738 + 739 + 740 + 741 + 742 + 743 + 744 + 745 + 746 + 747 + 748 + 749 + 750 + 751 + 752 + 753 + 754 + 755 + 756 + 757 + 758 + 759 + 760 + 761 + 762 + 763 + 764 + 765 + 766 + 767 + 768 + 769 + 770 + 771 + 772 + 773 + 774 + 775 + 776 + 777 + 778 + 779 + 780 + 781 + 782 + 783 + 784 + 785 + 786 + 787 + 788 + 789 + 790 + 791 + 792 + 793 + 794 + 795 + 796 + 797 + 798 + 799 + 800 + 801 + 802 + 803 + 804 + 805 + 806 + 807 + 808 + 809 + 810 + 811 + 812 + 813 + 814 + 815 + 816 + 817 + 818 + 819 + 820 + 821 + 822 + 823 + 824 + 825 + 826 + 827 + 828 + 829 + 830 + 831 + 832 + 833 + 834 + 835 + 836 + 837 + 838 + 839 + 840 + 841 + 842 + 843 + 844 + 845 + 846 + 847 + 848 + 849 + 850 + 851 + 852 + 853 + 854 + 855 + 856 + 857 + 858 + 859 + 860 + 861 + 862 + 863 + 864 + 865 + 866 + 867 + 868 + 869 + 870 + 871 + 872 + 873 + 874 + 875 + 876 + 877 + 878 + 879 + 880 + 881 + 882 + 883 + 884 + 885 + 886 + 887 + 888 + 889 + 890 + 891 + 892 + 893 + 894 + 895 + 896 + 897 + 898 + 899 + 900 + 901 + 902 + 903 + 904 + 905 + 906 + 907 + 908 + 909 + 910 + 911 + 912 + 913 + 914 + 915 + 916 + 917 + 918 + 919 + 920 + 921 + 922 + 923 + 924 + 925 + 926 + 927 + 928 + 929 + 930 + 931 + 932 + 933 + 934 + 935 + 936 + 937 + 938 + 939 + 940 + 941 + 942 + 943 + 944 + 945 + 946 + 947 + 948 + 949 + 950 + 951 + 952 + 953 + 954 + 955 + 956 + 957 + 958 + 959 + 960 + 961 + 962 + 963 + 964 + 965 + 966 + 967 + 968 + 969 + 970 + 971 + 972 + 973 + 974 + 975 + 976 + 977 + 978 + 979 + 980 + 981 + 982 + 983 + 984 + 985 + 986 + 987 + 988 + 989 + 990 + 991 + 992 + 993 + 994 + 995 + 996 + 997 + 998 + 999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +
+//! 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 +//! [**Identity**](struct.Identity.html) extractor should be used. +//! +//! ``` +//! use actix_web::*; +//! use actix_identity::{Identity, CookieIdentityPolicy, IdentityService}; +//! +//! async fn index(id: Identity) -> String { +//! // access request identity +//! if let Some(id) = id.identity() { +//! format!("Welcome! {}", id) +//! } else { +//! "Welcome Anonymous!".to_owned() +//! } +//! } +//! +//! async fn login(id: Identity) -> HttpResponse { +//! id.remember("User1".to_owned()); // <- remember identity +//! HttpResponse::Ok().finish() +//! } +//! +//! async fn logout(id: Identity) -> HttpResponse { +//! id.forget(); // <- remove identity +//! HttpResponse::Ok().finish() +//! } +//! +//! fn main() { +//! let app = App::new().wrap(IdentityService::new( +//! // <- create identity middleware +//! CookieIdentityPolicy::new(&[0; 32]) // <- create cookie identity policy +//! .name("auth-cookie") +//! .secure(false))) +//! .service(web::resource("/index.html").to(index)) +//! .service(web::resource("/login.html").to(login)) +//! .service(web::resource("/logout.html").to(logout)); +//! } +//! ``` + +#![deny(rust_2018_idioms)] + +use std::{future::Future, rc::Rc, time::SystemTime}; + +use actix_service::{Service, Transform}; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use serde::{Deserialize, Serialize}; +use time::Duration; + +use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; +use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; +use actix_web::error::{Error, Result}; +use actix_web::http::header::{self, HeaderValue}; +use actix_web::{FromRequest, HttpMessage, HttpRequest}; + +/// The extractor type to obtain your identity from a request. +/// +/// ```rust +/// use actix_web::*; +/// use actix_identity::Identity; +/// +/// fn index(id: Identity) -> Result<String> { +/// // access request identity +/// if let Some(id) = id.identity() { +/// Ok(format!("Welcome! {}", id)) +/// } else { +/// Ok("Welcome Anonymous!".to_owned()) +/// } +/// } +/// +/// fn login(id: Identity) -> HttpResponse { +/// id.remember("User1".to_owned()); // <- remember identity +/// HttpResponse::Ok().finish() +/// } +/// +/// fn logout(id: Identity) -> HttpResponse { +/// id.forget(); // <- remove identity +/// HttpResponse::Ok().finish() +/// } +/// # fn main() {} +/// ``` +#[derive(Clone)] +pub struct Identity(HttpRequest); + +impl Identity { + /// Return the claimed identity of the user associated request or + /// ``None`` if no identity can be found associated with the request. + pub fn identity(&self) -> Option<String> { + Identity::get_identity(&self.0.extensions()) + } + + /// Remember identity. + 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 + /// requests. + pub fn forget(&self) { + if let Some(id) = self.0.extensions_mut().get_mut::<IdentityItem>() { + id.id = None; + id.changed = true; + } + } + + fn get_identity(extensions: &Extensions) -> Option<String> { + if let Some(id) = extensions.get::<IdentityItem>() { + id.id.clone() + } else { + None + } + } +} + +struct IdentityItem { + id: Option<String>, + changed: bool, +} + +/// Helper trait that allows to get Identity. +/// +/// It could be used in middleware but identity policy must be set before any other middleware that needs identity +/// RequestIdentity is implemented both for `ServiceRequest` and `HttpRequest`. +pub trait RequestIdentity { + fn get_identity(&self) -> Option<String>; +} + +impl<T> RequestIdentity for T +where + T: HttpMessage, +{ + fn get_identity(&self) -> Option<String> { + Identity::get_identity(&self.extensions()) + } +} + +/// Extractor implementation for Identity type. +/// +/// ```rust +/// # use actix_web::*; +/// use actix_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 FromRequest for Identity { + type Config = (); + type Error = Error; + type Future = Ready<Result<Identity, Error>>; + + #[inline] + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + ok(Identity(req.clone())) + } +} + +/// Identity policy definition. +pub trait IdentityPolicy: Sized + 'static { + /// The return type of the middleware + type Future: Future<Output = Result<Option<String>, Error>>; + + /// The return type of the middleware + type ResponseFuture: Future<Output = Result<(), Error>>; + + /// Parse the session from request and load data from a service identity. + fn from_request(&self, request: &mut ServiceRequest) -> 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 +/// +/// ```rust +/// use actix_web::App; +/// use actix_identity::{CookieIdentityPolicy, IdentityService}; +/// +/// let app = App::new().wrap(IdentityService::new( +/// // <- create identity middleware +/// CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend +/// .name("auth-cookie") +/// .secure(false), +/// )); +/// ``` +pub struct IdentityService<T> { + backend: Rc<T>, +} + +impl<T> IdentityService<T> { + /// Create new identity service with specified backend. + pub fn new(backend: T) -> Self { + IdentityService { + backend: Rc::new(backend), + } + } +} + +impl<S, T, B> Transform<S, ServiceRequest> for IdentityService<T> +where + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, + S::Future: 'static, + T: IdentityPolicy, + B: 'static, +{ + type Response = ServiceResponse<B>; + type Error = Error; + type InitError = (); + type Transform = IdentityServiceMiddleware<S, T>; + type Future = Ready<Result<Self::Transform, Self::InitError>>; + + fn new_transform(&self, service: S) -> Self::Future { + ok(IdentityServiceMiddleware { + backend: self.backend.clone(), + service: Rc::new(service), + }) + } +} + +#[doc(hidden)] +pub struct IdentityServiceMiddleware<S, T> { + service: Rc<S>, + backend: Rc<T>, +} + +impl<S, T> Clone for IdentityServiceMiddleware<S, T> { + fn clone(&self) -> Self { + Self { + backend: Rc::clone(&self.backend), + service: Rc::clone(&self.service), + } + } +} + +impl<S, T, B> Service<ServiceRequest> for IdentityServiceMiddleware<S, T> +where + B: 'static, + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, + S::Future: 'static, + T: IdentityPolicy, +{ + type Response = ServiceResponse<B>; + type Error = Error; + type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>; + + actix_service::forward_ready!(service); + + fn call(&self, mut req: ServiceRequest) -> Self::Future { + let srv = Rc::clone(&self.service); + let backend = Rc::clone(&self.backend); + let fut = self.backend.from_request(&mut req); + + async move { + match fut.await { + Ok(id) => { + req.extensions_mut() + .insert(IdentityItem { id, changed: false }); + + let mut res = srv.call(req).await?; + let id = res.request().extensions_mut().remove::<IdentityItem>(); + + if let Some(id) = id { + match backend.to_response(id.id, id.changed, &mut res).await { + Ok(_) => Ok(res), + Err(e) => Ok(res.error_response(e)), + } + } else { + Ok(res) + } + } + Err(err) => Ok(req.error_response(err)), + } + } + .boxed_local() + } +} + +struct CookieIdentityInner { + key: Key, + key_v2: Key, + name: String, + path: String, + domain: Option<String>, + secure: bool, + max_age: Option<Duration>, + http_only: Option<bool>, + same_site: Option<SameSite>, + visit_deadline: Option<Duration>, + login_deadline: Option<Duration>, +} + +#[derive(Deserialize, Serialize, Debug)] +struct CookieValue { + identity: String, + + #[serde(skip_serializing_if = "Option::is_none")] + login_timestamp: Option<SystemTime>, + + #[serde(skip_serializing_if = "Option::is_none")] + visit_timestamp: Option<SystemTime>, +} + +#[derive(Debug)] +struct CookieIdentityExtension { + login_timestamp: Option<SystemTime>, +} + +impl CookieIdentityInner { + fn new(key: &[u8]) -> CookieIdentityInner { + let key_v2: Vec<u8> = key.iter().chain([1, 0, 0, 0].iter()).cloned().collect(); + CookieIdentityInner { + key: Key::derive_from(key), + key_v2: Key::derive_from(&key_v2), + name: "actix-identity".to_owned(), + path: "/".to_owned(), + domain: None, + secure: true, + max_age: None, + http_only: None, + same_site: None, + visit_deadline: None, + login_deadline: None, + } + } + + fn set_cookie<B>( + &self, + resp: &mut ServiceResponse<B>, + value: Option<CookieValue>, + ) -> Result<()> { + let add_cookie = value.is_some(); + let val = value.map(|val| { + if !self.legacy_supported() { + serde_json::to_string(&val) + } else { + Ok(val.identity) + } + }); + let mut cookie = + Cookie::new(self.name.clone(), val.unwrap_or_else(|| Ok(String::new()))?); + 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(http_only) = self.http_only { + cookie.set_http_only(http_only); + } + + if let Some(same_site) = self.same_site { + cookie.set_same_site(same_site); + } + + let mut jar = CookieJar::new(); + let key = if self.legacy_supported() { + &self.key + } else { + &self.key_v2 + }; + if add_cookie { + jar.private(&key).add(cookie); + } else { + jar.add_original(cookie.clone()); + jar.private(&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(&self, req: &ServiceRequest) -> Option<CookieValue> { + let cookie = req.cookie(&self.name)?; + let mut jar = CookieJar::new(); + jar.add_original(cookie.clone()); + let res = if self.legacy_supported() { + jar.private(&self.key).get(&self.name).map(|n| CookieValue { + identity: n.value().to_string(), + login_timestamp: None, + visit_timestamp: None, + }) + } else { + None + }; + res.or_else(|| { + jar.private(&self.key_v2) + .get(&self.name) + .and_then(|c| self.parse(c)) + }) + } + + fn parse(&self, cookie: Cookie<'_>) -> Option<CookieValue> { + let value: CookieValue = serde_json::from_str(cookie.value()).ok()?; + let now = SystemTime::now(); + if let Some(visit_deadline) = self.visit_deadline { + if now.duration_since(value.visit_timestamp?).ok()? > visit_deadline { + return None; + } + } + if let Some(login_deadline) = self.login_deadline { + if now.duration_since(value.login_timestamp?).ok()? > login_deadline { + return None; + } + } + Some(value) + } + + fn legacy_supported(&self) -> bool { + self.visit_deadline.is_none() && self.login_deadline.is_none() + } + + fn always_update_cookie(&self) -> bool { + self.visit_deadline.is_some() + } + + fn requires_oob_data(&self) -> bool { + self.login_deadline.is_some() + } +} + +/// 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 +/// use actix_web::App; +/// use actix_identity::{CookieIdentityPolicy, IdentityService}; +/// +/// let app = App::new().wrap(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 with given number of seconds. + pub fn max_age(self, seconds: i64) -> CookieIdentityPolicy { + self.max_age_time(Duration::seconds(seconds)) + } + + /// Sets the `max-age` field in the session cookie being built with `time::Duration`. + pub fn max_age_time(mut self, value: Duration) -> CookieIdentityPolicy { + Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); + self + } + + /// Sets the `http_only` field in the session cookie being built. + pub fn http_only(mut self, http_only: bool) -> Self { + Rc::get_mut(&mut self.0).unwrap().http_only = Some(http_only); + 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 + } + + /// Accepts only users whose cookie has been seen before the given deadline + /// + /// By default visit deadline is disabled. + pub fn visit_deadline(mut self, value: Duration) -> CookieIdentityPolicy { + Rc::get_mut(&mut self.0).unwrap().visit_deadline = Some(value); + self + } + + /// Accepts only users which has been authenticated before the given deadline + /// + /// By default login deadline is disabled. + pub fn login_deadline(mut self, value: Duration) -> CookieIdentityPolicy { + Rc::get_mut(&mut self.0).unwrap().login_deadline = Some(value); + self + } +} + +impl IdentityPolicy for CookieIdentityPolicy { + type Future = Ready<Result<Option<String>, Error>>; + type ResponseFuture = Ready<Result<(), Error>>; + + fn from_request(&self, req: &mut ServiceRequest) -> Self::Future { + ok(self.0.load(req).map( + |CookieValue { + identity, + login_timestamp, + .. + }| { + if self.0.requires_oob_data() { + req.extensions_mut() + .insert(CookieIdentityExtension { login_timestamp }); + } + identity + }, + )) + } + + fn to_response<B>( + &self, + id: Option<String>, + changed: bool, + res: &mut ServiceResponse<B>, + ) -> Self::ResponseFuture { + let _ = if changed { + let login_timestamp = SystemTime::now(); + self.0.set_cookie( + res, + id.map(|identity| CookieValue { + identity, + login_timestamp: self.0.login_deadline.map(|_| login_timestamp), + visit_timestamp: self.0.visit_deadline.map(|_| login_timestamp), + }), + ) + } else if self.0.always_update_cookie() && id.is_some() { + let visit_timestamp = SystemTime::now(); + let login_timestamp = if self.0.requires_oob_data() { + let CookieIdentityExtension { + login_timestamp: lt, + } = res.request().extensions_mut().remove().unwrap(); + lt + } else { + None + }; + self.0.set_cookie( + res, + Some(CookieValue { + identity: id.unwrap(), + login_timestamp, + visit_timestamp: self.0.visit_deadline.map(|_| visit_timestamp), + }), + ) + } else { + Ok(()) + }; + ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::borrow::Borrow; + + use super::*; + use actix_service::into_service; + use actix_web::http::StatusCode; + use actix_web::test::{self, TestRequest}; + use actix_web::{error, web, App, Error, HttpResponse}; + + const COOKIE_KEY_MASTER: [u8; 32] = [0; 32]; + const COOKIE_NAME: &str = "actix_auth"; + const COOKIE_LOGIN: &str = "test"; + + #[actix_rt::test] + async fn test_identity() { + let srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .secure(true), + )) + .service(web::resource("/index").to(|id: Identity| { + if id.identity().is_some() { + HttpResponse::Created() + } else { + HttpResponse::Ok() + } + })) + .service(web::resource("/login").to(|id: Identity| { + id.remember(COOKIE_LOGIN.to_string()); + HttpResponse::Ok() + })) + .service(web::resource("/logout").to(|id: Identity| { + if id.identity().is_some() { + id.forget(); + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + })), + ) + .await; + let resp = + test::call_service(&srv, TestRequest::with_uri("/index").to_request()).await; + assert_eq!(resp.status(), StatusCode::OK); + + let resp = + test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await; + assert_eq!(resp.status(), StatusCode::OK); + let c = resp.response().cookies().next().unwrap().to_owned(); + + let resp = test::call_service( + &srv, + TestRequest::with_uri("/index") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::CREATED); + + let resp = test::call_service( + &srv, + TestRequest::with_uri("/logout") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)) + } + + #[actix_rt::test] + async fn test_identity_max_age_time() { + let duration = Duration::days(1); + let srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age_time(duration) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = + test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(duration, c.max_age().unwrap()); + } + + #[actix_rt::test] + async fn test_http_only_same_site() { + let srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .http_only(true) + .same_site(SameSite::None), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + + let resp = + test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await; + + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + + let c = resp.response().cookies().next().unwrap().to_owned(); + assert!(c.http_only().unwrap()); + assert_eq!(SameSite::None, c.same_site().unwrap()); + } + + #[actix_rt::test] + async fn test_identity_max_age() { + let seconds = 60; + let srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age(seconds) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = + test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); + } + + async fn create_identity_server< + F: Fn(CookieIdentityPolicy) -> CookieIdentityPolicy + Sync + Send + Clone + 'static, + >( + f: F, + ) -> impl actix_service::Service< + actix_http::Request, + Response = ServiceResponse<actix_web::body::Body>, + Error = Error, + > { + test::init_service( + App::new() + .wrap(IdentityService::new(f(CookieIdentityPolicy::new( + &COOKIE_KEY_MASTER, + ) + .secure(false) + .name(COOKIE_NAME)))) + .service(web::resource("/").to(|id: Identity| async move { + let identity = id.identity(); + if identity.is_none() { + id.remember(COOKIE_LOGIN.to_string()) + } + web::Json(identity) + })), + ) + .await + } + + fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> { + let mut jar = CookieJar::new(); + jar.private(&Key::derive_from(&COOKIE_KEY_MASTER)) + .add(Cookie::new(COOKIE_NAME, identity)); + jar.get(COOKIE_NAME).unwrap().clone() + } + + fn login_cookie( + identity: &'static str, + login_timestamp: Option<SystemTime>, + visit_timestamp: Option<SystemTime>, + ) -> Cookie<'static> { + let mut jar = CookieJar::new(); + let key: Vec<u8> = COOKIE_KEY_MASTER + .iter() + .chain([1, 0, 0, 0].iter()) + .copied() + .collect(); + jar.private(&Key::derive_from(&key)).add(Cookie::new( + COOKIE_NAME, + serde_json::to_string(&CookieValue { + identity: identity.to_string(), + login_timestamp, + visit_timestamp, + }) + .unwrap(), + )); + jar.get(COOKIE_NAME).unwrap().clone() + } + + async fn assert_logged_in(response: ServiceResponse, identity: Option<&str>) { + let bytes = test::read_body(response).await; + let resp: Option<String> = serde_json::from_slice(&bytes[..]).unwrap(); + assert_eq!(resp.as_ref().map(|s| s.borrow()), identity); + } + + fn assert_legacy_login_cookie(response: &mut ServiceResponse, identity: &str) { + let mut cookies = CookieJar::new(); + for cookie in response.headers().get_all(header::SET_COOKIE) { + cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); + } + let cookie = cookies + .private(&Key::derive_from(&COOKIE_KEY_MASTER)) + .get(COOKIE_NAME) + .unwrap(); + assert_eq!(cookie.value(), identity); + } + + #[allow(clippy::enum_variant_names)] + enum LoginTimestampCheck { + NoTimestamp, + NewTimestamp, + OldTimestamp(SystemTime), + } + + #[allow(clippy::enum_variant_names)] + enum VisitTimeStampCheck { + NoTimestamp, + NewTimestamp, + } + + fn assert_login_cookie( + response: &mut ServiceResponse, + identity: &str, + login_timestamp: LoginTimestampCheck, + visit_timestamp: VisitTimeStampCheck, + ) { + let mut cookies = CookieJar::new(); + for cookie in response.headers().get_all(header::SET_COOKIE) { + cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); + } + let key: Vec<u8> = COOKIE_KEY_MASTER + .iter() + .chain([1, 0, 0, 0].iter()) + .copied() + .collect(); + let cookie = cookies + .private(&Key::derive_from(&key)) + .get(COOKIE_NAME) + .unwrap(); + let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap(); + assert_eq!(cv.identity, identity); + let now = SystemTime::now(); + let t30sec_ago = now - Duration::seconds(30); + match login_timestamp { + LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None), + LoginTimestampCheck::NewTimestamp => assert!( + t30sec_ago <= cv.login_timestamp.unwrap() + && cv.login_timestamp.unwrap() <= now + ), + LoginTimestampCheck::OldTimestamp(old_timestamp) => { + assert_eq!(cv.login_timestamp, Some(old_timestamp)) + } + } + match visit_timestamp { + VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None), + VisitTimeStampCheck::NewTimestamp => assert!( + t30sec_ago <= cv.visit_timestamp.unwrap() + && cv.visit_timestamp.unwrap() <= now + ), + } + } + + fn assert_no_login_cookie(response: &mut ServiceResponse) { + let mut cookies = CookieJar::new(); + for cookie in response.headers().get_all(header::SET_COOKIE) { + cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); + } + assert!(cookies.get(COOKIE_NAME).is_none()); + } + + #[actix_rt::test] + async fn test_identity_legacy_cookie_is_set() { + let srv = create_identity_server(|c| c).await; + let mut resp = + test::call_service(&srv, TestRequest::with_uri("/").to_request()).await; + assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_legacy_cookie_works() { + let srv = create_identity_server(|c| c).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + } + + #[actix_rt::test] + async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { + let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { + let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_login_timestamp_needed() { + let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_visit_timestamp_needed() { + let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_login_timestamp_too_old() { + let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + Some(SystemTime::now() - Duration::days(180)), + None, + ); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { + let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + None, + Some(SystemTime::now() - Duration::days(180)), + ); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + } + + #[actix_rt::test] + async fn test_identity_cookie_not_updated_on_login_deadline() { + let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + } + + // https://github.com/actix/actix-web/issues/1263 + #[actix_rt::test] + async fn test_identity_cookie_updated_on_visit_deadline() { + let srv = create_identity_server(|c| { + c.visit_deadline(Duration::days(90)) + .login_deadline(Duration::days(90)) + }) + .await; + let timestamp = SystemTime::now() - Duration::days(1); + let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); + let mut resp = test::call_service( + &srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::OldTimestamp(timestamp), + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + } + + #[actix_rt::test] + async fn test_borrowed_mut_error() { + use futures_util::future::{lazy, ok, Ready}; + + struct Ident; + impl IdentityPolicy for Ident { + type Future = Ready<Result<Option<String>, Error>>; + type ResponseFuture = Ready<Result<(), Error>>; + + fn from_request(&self, _: &mut ServiceRequest) -> Self::Future { + ok(Some("test".to_string())) + } + + fn to_response<B>( + &self, + _: Option<String>, + _: bool, + _: &mut ServiceResponse<B>, + ) -> Self::ResponseFuture { + ok(()) + } + } + + let srv = IdentityServiceMiddleware { + backend: Rc::new(Ident), + service: Rc::new(into_service(|_: ServiceRequest| async move { + actix_rt::time::sleep(std::time::Duration::from_secs(100)).await; + Err::<ServiceResponse, _>(error::ErrorBadRequest("error")) + })), + }; + + let srv2 = srv.clone(); + let req = TestRequest::default().to_srv_request(); + actix_rt::spawn(async move { + let _ = srv2.call(req).await; + }); + actix_rt::time::sleep(std::time::Duration::from_millis(50)).await; + + let _ = lazy(|cx| srv.poll_ready(cx)).await; + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +
+#![deny(rust_2018_idioms)] + +use derive_more::Display; +use std::fmt; +use std::future::Future; +use std::ops::{Deref, DerefMut}; +use std::pin::Pin; +use std::task; +use std::task::Poll; + +use prost::DecodeError as ProtoBufDecodeError; +use prost::EncodeError as ProtoBufEncodeError; +use prost::Message; + +use actix_web::dev::{HttpResponseBuilder, Payload}; +use actix_web::error::{Error, PayloadError, ResponseError}; +use actix_web::http::header::{CONTENT_LENGTH, CONTENT_TYPE}; +use actix_web::web::BytesMut; +use actix_web::{FromRequest, HttpMessage, HttpRequest, HttpResponse, Responder}; +use futures_util::future::{FutureExt, LocalBoxFuture}; +use futures_util::StreamExt; + +#[derive(Debug, Display)] +pub enum ProtoBufPayloadError { + /// Payload size is bigger than 256k + #[display(fmt = "Payload size is bigger than 256k")] + Overflow, + /// Content type error + #[display(fmt = "Content type error")] + ContentType, + /// Serialize error + #[display(fmt = "ProtoBuf serialize error: {}", _0)] + Serialize(ProtoBufEncodeError), + /// Deserialize error + #[display(fmt = "ProtoBuf deserialize error: {}", _0)] + Deserialize(ProtoBufDecodeError), + /// Payload error + #[display(fmt = "Error that occur during reading payload: {}", _0)] + Payload(PayloadError), +} + +impl ResponseError for ProtoBufPayloadError { + fn error_response(&self) -> HttpResponse { + match *self { + ProtoBufPayloadError::Overflow => HttpResponse::PayloadTooLarge().into(), + _ => HttpResponse::BadRequest().into(), + } + } +} + +impl From<PayloadError> for ProtoBufPayloadError { + fn from(err: PayloadError) -> ProtoBufPayloadError { + ProtoBufPayloadError::Payload(err) + } +} + +impl From<ProtoBufDecodeError> for ProtoBufPayloadError { + fn from(err: ProtoBufDecodeError) -> ProtoBufPayloadError { + ProtoBufPayloadError::Deserialize(err) + } +} + +pub struct ProtoBuf<T: Message>(pub T); + +impl<T: Message> Deref for ProtoBuf<T> { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +impl<T: Message> DerefMut for ProtoBuf<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl<T: Message> fmt::Debug for ProtoBuf<T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ProtoBuf: {:?}", self.0) + } +} + +impl<T: Message> fmt::Display for ProtoBuf<T> +where + T: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +pub struct ProtoBufConfig { + limit: usize, +} + +impl ProtoBufConfig { + /// Change max size of payload. By default max size is 256Kb + pub fn limit(&mut self, limit: usize) -> &mut Self { + self.limit = limit; + self + } +} + +impl Default for ProtoBufConfig { + fn default() -> Self { + ProtoBufConfig { limit: 262_144 } + } +} + +impl<T> FromRequest for ProtoBuf<T> +where + T: Message + Default + 'static, +{ + type Config = ProtoBufConfig; + type Error = Error; + type Future = LocalBoxFuture<'static, Result<Self, Error>>; + + #[inline] + fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { + let limit = req + .app_data::<ProtoBufConfig>() + .map(|c| c.limit) + .unwrap_or(262_144); + ProtoBufMessage::new(req, payload) + .limit(limit) + .map(move |res| match res { + Err(e) => Err(e.into()), + Ok(item) => Ok(ProtoBuf(item)), + }) + .boxed_local() + } +} + +impl<T: Message + Default> Responder for ProtoBuf<T> { + fn respond_to(self, _: &HttpRequest) -> HttpResponse { + let mut buf = Vec::new(); + match self.0.encode(&mut buf) { + Ok(()) => HttpResponse::Ok() + .content_type("application/protobuf") + .body(buf), + Err(err) => HttpResponse::from_error(Error::from( + ProtoBufPayloadError::Serialize(err), + )), + } + } +} + +pub struct ProtoBufMessage<T: Message + Default> { + limit: usize, + length: Option<usize>, + stream: Option<Payload>, + err: Option<ProtoBufPayloadError>, + fut: Option<LocalBoxFuture<'static, Result<T, ProtoBufPayloadError>>>, +} + +impl<T: Message + Default> ProtoBufMessage<T> { + /// Create `ProtoBufMessage` for request. + pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self { + if req.content_type() != "application/protobuf" { + return ProtoBufMessage { + limit: 262_144, + length: None, + stream: None, + fut: None, + err: Some(ProtoBufPayloadError::ContentType), + }; + } + + let mut len = None; + if let Some(l) = req.headers().get(CONTENT_LENGTH) { + if let Ok(s) = l.to_str() { + if let Ok(l) = s.parse::<usize>() { + len = Some(l) + } + } + } + + ProtoBufMessage { + limit: 262_144, + length: len, + stream: Some(payload.take()), + fut: None, + err: None, + } + } + + /// Change max size of payload. By default max size is 256Kb + pub fn limit(mut self, limit: usize) -> Self { + self.limit = limit; + self + } +} + +impl<T: Message + Default + 'static> Future for ProtoBufMessage<T> { + type Output = Result<T, ProtoBufPayloadError>; + + fn poll( + mut self: Pin<&mut Self>, + task: &mut task::Context<'_>, + ) -> Poll<Self::Output> { + if let Some(ref mut fut) = self.fut { + return Pin::new(fut).poll(task); + } + + if let Some(err) = self.err.take() { + return Poll::Ready(Err(err)); + } + + let limit = self.limit; + if let Some(len) = self.length.take() { + if len > limit { + return Poll::Ready(Err(ProtoBufPayloadError::Overflow)); + } + } + + let mut stream = self + .stream + .take() + .expect("ProtoBufMessage could not be used second time"); + + self.fut = Some( + async move { + let mut body = BytesMut::with_capacity(8192); + + while let Some(item) = stream.next().await { + let chunk = item?; + if (body.len() + chunk.len()) > limit { + return Err(ProtoBufPayloadError::Overflow); + } else { + body.extend_from_slice(&chunk); + } + } + + Ok(<T>::decode(&mut body)?) + } + .boxed_local(), + ); + self.poll(task) + } +} + +pub trait ProtoBufResponseBuilder { + fn protobuf<T: Message>(&mut self, value: T) -> Result<HttpResponse, Error>; +} + +impl ProtoBufResponseBuilder for HttpResponseBuilder { + fn protobuf<T: Message>(&mut self, value: T) -> Result<HttpResponse, Error> { + self.insert_header((CONTENT_TYPE, "application/protobuf")); + + let mut body = Vec::new(); + value + .encode(&mut body) + .map_err(ProtoBufPayloadError::Serialize)?; + Ok(self.body(body)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use actix_web::http::header; + use actix_web::test::TestRequest; + + impl PartialEq for ProtoBufPayloadError { + fn eq(&self, other: &ProtoBufPayloadError) -> bool { + match *self { + ProtoBufPayloadError::Overflow => { + matches!(*other, ProtoBufPayloadError::Overflow) + } + ProtoBufPayloadError::ContentType => { + matches!(*other, ProtoBufPayloadError::ContentType) + } + _ => false, + } + } + } + + #[derive(Clone, PartialEq, Message)] + pub struct MyObject { + #[prost(int32, tag = "1")] + pub number: i32, + #[prost(string, tag = "2")] + pub name: String, + } + + #[actix_rt::test] + async fn test_protobuf() { + let protobuf = ProtoBuf(MyObject { + number: 9, + name: "test".to_owned(), + }); + let req = TestRequest::default().to_http_request(); + let resp = protobuf.respond_to(&req).await.unwrap(); + let ct = resp.headers().get(header::CONTENT_TYPE).unwrap(); + assert_eq!(ct, "application/protobuf"); + } + + #[actix_rt::test] + async fn test_protobuf_message() { + let (req, mut pl) = TestRequest::default().to_http_parts(); + let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl).await; + assert_eq!(protobuf.err().unwrap(), ProtoBufPayloadError::ContentType); + + let (req, mut pl) = TestRequest::get() + .insert_header((header::CONTENT_TYPE, "application/text")) + .to_http_parts(); + let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl).await; + assert_eq!(protobuf.err().unwrap(), ProtoBufPayloadError::ContentType); + + let (req, mut pl) = TestRequest::get() + .insert_header((header::CONTENT_TYPE, "application/protobuf")) + .insert_header((header::CONTENT_LENGTH, "10000")) + .to_http_parts(); + let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl) + .limit(100) + .await; + assert_eq!(protobuf.err().unwrap(), ProtoBufPayloadError::Overflow); + } +} +
1 +\ No newline at end of file + 1 2 3 4 @@ -73,4 +73,5 @@ pub use redis_async::error::Error as RespError; pub use redis_async::resp::RespValue;-
1 +\ No newline at end of file + 1 2 3 4 @@ -285,4 +285,5 @@ } }-
1 +\ No newline at end of file + 1 2 3 4 @@ -1415,4 +1415,5 @@ } }-
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +
+//! Cookie based sessions. See docs for [`CookieSession`]. + +use std::{collections::HashMap, rc::Rc}; + +use actix_service::{Service, Transform}; +use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; +use actix_web::dev::{ServiceRequest, ServiceResponse}; +use actix_web::http::{header::SET_COOKIE, HeaderValue}; +use actix_web::{Error, HttpMessage, ResponseError}; +use derive_more::Display; +use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use serde_json::error::Error as JsonError; +use time::{Duration, OffsetDateTime}; + +use crate::{Session, SessionStatus}; + +/// Errors that can occur during handling cookie session +#[derive(Debug, Display)] +pub enum CookieSessionError { + /// Size of the serialized session is greater than 4000 bytes. + #[display(fmt = "Size of the serialized session is greater than 4000 bytes.")] + Overflow, + + /// Fail to serialize session. + #[display(fmt = "Fail to serialize session")] + Serialize(JsonError), +} + +impl ResponseError for CookieSessionError {} + +enum CookieSecurity { + Signed, + Private, +} + +struct CookieSessionInner { + key: Key, + security: CookieSecurity, + name: String, + path: String, + domain: Option<String>, + lazy: bool, + secure: bool, + http_only: bool, + max_age: Option<Duration>, + expires_in: Option<Duration>, + same_site: Option<SameSite>, +} + +impl CookieSessionInner { + fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner { + CookieSessionInner { + security, + key: Key::derive_from(key), + name: "actix-session".to_owned(), + path: "/".to_owned(), + domain: None, + lazy: false, + secure: true, + http_only: true, + max_age: None, + expires_in: None, + same_site: None, + } + } + + fn set_cookie<B>( + &self, + res: &mut ServiceResponse<B>, + state: impl Iterator<Item = (String, String)>, + ) -> Result<(), Error> { + let state: HashMap<String, String> = state.collect(); + + if self.lazy && state.is_empty() { + return Ok(()); + } + + let value = + serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?; + if value.len() > 4064 { + return Err(CookieSessionError::Overflow.into()); + } + + let mut cookie = Cookie::new(self.name.clone(), value); + cookie.set_path(self.path.clone()); + cookie.set_secure(self.secure); + cookie.set_http_only(self.http_only); + + if let Some(ref domain) = self.domain { + cookie.set_domain(domain.clone()); + } + + if let Some(expires_in) = self.expires_in { + cookie.set_expires(OffsetDateTime::now_utc() + expires_in); + } + + 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(); + + match self.security { + CookieSecurity::Signed => jar.signed(&self.key).add(cookie), + CookieSecurity::Private => jar.private(&self.key).add(cookie), + } + + for cookie in jar.delta() { + let val = HeaderValue::from_str(&cookie.encoded().to_string())?; + res.headers_mut().append(SET_COOKIE, val); + } + + Ok(()) + } + + /// invalidates session cookie + fn remove_cookie<B>(&self, res: &mut ServiceResponse<B>) -> Result<(), Error> { + let mut cookie = Cookie::named(self.name.clone()); + cookie.set_path(self.path.clone()); + cookie.set_value(""); + cookie.set_max_age(Duration::zero()); + cookie.set_expires(OffsetDateTime::now_utc() - Duration::days(365)); + + let val = HeaderValue::from_str(&cookie.to_string())?; + res.headers_mut().append(SET_COOKIE, val); + + Ok(()) + } + + fn load(&self, req: &ServiceRequest) -> (bool, HashMap<String, 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 = match self.security { + CookieSecurity::Signed => jar.signed(&self.key).get(&self.name), + CookieSecurity::Private => { + jar.private(&self.key).get(&self.name) + } + }; + if let Some(cookie) = cookie_opt { + if let Ok(val) = serde_json::from_str(cookie.value()) { + return (false, val); + } + } + } + } + } + (true, HashMap::new()) + } +} + +/// Use cookies for session storage. +/// +/// `CookieSession` creates sessions which are limited to storing +/// fewer than 4000 bytes of data (as the payload must fit into a single +/// cookie). An Internal Server Error is generated if the session contains more +/// than 4000 bytes. +/// +/// A cookie may have a security policy of *signed* or *private*. Each has a +/// respective `CookieSession` constructor. +/// +/// A *signed* cookie is stored on the client as plaintext alongside +/// a signature such that the cookie may be viewed but not modified by the +/// client. +/// +/// A *private* cookie is stored on the client as encrypted text +/// such that it may neither be viewed nor modified by the client. +/// +/// The constructors take a key as an argument. +/// This is the private key for cookie session - when this value is changed, +/// all session data is lost. The constructors will panic if the key is less +/// than 32 bytes in length. +/// +/// The backend relies on `cookie` crate to create and read cookies. +/// By default all cookies are percent encoded, but certain symbols may +/// cause troubles when reading cookie, if they are not properly percent encoded. +/// +/// # Examples +/// ``` +/// use actix_session::CookieSession; +/// use actix_web::{web, App, HttpResponse, HttpServer}; +/// +/// let app = App::new().wrap( +/// CookieSession::signed(&[0; 32]) +/// .domain("www.rust-lang.org") +/// .name("actix_session") +/// .path("/") +/// .secure(true)) +/// .service(web::resource("/").to(|| HttpResponse::Ok())); +/// ``` +pub struct CookieSession(Rc<CookieSessionInner>); + +impl CookieSession { + /// Construct new *signed* `CookieSession` instance. + /// + /// Panics if key length is less than 32 bytes. + pub fn signed(key: &[u8]) -> CookieSession { + CookieSession(Rc::new(CookieSessionInner::new( + key, + CookieSecurity::Signed, + ))) + } + + /// Construct new *private* `CookieSession` instance. + /// + /// Panics if key length is less than 32 bytes. + pub fn private(key: &[u8]) -> CookieSession { + CookieSession(Rc::new(CookieSessionInner::new( + key, + CookieSecurity::Private, + ))) + } + + /// Sets the `path` field in the session cookie being built. + pub fn path<S: Into<String>>(mut self, value: S) -> CookieSession { + 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) -> CookieSession { + 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) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into()); + self + } + + /// When true, prevents adding session cookies to responses until + /// the session contains data. Default is `false`. + /// + /// Useful when trying to comply with laws that require consent for setting cookies. + pub fn lazy(mut self, value: bool) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().lazy = value; + 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) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().secure = value; + self + } + + /// Sets the `http_only` field in the session cookie being built. + pub fn http_only(mut self, value: bool) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().http_only = value; + self + } + + /// Sets the `same_site` field in the session cookie being built. + pub fn same_site(mut self, value: SameSite) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().same_site = Some(value); + self + } + + /// Sets the `max-age` field in the session cookie being built. + pub fn max_age(self, seconds: i64) -> CookieSession { + self.max_age_time(Duration::seconds(seconds)) + } + + /// Sets the `max-age` field in the session cookie being built. + pub fn max_age_time(mut self, value: time::Duration) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); + self + } + + /// Sets the `expires` field in the session cookie being built. + pub fn expires_in(self, seconds: i64) -> CookieSession { + self.expires_in_time(Duration::seconds(seconds)) + } + + /// Sets the `expires` field in the session cookie being built. + pub fn expires_in_time(mut self, value: Duration) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().expires_in = Some(value); + self + } +} + +impl<S, B: 'static> Transform<S, ServiceRequest> for CookieSession +where + S: Service<ServiceRequest, Response = ServiceResponse<B>>, + S::Future: 'static, + S::Error: 'static, +{ + type Response = ServiceResponse<B>; + type Error = S::Error; + type InitError = (); + type Transform = CookieSessionMiddleware<S>; + type Future = Ready<Result<Self::Transform, Self::InitError>>; + + fn new_transform(&self, service: S) -> Self::Future { + ok(CookieSessionMiddleware { + service, + inner: self.0.clone(), + }) + } +} + +/// Cookie session middleware +pub struct CookieSessionMiddleware<S> { + service: S, + inner: Rc<CookieSessionInner>, +} + +impl<S, B: 'static> Service<ServiceRequest> for CookieSessionMiddleware<S> +where + S: Service<ServiceRequest, Response = ServiceResponse<B>>, + S::Future: 'static, + S::Error: 'static, +{ + type Response = ServiceResponse<B>; + type Error = S::Error; + type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>; + + actix_service::forward_ready!(service); + + /// On first request, a new session cookie is returned in response, regardless + /// of whether any session state is set. With subsequent requests, if the + /// session state changes, then set-cookie is returned in response. As + /// a user logs out, call session.purge() to set SessionStatus accordingly + /// and this will trigger removal of the session cookie in the response. + fn call(&self, mut req: ServiceRequest) -> Self::Future { + let inner = self.inner.clone(); + let (is_new, state) = self.inner.load(&req); + let prolong_expiration = self.inner.expires_in.is_some(); + Session::set_session(state, &mut req); + + let fut = self.service.call(req); + + async move { + fut.await.map(|mut res| { + match Session::get_changes(&mut res) { + (SessionStatus::Changed, Some(state)) + | (SessionStatus::Renewed, Some(state)) => { + res.checked_expr(|res| inner.set_cookie(res, state)) + } + (SessionStatus::Unchanged, Some(state)) if prolong_expiration => { + res.checked_expr(|res| inner.set_cookie(res, state)) + } + (SessionStatus::Unchanged, _) => + // set a new session cookie upon first request (new client) + { + if is_new { + let state: HashMap<String, String> = HashMap::new(); + res.checked_expr(|res| { + inner.set_cookie(res, state.into_iter()) + }) + } else { + res + } + } + (SessionStatus::Purged, _) => { + let _ = inner.remove_cookie(&mut res); + res + } + _ => res, + } + }) + } + .boxed_local() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use actix_web::web::Bytes; + use actix_web::{test, web, App}; + + #[actix_rt::test] + async fn cookie_session() { + let app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .any(|c| c.name() == "actix-session")); + } + + #[actix_rt::test] + async fn private_cookie() { + let app = test::init_service( + App::new() + .wrap(CookieSession::private(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .any(|c| c.name() == "actix-session")); + } + + #[actix_rt::test] + async fn lazy_cookie() { + let app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false).lazy(true)) + .service(web::resource("/count").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "counting" + })) + .service(web::resource("/").to(|_ses: Session| async move { "test" })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response.response().cookies().count() == 0); + + let request = test::TestRequest::with_uri("/count").to_request(); + let response = app.call(request).await.unwrap(); + + assert!(response + .response() + .cookies() + .any(|c| c.name() == "actix-session")); + } + + #[actix_rt::test] + async fn cookie_session_extractor() { + let app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .any(|c| c.name() == "actix-session")); + } + + #[actix_rt::test] + async fn basics() { + let app = test::init_service( + App::new() + .wrap( + CookieSession::signed(&[0; 32]) + .path("/test/") + .name("actix-test") + .domain("localhost") + .http_only(true) + .same_site(SameSite::Lax) + .max_age(100), + ) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "test" + })) + .service(web::resource("/test/").to(|ses: Session| async move { + let val: usize = ses.get("counter").unwrap().unwrap(); + format!("counter: {}", val) + })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie = response + .response() + .cookies() + .find(|c| c.name() == "actix-test") + .unwrap() + .clone(); + assert_eq!(cookie.path().unwrap(), "/test/"); + + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie) + .to_request(); + let body = test::read_response(&app, request).await; + assert_eq!(body, Bytes::from_static(b"counter: 100")); + } + + #[actix_rt::test] + async fn prolong_expiration() { + let app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false).expires_in(60)) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.set("counter", 100); + "test" + })) + .service( + web::resource("/test/") + .to(|| async move { "no-changes-in-session" }), + ), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let expires_1 = response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .expect("Cookie is set") + .expires() + .expect("Expiration is set"); + + actix_rt::time::sleep(std::time::Duration::from_secs(1)).await; + + let request = test::TestRequest::with_uri("/test/").to_request(); + let response = app.call(request).await.unwrap(); + let expires_2 = response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .expect("Cookie is set") + .expires() + .expect("Expiration is set"); + + assert!(expires_2 - expires_1 >= Duration::seconds(1)); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +
+//! Sessions for Actix Web. +//! +//! Provides a general solution for session management. Session middleware could provide different +//! implementations which could be accessed via general session API. +//! +//! This crate provides a general solution for session management and includes a cookie backend. +//! Other backend implementations can be built to use persistent or key-value stores, for example. +//! +//! In general, some session middleware, such as a [`CookieSession`] is initialized and applied. +//! To access session data, the [`Session`] extractor must be used. This extractor allows reading +//! modifying session data. +//! +//! ```no_run +//! use actix_web::{web, App, HttpServer, HttpResponse, Error}; +//! use actix_session::{Session, CookieSession}; +//! +//! fn index(session: Session) -> Result<&'static str, Error> { +//! // access session data +//! if let Some(count) = session.get::<i32>("counter")? { +//! println!("SESSION value: {}", count); +//! session.set("counter", count + 1)?; +//! } else { +//! session.set("counter", 1)?; +//! } +//! +//! Ok("Welcome!") +//! } +//! +//! #[actix_rt::main] +//! async fn main() -> std::io::Result<()> { +//! HttpServer::new( +//! || App::new() +//! // create cookie based session middleware +//! .wrap(CookieSession::signed(&[0; 32]).secure(false)) +//! .default_service(web::to(|| HttpResponse::Ok()))) +//! .bind(("127.0.0.1", 8080))? +//! .run() +//! .await +//! } +//! ``` + +#![deny(rust_2018_idioms)] + +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +use actix_web::dev::{ + Extensions, Payload, RequestHead, ServiceRequest, ServiceResponse, +}; +use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; +use futures_util::future::{ok, Ready}; +use serde::{de::DeserializeOwned, Serialize}; + +#[cfg(feature = "cookie-session")] +mod cookie; +#[cfg(feature = "cookie-session")] +pub use crate::cookie::CookieSession; + +/// The high-level interface you use to modify session data. +/// +/// Session object is obtained with [`UserSession::get_session`]. The [`UserSession`] trait is +/// implemented for `HttpRequest`, `ServiceRequest`, and `RequestHead`. +/// +/// ``` +/// use actix_session::Session; +/// use actix_web::Result; +/// +/// async fn index(session: Session) -> Result<&'static str> { +/// // access session data +/// if let Some(count) = session.get::<i32>("counter")? { +/// session.set("counter", count + 1)?; +/// } else { +/// session.set("counter", 1)?; +/// } +/// +/// Ok("Welcome!") +/// } +/// ``` +pub struct Session(Rc<RefCell<SessionInner>>); + +/// Extraction of a [`Session`] object. +pub trait UserSession { + fn get_session(&self) -> Session; +} + +impl UserSession for HttpRequest { + fn get_session(&self) -> Session { + Session::get_session(&mut *self.extensions_mut()) + } +} + +impl UserSession for ServiceRequest { + fn get_session(&self) -> Session { + Session::get_session(&mut *self.extensions_mut()) + } +} + +impl UserSession for RequestHead { + fn get_session(&self) -> Session { + Session::get_session(&mut *self.extensions_mut()) + } +} + +#[derive(PartialEq, Clone, Debug)] +pub enum SessionStatus { + Changed, + Purged, + Renewed, + Unchanged, +} +impl Default for SessionStatus { + fn default() -> SessionStatus { + SessionStatus::Unchanged + } +} + +#[derive(Default)] +struct SessionInner { + state: HashMap<String, String>, + pub status: SessionStatus, +} + +impl Session { + /// Get a `value` from the session. + pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, Error> { + if let Some(s) = self.0.borrow().state.get(key) { + Ok(Some(serde_json::from_str(s)?)) + } else { + Ok(None) + } + } + + /// Set a `value` from the session. + pub fn set<T: Serialize>(&self, key: &str, value: T) -> Result<(), Error> { + let mut inner = self.0.borrow_mut(); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner + .state + .insert(key.to_owned(), serde_json::to_string(&value)?); + } + Ok(()) + } + + /// Remove value from the session. + pub fn remove(&self, key: &str) { + let mut inner = self.0.borrow_mut(); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner.state.remove(key); + } + } + + /// Clear the session. + pub fn clear(&self) { + let mut inner = self.0.borrow_mut(); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner.state.clear() + } + } + + /// Removes session, both client and server side. + pub fn purge(&self) { + let mut inner = self.0.borrow_mut(); + inner.status = SessionStatus::Purged; + inner.state.clear(); + } + + /// Renews the session key, assigning existing session state to new key. + pub fn renew(&self) { + let mut inner = self.0.borrow_mut(); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Renewed; + } + } + + /// Adds the given key-value pairs to the session on the request. + /// + /// Values that match keys already existing on the session will be overwritten. Values should + /// already be JSON serialized. + /// + /// # Examples + /// ``` + /// # use actix_session::Session; + /// # use actix_web::test; + /// let mut req = test::TestRequest::default().to_srv_request(); + /// + /// Session::set_session( + /// vec![("counter".to_string(), serde_json::to_string(&0).unwrap())], + /// &mut req, + /// ); + /// ``` + pub fn set_session( + data: impl IntoIterator<Item = (String, String)>, + req: &mut ServiceRequest, + ) { + let session = Session::get_session(&mut *req.extensions_mut()); + let mut inner = session.0.borrow_mut(); + inner.state.extend(data); + } + + pub fn get_changes<B>( + res: &mut ServiceResponse<B>, + ) -> ( + SessionStatus, + Option<impl Iterator<Item = (String, String)>>, + ) { + if let Some(s_impl) = res + .request() + .extensions() + .get::<Rc<RefCell<SessionInner>>>() + { + let state = + std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new()); + (s_impl.borrow().status.clone(), Some(state.into_iter())) + } else { + (SessionStatus::Unchanged, None) + } + } + + fn get_session(extensions: &mut Extensions) -> Session { + if let Some(s_impl) = extensions.get::<Rc<RefCell<SessionInner>>>() { + return Session(Rc::clone(&s_impl)); + } + let inner = Rc::new(RefCell::new(SessionInner::default())); + extensions.insert(inner.clone()); + Session(inner) + } +} + +/// Extractor implementation for Session type. +/// +/// ```rust +/// # use actix_web::*; +/// use actix_session::Session; +/// +/// fn index(session: Session) -> Result<&'static str> { +/// // access session data +/// if let Some(count) = session.get::<i32>("counter")? { +/// session.set("counter", count + 1)?; +/// } else { +/// session.set("counter", 1)?; +/// } +/// +/// Ok("Welcome!") +/// } +/// # fn main() {} +/// ``` +impl FromRequest for Session { + type Error = Error; + type Future = Ready<Result<Session, Error>>; + type Config = (); + + #[inline] + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + ok(Session::get_session(&mut *req.extensions_mut())) + } +} + +#[cfg(test)] +mod tests { + use actix_web::{test, HttpResponse}; + + use super::*; + + #[test] + fn session() { + let mut req = test::TestRequest::default().to_srv_request(); + + Session::set_session( + vec![("key".to_string(), serde_json::to_string("value").unwrap())], + &mut req, + ); + let session = Session::get_session(&mut *req.extensions_mut()); + let res = session.get::<String>("key").unwrap(); + assert_eq!(res, Some("value".to_string())); + + session.set("key2", "value2".to_string()).unwrap(); + session.remove("key"); + + let mut res = req.into_response(HttpResponse::Ok().finish()); + let (_status, state) = Session::get_changes(&mut res); + let changes: Vec<_> = state.unwrap().collect(); + assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]); + } + + #[test] + fn get_session() { + let mut req = test::TestRequest::default().to_srv_request(); + + Session::set_session( + vec![("key".to_string(), serde_json::to_string(&true).unwrap())], + &mut req, + ); + + let session = req.get_session(); + let res = session.get("key").unwrap(); + assert_eq!(res, Some(true)); + } + + #[test] + fn get_session_from_request_head() { + let mut req = test::TestRequest::default().to_srv_request(); + + Session::set_session( + vec![("key".to_string(), serde_json::to_string(&10).unwrap())], + &mut req, + ); + + let session = req.head_mut().get_session(); + let res = session.get::<u32>("key").unwrap(); + assert_eq!(res, Some(10)); + } + + #[test] + fn purge_session() { + let req = test::TestRequest::default().to_srv_request(); + let session = Session::get_session(&mut *req.extensions_mut()); + assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); + session.purge(); + assert_eq!(session.0.borrow().status, SessionStatus::Purged); + } + + #[test] + fn renew_session() { + let req = test::TestRequest::default().to_srv_request(); + let session = Session::get_session(&mut *req.extensions_mut()); + assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); + session.renew(); + assert_eq!(session.0.borrow().status, SessionStatus::Renewed); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +
+//! Extractor for the "Basic" HTTP Authentication Scheme + +use std::borrow::Cow; + +use actix_web::dev::{Payload, ServiceRequest}; +use actix_web::http::header::Header; +use actix_web::{FromRequest, HttpRequest}; +use futures_util::future::{ready, Ready}; + +use super::config::AuthExtractorConfig; +use super::errors::AuthenticationError; +use super::AuthExtractor; +use crate::headers::authorization::{Authorization, Basic}; +use crate::headers::www_authenticate::basic::Basic as Challenge; + +/// [`BasicAuth`] extractor configuration, +/// used for [`WWW-Authenticate`] header later. +/// +/// [`BasicAuth`]: ./struct.BasicAuth.html +/// [`WWW-Authenticate`]: +/// ../../headers/www_authenticate/struct.WwwAuthenticate.html +#[derive(Debug, Clone, Default)] +pub struct Config(Challenge); + +impl Config { + /// Set challenge `realm` attribute. + /// + /// The "realm" attribute indicates the scope of protection in the manner + /// described in HTTP/1.1 [RFC2617](https://tools.ietf.org/html/rfc2617#section-1.2). + pub fn realm<T>(mut self, value: T) -> Config + where + T: Into<Cow<'static, str>>, + { + self.0.realm = Some(value.into()); + self + } +} + +impl AsRef<Challenge> for Config { + fn as_ref(&self) -> &Challenge { + &self.0 + } +} + +impl AuthExtractorConfig for Config { + type Inner = Challenge; + + fn into_inner(self) -> Self::Inner { + self.0 + } +} + +// Needs `fn main` to display complete example. +#[allow(clippy::needless_doctest_main)] +/// Extractor for HTTP Basic auth. +/// +/// # Example +/// +/// ``` +/// use actix_web::Result; +/// use actix_web_httpauth::extractors::basic::BasicAuth; +/// +/// async fn index(auth: BasicAuth) -> String { +/// format!("Hello, {}!", auth.user_id()) +/// } +/// ``` +/// +/// If authentication fails, this extractor fetches the [`Config`] instance +/// from the [app data] in order to properly form the `WWW-Authenticate` +/// response header. +/// +/// ## Example +/// +/// ``` +/// use actix_web::{web, App}; +/// use actix_web_httpauth::extractors::basic::{BasicAuth, Config}; +/// +/// async fn index(auth: BasicAuth) -> String { +/// format!("Hello, {}!", auth.user_id()) +/// } +/// +/// fn main() { +/// let app = App::new() +/// .data(Config::default().realm("Restricted area")) +/// .service(web::resource("/index.html").route(web::get().to(index))); +/// } +/// ``` +/// +/// [`Config`]: ./struct.Config.html +/// [app data]: https://docs.rs/actix-web/1.0.0-beta.5/actix_web/struct.App.html#method.data +#[derive(Debug, Clone)] +pub struct BasicAuth(Basic); + +impl BasicAuth { + /// Returns client's user-ID. + pub fn user_id(&self) -> &Cow<'static, str> { + &self.0.user_id() + } + + /// Returns client's password. + pub fn password(&self) -> Option<&Cow<'static, str>> { + self.0.password() + } +} + +impl FromRequest for BasicAuth { + type Future = Ready<Result<Self, Self::Error>>; + type Config = Config; + type Error = AuthenticationError<Challenge>; + + fn from_request( + req: &HttpRequest, + _: &mut Payload, + ) -> <Self as FromRequest>::Future { + ready( + Authorization::<Basic>::parse(req) + .map(|auth| BasicAuth(auth.into_scheme())) + .map_err(|_| { + // TODO: debug! the original error + let challenge = req + .app_data::<Self::Config>() + .map(|config| config.0.clone()) + // TODO: Add trace! about `Default::default` call + .unwrap_or_else(Default::default); + + AuthenticationError::new(challenge) + }), + ) + } +} + +impl AuthExtractor for BasicAuth { + type Error = AuthenticationError<Challenge>; + type Future = Ready<Result<Self, Self::Error>>; + + fn from_service_request(req: &ServiceRequest) -> Self::Future { + ready( + Authorization::<Basic>::parse(req) + .map(|auth| BasicAuth(auth.into_scheme())) + .map_err(|_| { + // TODO: debug! the original error + let challenge = req + .app_data::<Config>() + .map(|config| config.0.clone()) + // TODO: Add trace! about `Default::default` call + .unwrap_or_else(Default::default); + + AuthenticationError::new(challenge) + }), + ) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +
+//! Extractor for the "Bearer" HTTP Authentication Scheme + +use std::borrow::Cow; +use std::default::Default; + +use actix_web::dev::{Payload, ServiceRequest}; +use actix_web::http::header::Header; +use actix_web::{FromRequest, HttpRequest}; +use futures_util::future::{ready, Ready}; + +use super::config::AuthExtractorConfig; +use super::errors::AuthenticationError; +use super::AuthExtractor; +use crate::headers::authorization; +use crate::headers::www_authenticate::bearer; +pub use crate::headers::www_authenticate::bearer::Error; + +/// [BearerAuth](./struct/BearerAuth.html) extractor configuration. +#[derive(Debug, Clone, Default)] +pub struct Config(bearer::Bearer); + +impl Config { + /// Set challenge `scope` attribute. + /// + /// The `"scope"` attribute is a space-delimited list of case-sensitive + /// scope values indicating the required scope of the access token for + /// accessing the requested resource. + pub fn scope<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config { + self.0.scope = Some(value.into()); + self + } + + /// Set challenge `realm` attribute. + /// + /// The "realm" attribute indicates the scope of protection in the manner + /// described in HTTP/1.1 [RFC2617](https://tools.ietf.org/html/rfc2617#section-1.2). + pub fn realm<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config { + self.0.realm = Some(value.into()); + self + } +} + +impl AsRef<bearer::Bearer> for Config { + fn as_ref(&self) -> &bearer::Bearer { + &self.0 + } +} + +impl AuthExtractorConfig for Config { + type Inner = bearer::Bearer; + + fn into_inner(self) -> Self::Inner { + self.0 + } +} + +// Needs `fn main` to display complete example. +#[allow(clippy::needless_doctest_main)] +/// Extractor for HTTP Bearer auth +/// +/// # Example +/// +/// ``` +/// use actix_web_httpauth::extractors::bearer::BearerAuth; +/// +/// async fn index(auth: BearerAuth) -> String { +/// format!("Hello, user with token {}!", auth.token()) +/// } +/// ``` +/// +/// If authentication fails, this extractor fetches the [`Config`] instance +/// from the [app data] in order to properly form the `WWW-Authenticate` +/// response header. +/// +/// ## Example +/// +/// ``` +/// use actix_web::{web, App}; +/// use actix_web_httpauth::extractors::bearer::{BearerAuth, Config}; +/// +/// async fn index(auth: BearerAuth) -> String { +/// format!("Hello, {}!", auth.token()) +/// } +/// +/// fn main() { +/// let app = App::new() +/// .data( +/// Config::default() +/// .realm("Restricted area") +/// .scope("email photo"), +/// ) +/// .service(web::resource("/index.html").route(web::get().to(index))); +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct BearerAuth(authorization::Bearer); + +impl BearerAuth { + /// Returns bearer token provided by client. + pub fn token(&self) -> &str { + self.0.token() + } +} + +impl FromRequest for BearerAuth { + type Config = Config; + type Future = Ready<Result<Self, Self::Error>>; + type Error = AuthenticationError<bearer::Bearer>; + + fn from_request( + req: &HttpRequest, + _payload: &mut Payload, + ) -> <Self as FromRequest>::Future { + ready( + authorization::Authorization::<authorization::Bearer>::parse(req) + .map(|auth| BearerAuth(auth.into_scheme())) + .map_err(|_| { + let bearer = req + .app_data::<Self::Config>() + .map(|config| config.0.clone()) + .unwrap_or_else(Default::default); + + AuthenticationError::new(bearer) + }), + ) + } +} + +impl AuthExtractor for BearerAuth { + type Future = Ready<Result<Self, Self::Error>>; + type Error = AuthenticationError<bearer::Bearer>; + + fn from_service_request(req: &ServiceRequest) -> Self::Future { + ready( + authorization::Authorization::<authorization::Bearer>::parse(req) + .map(|auth| BearerAuth(auth.into_scheme())) + .map_err(|_| { + let bearer = req + .app_data::<Config>() + .map(|config| config.0.clone()) + .unwrap_or_else(Default::default); + + AuthenticationError::new(bearer) + }), + ) + } +} + +/// Extended error customization for HTTP `Bearer` auth. +impl AuthenticationError<bearer::Bearer> { + /// Attach `Error` to the current Authentication error. + /// + /// Error status code will be changed to the one provided by the `kind` + /// Error. + pub fn with_error(mut self, kind: Error) -> Self { + *self.status_code_mut() = kind.status_code(); + self.challenge_mut().error = Some(kind); + self + } + + /// Attach error description to the current Authentication error. + pub fn with_error_description<T>(mut self, desc: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.challenge_mut().error_description = Some(desc.into()); + self + } + + /// Attach error URI to the current Authentication error. + /// + /// It is up to implementor to provide properly formed absolute URI. + pub fn with_error_uri<T>(mut self, uri: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.challenge_mut().error_uri = Some(uri.into()); + self + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +
+use super::AuthenticationError; +use crate::headers::www_authenticate::Challenge; + +/// Trait implemented for types that provides configuration +/// for the authentication [extractors]. +/// +/// [extractors]: ./trait.AuthExtractor.html +pub trait AuthExtractorConfig { + /// Associated challenge type. + type Inner: Challenge; + + /// Convert the config instance into a HTTP challenge. + fn into_inner(self) -> Self::Inner; +} + +impl<T> From<T> for AuthenticationError<<T as AuthExtractorConfig>::Inner> +where + T: AuthExtractorConfig, +{ + fn from(config: T) -> Self { + AuthenticationError::new(config.into_inner()) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +
+use std::error::Error; +use std::fmt; + +use actix_web::http::StatusCode; +use actix_web::{HttpResponse, ResponseError}; + +use crate::headers::www_authenticate::Challenge; +use crate::headers::www_authenticate::WwwAuthenticate; + +/// Authentication error returned by authentication extractors. +/// +/// Different extractors may extend `AuthenticationError` implementation +/// in order to provide access to inner challenge fields. +#[derive(Debug)] +pub struct AuthenticationError<C: Challenge> { + challenge: C, + status_code: StatusCode, +} + +impl<C: Challenge> AuthenticationError<C> { + /// Creates new authentication error from the provided `challenge`. + /// + /// By default returned error will resolve into the `HTTP 401` status code. + pub fn new(challenge: C) -> AuthenticationError<C> { + AuthenticationError { + challenge, + status_code: StatusCode::UNAUTHORIZED, + } + } + + /// Returns mutable reference to the inner challenge instance. + pub fn challenge_mut(&mut self) -> &mut C { + &mut self.challenge + } + + /// Returns mutable reference to the inner status code. + /// + /// Can be used to override returned status code, but by default + /// this lib tries to stick to the RFC, so it might be unreasonable. + pub fn status_code_mut(&mut self) -> &mut StatusCode { + &mut self.status_code + } +} + +impl<C: Challenge> fmt::Display for AuthenticationError<C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.status_code, f) + } +} + +impl<C: 'static + Challenge> Error for AuthenticationError<C> {} + +impl<C: 'static + Challenge> ResponseError for AuthenticationError<C> { + fn error_response(&self) -> HttpResponse { + HttpResponse::build(self.status_code) + // TODO: Get rid of the `.clone()` + .insert_header(WwwAuthenticate(self.challenge.clone())) + .finish() + } + + fn status_code(&self) -> StatusCode { + self.status_code + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::headers::www_authenticate::basic::Basic; + use actix_web::Error; + + #[test] + fn test_status_code_is_preserved_across_error_conversions() { + let ae: AuthenticationError<Basic> = AuthenticationError::new(Basic::default()); + let expected = ae.status_code; + + // Converting the AuthenticationError into a ResponseError should preserve the status code. + let e = Error::from(ae); + let re = e.as_response_error(); + assert_eq!(expected, re.status_code()); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +
+//! Type-safe authentication information extractors + +use actix_web::dev::ServiceRequest; +use actix_web::Error; +use std::future::Future; + +pub mod basic; +pub mod bearer; +mod config; +mod errors; + +pub use self::config::AuthExtractorConfig; +pub use self::errors::AuthenticationError; + +/// Trait implemented by types that can extract +/// HTTP authentication scheme credentials from the request. +/// +/// It is very similar to actix' `FromRequest` trait, +/// except it operates with a `ServiceRequest` struct instead, +/// therefore it can be used in the middlewares. +/// +/// You will not need it unless you want to implement your own +/// authentication scheme. +pub trait AuthExtractor: Sized { + /// The associated error which can be returned. + type Error: Into<Error>; + + /// Future that resolves into extracted credentials type. + type Future: Future<Output = Result<Self, Self::Error>>; + + /// Parse the authentication credentials from the actix' `ServiceRequest`. + fn from_service_request(req: &ServiceRequest) -> Self::Future; +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +
+use std::convert::From; +use std::error::Error; +use std::fmt; +use std::str; + +use actix_web::http::header; + +/// Possible errors while parsing `Authorization` header. +/// +/// Should not be used directly unless you are implementing +/// your own [authentication scheme](./trait.Scheme.html). +#[derive(Debug)] +pub enum ParseError { + /// Header value is malformed + Invalid, + /// Authentication scheme is missing + MissingScheme, + /// Required authentication field is missing + MissingField(&'static str), + /// Unable to convert header into the str + ToStrError(header::ToStrError), + /// Malformed base64 string + Base64DecodeError(base64::DecodeError), + /// Malformed UTF-8 string + Utf8Error(str::Utf8Error), +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let display = match self { + ParseError::Invalid => "Invalid header value".to_string(), + ParseError::MissingScheme => "Missing authorization scheme".to_string(), + ParseError::MissingField(_) => "Missing header field".to_string(), + ParseError::ToStrError(e) => e.to_string(), + ParseError::Base64DecodeError(e) => e.to_string(), + ParseError::Utf8Error(e) => e.to_string(), + }; + f.write_str(&display) + } +} + +impl Error for ParseError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + ParseError::Invalid => None, + ParseError::MissingScheme => None, + ParseError::MissingField(_) => None, + ParseError::ToStrError(e) => Some(e), + ParseError::Base64DecodeError(e) => Some(e), + ParseError::Utf8Error(e) => Some(e), + } + } +} + +impl From<header::ToStrError> for ParseError { + fn from(e: header::ToStrError) -> Self { + ParseError::ToStrError(e) + } +} +impl From<base64::DecodeError> for ParseError { + fn from(e: base64::DecodeError) -> Self { + ParseError::Base64DecodeError(e) + } +} +impl From<str::Utf8Error> for ParseError { + fn from(e: str::Utf8Error) -> Self { + ParseError::Utf8Error(e) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +
+use std::fmt; + +use actix_web::error::ParseError; +use actix_web::http::header::{ + Header, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION, +}; +use actix_web::HttpMessage; + +use crate::headers::authorization::scheme::Scheme; + +/// `Authorization` header, defined in [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.2) +/// +/// The "Authorization" header field allows a user agent to authenticate +/// itself with an origin server -- usually, but not necessarily, after +/// receiving a 401 (Unauthorized) response. Its value consists of +/// credentials containing the authentication information of the user +/// agent for the realm of the resource being requested. +/// +/// `Authorization` header is generic over [authentication +/// scheme](./trait.Scheme.html). +/// +/// # Example +/// +/// ``` +/// # use actix_web::http::header::Header; +/// # use actix_web::{HttpRequest, Result}; +/// # use actix_web_httpauth::headers::authorization::{Authorization, Basic}; +/// fn handler(req: HttpRequest) -> Result<String> { +/// let auth = Authorization::<Basic>::parse(&req)?; +/// +/// Ok(format!("Hello, {}!", auth.as_ref().user_id())) +/// } +/// ``` +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)] +pub struct Authorization<S: Scheme>(S); + +impl<S> Authorization<S> +where + S: Scheme, +{ + /// Consumes `Authorization` header and returns inner [`Scheme`] + /// implementation. + /// + /// [`Scheme`]: ./trait.Scheme.html + pub fn into_scheme(self) -> S { + self.0 + } +} + +impl<S> From<S> for Authorization<S> +where + S: Scheme, +{ + fn from(scheme: S) -> Authorization<S> { + Authorization(scheme) + } +} + +impl<S> AsRef<S> for Authorization<S> +where + S: Scheme, +{ + fn as_ref(&self) -> &S { + &self.0 + } +} + +impl<S> AsMut<S> for Authorization<S> +where + S: Scheme, +{ + fn as_mut(&mut self) -> &mut S { + &mut self.0 + } +} + +impl<S: Scheme> Header for Authorization<S> { + #[inline] + fn name() -> HeaderName { + AUTHORIZATION + } + + fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> { + let header = msg.headers().get(AUTHORIZATION).ok_or(ParseError::Header)?; + let scheme = S::parse(header).map_err(|_| ParseError::Header)?; + + Ok(Authorization(scheme)) + } +} + +impl<S: Scheme> IntoHeaderValue for Authorization<S> { + type Error = <S as IntoHeaderValue>::Error; + + fn try_into_value(self) -> Result<HeaderValue, Self::Error> { + self.0.try_into_value() + } +} + +impl<S: Scheme> fmt::Display for Authorization<S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +
+//! `Authorization` header and various auth schemes + +mod errors; +mod header; +mod scheme; + +pub use self::errors::ParseError; +pub use self::header::Authorization; +pub use self::scheme::basic::Basic; +pub use self::scheme::bearer::Bearer; +pub use self::scheme::Scheme; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +
+use std::borrow::Cow; +use std::fmt; +use std::str; + +use actix_web::http::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue}; +use actix_web::web::{BufMut, BytesMut}; + +use crate::headers::authorization::errors::ParseError; +use crate::headers::authorization::Scheme; + +/// Credentials for `Basic` authentication scheme, defined in [RFC 7617](https://tools.ietf.org/html/rfc7617) +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Basic { + user_id: Cow<'static, str>, + password: Option<Cow<'static, str>>, +} + +impl Basic { + /// Creates `Basic` credentials with provided `user_id` and optional + /// `password`. + /// + /// ## Example + /// + /// ``` + /// # use actix_web_httpauth::headers::authorization::Basic; + /// let credentials = Basic::new("Alladin", Some("open sesame")); + /// ``` + pub fn new<U, P>(user_id: U, password: Option<P>) -> Basic + where + U: Into<Cow<'static, str>>, + P: Into<Cow<'static, str>>, + { + Basic { + user_id: user_id.into(), + password: password.map(Into::into), + } + } + + /// Returns client's user-ID. + pub fn user_id(&self) -> &Cow<'static, str> { + &self.user_id + } + + /// Returns client's password if provided. + pub fn password(&self) -> Option<&Cow<'static, str>> { + self.password.as_ref() + } +} + +impl Scheme for Basic { + fn parse(header: &HeaderValue) -> Result<Self, ParseError> { + // "Basic *" length + if header.len() < 7 { + return Err(ParseError::Invalid); + } + + let mut parts = header.to_str()?.splitn(2, ' '); + match parts.next() { + Some(scheme) if scheme == "Basic" => (), + _ => return Err(ParseError::MissingScheme), + } + + let decoded = base64::decode(parts.next().ok_or(ParseError::Invalid)?)?; + let mut credentials = str::from_utf8(&decoded)?.splitn(2, ':'); + + let user_id = credentials + .next() + .ok_or(ParseError::MissingField("user_id")) + .map(|user_id| user_id.to_string().into())?; + let password = credentials + .next() + .ok_or(ParseError::MissingField("password")) + .map(|password| { + if password.is_empty() { + None + } else { + Some(password.to_string().into()) + } + })?; + + Ok(Basic { user_id, password }) + } +} + +impl fmt::Debug for Basic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("Basic {}:******", self.user_id)) + } +} + +impl fmt::Display for Basic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("Basic {}:******", self.user_id)) + } +} + +impl IntoHeaderValue for Basic { + type Error = InvalidHeaderValue; + + fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> { + let mut credentials = BytesMut::with_capacity( + self.user_id.len() + + 1 // ':' + + self.password.as_ref().map_or(0, |pwd| pwd.len()), + ); + + credentials.extend_from_slice(self.user_id.as_bytes()); + credentials.put_u8(b':'); + if let Some(ref password) = self.password { + credentials.extend_from_slice(password.as_bytes()); + } + + // TODO: It would be nice not to allocate new `String` here but write + // directly to `value` + let encoded = base64::encode(&credentials); + let mut value = BytesMut::with_capacity(6 + encoded.len()); + value.put(&b"Basic "[..]); + value.put(encoded.as_bytes()); + + HeaderValue::from_maybe_shared(value.freeze()) + } +} + +#[cfg(test)] +mod tests { + use super::{Basic, Scheme}; + use actix_web::http::header::{HeaderValue, IntoHeaderValue}; + + #[test] + fn test_parse_header() { + let value = HeaderValue::from_static("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); + let scheme = Basic::parse(&value); + + assert!(scheme.is_ok()); + let scheme = scheme.unwrap(); + assert_eq!(scheme.user_id, "Aladdin"); + assert_eq!(scheme.password, Some("open sesame".into())); + } + + #[test] + fn test_empty_password() { + let value = HeaderValue::from_static("Basic QWxhZGRpbjo="); + let scheme = Basic::parse(&value); + + assert!(scheme.is_ok()); + let scheme = scheme.unwrap(); + assert_eq!(scheme.user_id, "Aladdin"); + assert_eq!(scheme.password, None); + } + + #[test] + fn test_empty_header() { + let value = HeaderValue::from_static(""); + let scheme = Basic::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_wrong_scheme() { + let value = HeaderValue::from_static("THOUSHALLNOTPASS please?"); + let scheme = Basic::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_missing_credentials() { + let value = HeaderValue::from_static("Basic "); + let scheme = Basic::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_missing_credentials_colon() { + let value = HeaderValue::from_static("Basic QWxsYWRpbg=="); + let scheme = Basic::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_into_header_value() { + let basic = Basic { + user_id: "Aladdin".into(), + password: Some("open sesame".into()), + }; + + let result = basic.try_into_value(); + assert!(result.is_ok()); + assert_eq!( + result.unwrap(), + HeaderValue::from_static("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==") + ); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +
+use std::borrow::Cow; +use std::fmt; + +use actix_web::http::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue}; +use actix_web::web::{BufMut, BytesMut}; + +use crate::headers::authorization::errors::ParseError; +use crate::headers::authorization::scheme::Scheme; + +/// Credentials for `Bearer` authentication scheme, defined in [RFC6750](https://tools.ietf.org/html/rfc6750) +/// +/// Should be used in combination with +/// [`Authorization`](./struct.Authorization.html) header. +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Bearer { + token: Cow<'static, str>, +} + +impl Bearer { + /// Creates new `Bearer` credentials with the token provided. + /// + /// ## Example + /// + /// ``` + /// # use actix_web_httpauth::headers::authorization::Bearer; + /// let credentials = Bearer::new("mF_9.B5f-4.1JqM"); + /// ``` + pub fn new<T>(token: T) -> Bearer + where + T: Into<Cow<'static, str>>, + { + Bearer { + token: token.into(), + } + } + + /// Gets reference to the credentials token. + pub fn token(&self) -> &Cow<'static, str> { + &self.token + } +} + +impl Scheme for Bearer { + fn parse(header: &HeaderValue) -> Result<Self, ParseError> { + // "Bearer *" length + if header.len() < 8 { + return Err(ParseError::Invalid); + } + + let mut parts = header.to_str()?.splitn(2, ' '); + match parts.next() { + Some(scheme) if scheme == "Bearer" => (), + _ => return Err(ParseError::MissingScheme), + } + + let token = parts.next().ok_or(ParseError::Invalid)?; + + Ok(Bearer { + token: token.to_string().into(), + }) + } +} + +impl fmt::Debug for Bearer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("Bearer ******")) + } +} + +impl fmt::Display for Bearer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("Bearer {}", self.token)) + } +} + +impl IntoHeaderValue for Bearer { + type Error = InvalidHeaderValue; + + fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> { + let mut buffer = BytesMut::with_capacity(7 + self.token.len()); + buffer.put(&b"Bearer "[..]); + buffer.extend_from_slice(self.token.as_bytes()); + + HeaderValue::from_maybe_shared(buffer.freeze()) + } +} + +#[cfg(test)] +mod tests { + use super::{Bearer, Scheme}; + use actix_web::http::header::{HeaderValue, IntoHeaderValue}; + + #[test] + fn test_parse_header() { + let value = HeaderValue::from_static("Bearer mF_9.B5f-4.1JqM"); + let scheme = Bearer::parse(&value); + + assert!(scheme.is_ok()); + let scheme = scheme.unwrap(); + assert_eq!(scheme.token, "mF_9.B5f-4.1JqM"); + } + + #[test] + fn test_empty_header() { + let value = HeaderValue::from_static(""); + let scheme = Bearer::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_wrong_scheme() { + let value = HeaderValue::from_static("OAuthToken foo"); + let scheme = Bearer::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_missing_token() { + let value = HeaderValue::from_static("Bearer "); + let scheme = Bearer::parse(&value); + + assert!(scheme.is_err()); + } + + #[test] + fn test_into_header_value() { + let bearer = Bearer::new("mF_9.B5f-4.1JqM"); + + let result = bearer.try_into_value(); + assert!(result.is_ok()); + assert_eq!( + result.unwrap(), + HeaderValue::from_static("Bearer mF_9.B5f-4.1JqM") + ); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +
+use std::fmt::{Debug, Display}; + +use actix_web::http::header::{HeaderValue, IntoHeaderValue}; + +pub mod basic; +pub mod bearer; + +use crate::headers::authorization::errors::ParseError; + +/// Authentication scheme for [`Authorization`](./struct.Authorization.html) +/// header. +pub trait Scheme: IntoHeaderValue + Debug + Display + Clone + Send + Sync { + /// Try to parse the authentication scheme from the `Authorization` header. + fn parse(header: &HeaderValue) -> Result<Self, ParseError>; +} +
1 +2 +3 +4 +
+//! Typed HTTP headers + +pub mod authorization; +pub mod www_authenticate; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +
+//! Challenge for the "Basic" HTTP Authentication Scheme + +use std::borrow::Cow; +use std::default::Default; +use std::fmt; +use std::str; + +use actix_web::http::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue}; +use actix_web::web::{BufMut, Bytes, BytesMut}; + +use super::Challenge; +use crate::utils; + +/// Challenge for [`WWW-Authenticate`] header with HTTP Basic auth scheme, +/// described in [RFC 7617](https://tools.ietf.org/html/rfc7617) +/// +/// ## Example +/// +/// ``` +/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; +/// use actix_web_httpauth::headers::www_authenticate::basic::Basic; +/// use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; +/// +/// fn index(_req: HttpRequest) -> HttpResponse { +/// let challenge = Basic::with_realm("Restricted area"); +/// +/// HttpResponse::Unauthorized() +/// .insert_header(WwwAuthenticate(challenge)) +/// .finish() +/// } +/// ``` +/// +/// [`WWW-Authenticate`]: ../struct.WwwAuthenticate.html +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)] +pub struct Basic { + // "realm" parameter is optional now: https://tools.ietf.org/html/rfc7235#appendix-A + pub(crate) realm: Option<Cow<'static, str>>, +} + +impl Basic { + /// Creates new `Basic` challenge with an empty `realm` field. + /// + /// ## Example + /// + /// ``` + /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; + /// let challenge = Basic::new(); + /// ``` + pub fn new() -> Basic { + Default::default() + } + + /// Creates new `Basic` challenge from the provided `realm` field value. + /// + /// ## Examples + /// + /// ``` + /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; + /// let challenge = Basic::with_realm("Restricted area"); + /// ``` + /// + /// ``` + /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; + /// let my_realm = "Earth realm".to_string(); + /// let challenge = Basic::with_realm(my_realm); + /// ``` + pub fn with_realm<T>(value: T) -> Basic + where + T: Into<Cow<'static, str>>, + { + Basic { + realm: Some(value.into()), + } + } +} + +#[doc(hidden)] +impl Challenge for Basic { + fn to_bytes(&self) -> Bytes { + // 5 is for `"Basic"`, 9 is for `"realm=\"\""` + let length = 5 + self.realm.as_ref().map_or(0, |realm| realm.len() + 9); + let mut buffer = BytesMut::with_capacity(length); + buffer.put(&b"Basic"[..]); + if let Some(ref realm) = self.realm { + buffer.put(&b" realm=\""[..]); + utils::put_quoted(&mut buffer, realm); + buffer.put_u8(b'"'); + } + + buffer.freeze() + } +} + +impl fmt::Display for Basic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let bytes = self.to_bytes(); + let repr = str::from_utf8(&bytes) + // Should not happen since challenges are crafted manually + // from a `&'static str` or `String` + .map_err(|_| fmt::Error)?; + + f.write_str(repr) + } +} + +impl IntoHeaderValue for Basic { + type Error = InvalidHeaderValue; + + fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> { + HeaderValue::from_maybe_shared(self.to_bytes()) + } +} + +#[cfg(test)] +mod tests { + use super::Basic; + use actix_web::http::header::IntoHeaderValue; + + #[test] + fn test_plain_into_header_value() { + let challenge = Basic { realm: None }; + + let value = challenge.try_into_value(); + assert!(value.is_ok()); + let value = value.unwrap(); + assert_eq!(value, "Basic"); + } + + #[test] + fn test_with_realm_into_header_value() { + let challenge = Basic { + realm: Some("Restricted area".into()), + }; + + let value = challenge.try_into_value(); + assert!(value.is_ok()); + let value = value.unwrap(); + assert_eq!(value, "Basic realm=\"Restricted area\""); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +
+use std::borrow::Cow; + +use super::{Bearer, Error}; + +/// Builder for the [`Bearer`] challenge. +/// +/// It is up to implementor to fill all required fields, +/// neither this `Builder` or [`Bearer`] does not provide any validation. +/// +/// [`Bearer`]: struct.Bearer.html +#[derive(Debug, Default)] +pub struct BearerBuilder(Bearer); + +impl BearerBuilder { + /// Provides the `scope` attribute, as defined in [RFC6749, Section 3.3](https://tools.ietf.org/html/rfc6749#section-3.3) + pub fn scope<T>(mut self, value: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.0.scope = Some(value.into()); + self + } + + /// Provides the `realm` attribute, as defined in [RFC2617](https://tools.ietf.org/html/rfc2617) + pub fn realm<T>(mut self, value: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.0.realm = Some(value.into()); + self + } + + /// Provides the `error` attribute, as defined in [RFC6750, Section 3.1](https://tools.ietf.org/html/rfc6750#section-3.1) + pub fn error(mut self, value: Error) -> Self { + self.0.error = Some(value); + self + } + + /// Provides the `error_description` attribute, as defined in [RFC6750, Section 3](https://tools.ietf.org/html/rfc6750#section-3) + pub fn error_description<T>(mut self, value: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.0.error_description = Some(value.into()); + self + } + + /// Provides the `error_uri` attribute, as defined in [RFC6750, Section 3](https://tools.ietf.org/html/rfc6750#section-3) + /// + /// It is up to implementor to provide properly-formed absolute URI. + pub fn error_uri<T>(mut self, value: T) -> Self + where + T: Into<Cow<'static, str>>, + { + self.0.error_uri = Some(value.into()); + self + } + + /// Consumes the builder and returns built `Bearer` instance. + pub fn finish(self) -> Bearer { + self.0 + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +
+use std::borrow::Cow; +use std::fmt; +use std::str; + +use actix_web::http::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue}; +use actix_web::web::{BufMut, Bytes, BytesMut}; + +use super::super::Challenge; +use super::{BearerBuilder, Error}; +use crate::utils; + +/// Challenge for [`WWW-Authenticate`] header with HTTP Bearer auth scheme, +/// described in [RFC 6750](https://tools.ietf.org/html/rfc6750#section-3) +/// +/// ## Example +/// +/// ``` +/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; +/// use actix_web_httpauth::headers::www_authenticate::bearer::{ +/// Bearer, Error, +/// }; +/// use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; +/// +/// fn index(_req: HttpRequest) -> HttpResponse { +/// let challenge = Bearer::build() +/// .realm("example") +/// .scope("openid profile email") +/// .error(Error::InvalidToken) +/// .error_description("The access token expired") +/// .error_uri("http://example.org") +/// .finish(); +/// +/// HttpResponse::Unauthorized() +/// .insert_header(WwwAuthenticate(challenge)) +/// .finish() +/// } +/// ``` +/// +/// [`WWW-Authenticate`]: ../struct.WwwAuthenticate.html +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)] +pub struct Bearer { + pub(crate) scope: Option<Cow<'static, str>>, + pub(crate) realm: Option<Cow<'static, str>>, + pub(crate) error: Option<Error>, + pub(crate) error_description: Option<Cow<'static, str>>, + pub(crate) error_uri: Option<Cow<'static, str>>, +} + +impl Bearer { + /// Creates the builder for `Bearer` challenge. + /// + /// ## Example + /// + /// ``` + /// # use actix_web_httpauth::headers::www_authenticate::bearer::{Bearer}; + /// let challenge = Bearer::build() + /// .realm("Restricted area") + /// .scope("openid profile email") + /// .finish(); + /// ``` + pub fn build() -> BearerBuilder { + BearerBuilder::default() + } +} + +#[doc(hidden)] +impl Challenge for Bearer { + fn to_bytes(&self) -> Bytes { + let desc_uri_required = self + .error_description + .as_ref() + .map_or(0, |desc| desc.len() + 20) + + self.error_uri.as_ref().map_or(0, |url| url.len() + 12); + let capacity = 6 + + self.realm.as_ref().map_or(0, |realm| realm.len() + 9) + + self.scope.as_ref().map_or(0, |scope| scope.len() + 9) + + desc_uri_required; + let mut buffer = BytesMut::with_capacity(capacity); + buffer.put(&b"Bearer"[..]); + + if let Some(ref realm) = self.realm { + buffer.put(&b" realm=\""[..]); + utils::put_quoted(&mut buffer, realm); + buffer.put_u8(b'"'); + } + + if let Some(ref scope) = self.scope { + buffer.put(&b" scope=\""[..]); + utils::put_quoted(&mut buffer, scope); + buffer.put_u8(b'"'); + } + + if let Some(ref error) = self.error { + let error_repr = error.as_str(); + let remaining = buffer.remaining_mut(); + let required = desc_uri_required + error_repr.len() + 9; // 9 is for `" error=\"\""` + if remaining < required { + buffer.reserve(required); + } + buffer.put(&b" error=\""[..]); + utils::put_quoted(&mut buffer, error_repr); + buffer.put_u8(b'"') + } + + if let Some(ref error_description) = self.error_description { + buffer.put(&b" error_description=\""[..]); + utils::put_quoted(&mut buffer, error_description); + buffer.put_u8(b'"'); + } + + if let Some(ref error_uri) = self.error_uri { + buffer.put(&b" error_uri=\""[..]); + utils::put_quoted(&mut buffer, error_uri); + buffer.put_u8(b'"'); + } + + buffer.freeze() + } +} + +impl fmt::Display for Bearer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let bytes = self.to_bytes(); + let repr = str::from_utf8(&bytes) + // Should not happen since challenges are crafted manually + // from `&'static str`'s and Strings + .map_err(|_| fmt::Error)?; + + f.write_str(repr) + } +} + +impl IntoHeaderValue for Bearer { + type Error = InvalidHeaderValue; + + fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> { + HeaderValue::from_maybe_shared(self.to_bytes()) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +
+use std::fmt; + +use actix_web::http::StatusCode; + +/// Bearer authorization error types, described in [RFC 6750](https://tools.ietf.org/html/rfc6750#section-3.1) +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum Error { + /// The request is missing a required parameter, includes an unsupported + /// parameter or parameter value, repeats the same parameter, uses more + /// than one method for including an access token, or is otherwise + /// malformed. + InvalidRequest, + + /// The access token provided is expired, revoked, malformed, or invalid + /// for other reasons. + InvalidToken, + + /// The request requires higher privileges than provided by the access + /// token. + InsufficientScope, +} + +impl Error { + /// Returns [HTTP status code] suitable for current error type. + /// + /// [HTTP status code]: `actix_web::http::StatusCode` + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn status_code(&self) -> StatusCode { + match self { + Error::InvalidRequest => StatusCode::BAD_REQUEST, + Error::InvalidToken => StatusCode::UNAUTHORIZED, + Error::InsufficientScope => StatusCode::FORBIDDEN, + } + } + + #[doc(hidden)] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_str(&self) -> &str { + match self { + Error::InvalidRequest => "invalid_request", + Error::InvalidToken => "invalid_token", + Error::InsufficientScope => "insufficient_scope", + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +
+//! Challenge for the "Bearer" HTTP Authentication Scheme + +mod builder; +mod challenge; +mod errors; + +pub use self::builder::BearerBuilder; +pub use self::challenge::Bearer; +pub use self::errors::Error; + +#[cfg(test)] +mod tests; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +
+use std::fmt::{Debug, Display}; + +use actix_web::http::header::IntoHeaderValue; +use actix_web::web::Bytes; + +pub mod basic; +pub mod bearer; + +/// Authentication challenge for `WWW-Authenticate` header. +pub trait Challenge: IntoHeaderValue + Debug + Display + Clone + Send + Sync { + /// Converts the challenge into a bytes suitable for HTTP transmission. + fn to_bytes(&self) -> Bytes; +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +
+use actix_web::error::ParseError; +use actix_web::http::header::{ + Header, HeaderName, HeaderValue, IntoHeaderValue, WWW_AUTHENTICATE, +}; +use actix_web::HttpMessage; + +use super::Challenge; + +/// `WWW-Authenticate` header, described in [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.1) +/// +/// This header is generic over [Challenge](./trait.Challenge.html) trait, +/// see [Basic](./basic/struct.Basic.html) and +/// [Bearer](./bearer/struct.Bearer.html) challenges for details. +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)] +pub struct WwwAuthenticate<C: Challenge>(pub C); + +impl<C: Challenge> Header for WwwAuthenticate<C> { + fn name() -> HeaderName { + WWW_AUTHENTICATE + } + + fn parse<T: HttpMessage>(_msg: &T) -> Result<Self, ParseError> { + unimplemented!() + } +} + +impl<C: Challenge> IntoHeaderValue for WwwAuthenticate<C> { + type Error = <C as IntoHeaderValue>::Error; + + fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> { + self.0.try_into_value() + } +} +
1 +2 +3 +4 +5 +6 +7 +8 +9 +
+//! `WWW-Authenticate` header and various auth challenges + +mod challenge; +mod header; + +pub use self::challenge::basic; +pub use self::challenge::bearer; +pub use self::challenge::Challenge; +pub use self::header::WwwAuthenticate; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +
+//! HTTP authentication schemes for [actix-web](https://actix.rs). +//! +//! Provides: +//! - Typed [Authorization] and [WWW-Authenticate] headers +//! - [Extractors] for an [Authorization] header +//! - [Middleware] for easier authorization checking +//! +//! ## Supported schemes +//! +//! - `Basic`, as defined in [RFC7617](https://tools.ietf.org/html/rfc7617) +//! - `Bearer`, as defined in [RFC6750](https://tools.ietf.org/html/rfc6750) +//! +//! [Authorization]: `crate::headers::authorization::Authorization` +//! [WWW-Authenticate]: `crate::headers::www_authenticate::WwwAuthenticate` +//! [Extractors]: https://actix.rs/docs/extractors/ +//! [Middleware]: ./middleware + +#![deny(missing_docs, nonstandard_style, rust_2018_idioms)] +#![deny(clippy::all)] + +pub mod extractors; +pub mod headers; +pub mod middleware; +mod utils; +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +
+//! HTTP Authentication middleware. + +use std::{future::Future, marker::PhantomData, pin::Pin, rc::Rc, sync::Arc}; + +use actix_web::{ + dev::{Service, ServiceRequest, ServiceResponse, Transform}, + Error, +}; +use futures_util::{ + future::{self, FutureExt as _, LocalBoxFuture, TryFutureExt as _}, + ready, + task::{Context, Poll}, +}; + +use crate::extractors::{basic, bearer, AuthExtractor}; + +/// Middleware for checking HTTP authentication. +/// +/// If there is no `Authorization` header in the request, this middleware returns an error +/// immediately, without calling the `F` callback. +/// +/// Otherwise, it will pass both the request and the parsed credentials into it. In case of +/// successful validation `F` callback is required to return the `ServiceRequest` back. +#[derive(Debug, Clone)] +pub struct HttpAuthentication<T, F> +where + T: AuthExtractor, +{ + process_fn: Arc<F>, + _extractor: PhantomData<T>, +} + +impl<T, F, O> HttpAuthentication<T, F> +where + T: AuthExtractor, + F: Fn(ServiceRequest, T) -> O, + O: Future<Output = Result<ServiceRequest, Error>>, +{ + /// Construct `HttpAuthentication` middleware with the provided auth extractor `T` and + /// validation callback `F`. + pub fn with_fn(process_fn: F) -> HttpAuthentication<T, F> { + HttpAuthentication { + process_fn: Arc::new(process_fn), + _extractor: PhantomData, + } + } +} + +impl<F, O> HttpAuthentication<basic::BasicAuth, F> +where + F: Fn(ServiceRequest, basic::BasicAuth) -> O, + O: Future<Output = Result<ServiceRequest, Error>>, +{ + /// Construct `HttpAuthentication` middleware for the HTTP "Basic" authentication scheme. + /// + /// # Example + /// + /// ``` + /// # use actix_web::Error; + /// # use actix_web::dev::ServiceRequest; + /// # use actix_web_httpauth::middleware::HttpAuthentication; + /// # use actix_web_httpauth::extractors::basic::BasicAuth; + /// // In this example validator returns immediately, but since it is required to return + /// // anything that implements `IntoFuture` trait, it can be extended to query database or to + /// // do something else in a async manner. + /// async fn validator( + /// req: ServiceRequest, + /// credentials: BasicAuth, + /// ) -> Result<ServiceRequest, Error> { + /// // All users are great and more than welcome! + /// Ok(req) + /// } + /// + /// let middleware = HttpAuthentication::basic(validator); + /// ``` + pub fn basic(process_fn: F) -> Self { + Self::with_fn(process_fn) + } +} + +impl<F, O> HttpAuthentication<bearer::BearerAuth, F> +where + F: Fn(ServiceRequest, bearer::BearerAuth) -> O, + O: Future<Output = Result<ServiceRequest, Error>>, +{ + /// Construct `HttpAuthentication` middleware for the HTTP "Bearer" authentication scheme. + /// + /// # Example + /// + /// ``` + /// # use actix_web::Error; + /// # use actix_web::dev::ServiceRequest; + /// # use actix_web_httpauth::middleware::HttpAuthentication; + /// # use actix_web_httpauth::extractors::bearer::{Config, BearerAuth}; + /// # use actix_web_httpauth::extractors::{AuthenticationError, AuthExtractorConfig}; + /// async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> { + /// if credentials.token() == "mF_9.B5f-4.1JqM" { + /// Ok(req) + /// } else { + /// let config = req.app_data::<Config>() + /// .map(|data| data.clone()) + /// .unwrap_or_else(Default::default) + /// .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13"); + /// + /// Err(AuthenticationError::from(config).into()) + /// } + /// } + /// + /// let middleware = HttpAuthentication::bearer(validator); + /// ``` + pub fn bearer(process_fn: F) -> Self { + Self::with_fn(process_fn) + } +} + +impl<S, B, T, F, O> Transform<S, ServiceRequest> for HttpAuthentication<T, F> +where + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, + S::Future: 'static, + F: Fn(ServiceRequest, T) -> O + 'static, + O: Future<Output = Result<ServiceRequest, Error>> + 'static, + T: AuthExtractor + 'static, +{ + type Response = ServiceResponse<B>; + type Error = Error; + type Transform = AuthenticationMiddleware<S, F, T>; + type InitError = (); + type Future = future::Ready<Result<Self::Transform, Self::InitError>>; + + fn new_transform(&self, service: S) -> Self::Future { + future::ok(AuthenticationMiddleware { + service: Rc::new(service), + process_fn: self.process_fn.clone(), + _extractor: PhantomData, + }) + } +} + +#[doc(hidden)] +pub struct AuthenticationMiddleware<S, F, T> +where + T: AuthExtractor, +{ + service: Rc<S>, + process_fn: Arc<F>, + _extractor: PhantomData<T>, +} + +impl<S, B, F, T, O> Service<ServiceRequest> for AuthenticationMiddleware<S, F, T> +where + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, + S::Future: 'static, + F: Fn(ServiceRequest, T) -> O + 'static, + O: Future<Output = Result<ServiceRequest, Error>> + 'static, + T: AuthExtractor + 'static, +{ + type Response = ServiceResponse<B>; + type Error = S::Error; + type Future = LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>>; + + actix_service::forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + let process_fn = Arc::clone(&self.process_fn); + + let service = Rc::clone(&self.service); + + async move { + let (req, credentials) = match Extract::<T>::new(req).await { + Ok(req) => req, + Err((err, req)) => { + return Ok(req.error_response(err)); + } + }; + + // TODO: alter to remove ? operator; an error response is required for downstream + // middleware to do their thing (eg. cors adding headers) + let req = process_fn(req, credentials).await?; + + service.call(req).await + } + .boxed_local() + } +} + +struct Extract<T> { + req: Option<ServiceRequest>, + f: Option<LocalBoxFuture<'static, Result<T, Error>>>, + _extractor: PhantomData<fn() -> T>, +} + +impl<T> Extract<T> { + pub fn new(req: ServiceRequest) -> Self { + Extract { + req: Some(req), + f: None, + _extractor: PhantomData, + } + } +} + +impl<T> Future for Extract<T> +where + T: AuthExtractor, + T::Future: 'static, + T::Error: 'static, +{ + type Output = Result<(ServiceRequest, T), (Error, ServiceRequest)>; + + fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> { + if self.f.is_none() { + let req = self.req.as_ref().expect("Extract future was polled twice!"); + let f = T::from_service_request(req).map_err(Into::into); + self.f = Some(f.boxed_local()); + } + + let f = self + .f + .as_mut() + .expect("Extraction future should be initialized at this point"); + + let credentials = ready!(f.as_mut().poll(ctx)).map_err(|err| { + ( + err, + // returning request allows a proper error response to be created + self.req.take().expect("Extract future was polled twice!"), + ) + })?; + + let req = self.req.take().expect("Extract future was polled twice!"); + Poll::Ready(Ok((req, credentials))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::extractors::bearer::BearerAuth; + use actix_service::{into_service, Service}; + use actix_web::error; + use actix_web::test::TestRequest; + + /// This is a test for https://github.com/actix/actix-extras/issues/10 + #[actix_rt::test] + async fn test_middleware_panic() { + let middleware = AuthenticationMiddleware { + service: Rc::new(into_service(|_: ServiceRequest| async move { + actix_rt::time::sleep(std::time::Duration::from_secs(1)).await; + Err::<ServiceResponse, _>(error::ErrorBadRequest("error")) + })), + process_fn: Arc::new(|req, _: BearerAuth| async { Ok(req) }), + _extractor: PhantomData, + }; + + let req = TestRequest::get() + .append_header(("Authorization", "Bearer 1")) + .to_srv_request(); + + let f = middleware.call(req).await; + + let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; + + assert!(f.is_err()); + } + + /// This is a test for https://github.com/actix/actix-extras/issues/10 + #[actix_rt::test] + async fn test_middleware_panic_several_orders() { + let middleware = AuthenticationMiddleware { + service: Rc::new(into_service(|_: ServiceRequest| async move { + actix_rt::time::sleep(std::time::Duration::from_secs(1)).await; + Err::<ServiceResponse, _>(error::ErrorBadRequest("error")) + })), + process_fn: Arc::new(|req, _: BearerAuth| async { Ok(req) }), + _extractor: PhantomData, + }; + + let req = TestRequest::get() + .append_header(("Authorization", "Bearer 1")) + .to_srv_request(); + + let f1 = middleware.call(req).await; + + let req = TestRequest::get() + .append_header(("Authorization", "Bearer 1")) + .to_srv_request(); + + let f2 = middleware.call(req).await; + + let req = TestRequest::get() + .append_header(("Authorization", "Bearer 1")) + .to_srv_request(); + + let f3 = middleware.call(req).await; + + let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; + + assert!(f1.is_err()); + assert!(f2.is_err()); + assert!(f3.is_err()); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +
+use std::str; + +use actix_web::web::BytesMut; + +enum State { + YieldStr, + YieldQuote, +} + +struct Quoted<'a> { + inner: ::std::iter::Peekable<str::Split<'a, char>>, + state: State, +} + +impl<'a> Quoted<'a> { + pub fn new(s: &'a str) -> Quoted<'_> { + Quoted { + inner: s.split('"').peekable(), + state: State::YieldStr, + } + } +} + +impl<'a> Iterator for Quoted<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<Self::Item> { + match self.state { + State::YieldStr => match self.inner.next() { + Some(s) => { + self.state = State::YieldQuote; + Some(s) + } + None => None, + }, + State::YieldQuote => match self.inner.peek() { + Some(_) => { + self.state = State::YieldStr; + Some("\\\"") + } + None => None, + }, + } + } +} + +/// Tries to quote the quotes in the passed `value` +pub fn put_quoted(buf: &mut BytesMut, value: &str) { + for part in Quoted::new(value) { + buf.extend_from_slice(part.as_bytes()); + } +} + +#[cfg(test)] +mod tests { + use std::str; + + use actix_web::web::BytesMut; + + use super::put_quoted; + + #[test] + fn test_quote_str() { + let input = "a \"quoted\" string"; + let mut output = BytesMut::new(); + put_quoted(&mut output, input); + let result = str::from_utf8(&output).unwrap(); + + assert_eq!(result, "a \\\"quoted\\\" string"); + } + + #[test] + fn test_without_quotes() { + let input = "non-quoted string"; + let mut output = BytesMut::new(); + put_quoted(&mut output, input); + let result = str::from_utf8(&output).unwrap(); + + assert_eq!(result, "non-quoted string"); + } + + #[test] + fn test_starts_with_quote() { + let input = "\"first-quoted string"; + let mut output = BytesMut::new(); + put_quoted(&mut output, input); + let result = str::from_utf8(&output).unwrap(); + + assert_eq!(result, "\\\"first-quoted string"); + } + + #[test] + fn test_ends_with_quote() { + let input = "last-quoted string\""; + let mut output = BytesMut::new(); + put_quoted(&mut output, input); + let result = str::from_utf8(&output).unwrap(); + + assert_eq!(result, "last-quoted string\\\""); + } + + #[test] + fn test_double_quote() { + let input = "quote\"\"string"; + let mut output = BytesMut::new(); + put_quoted(&mut output, input); + let result = str::from_utf8(&output).unwrap(); + + assert_eq!(result, "quote\\\"\\\"string"); + } +} +
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +
+use actix_protobuf::*; +use actix_web::*; +use prost_derive::Message; + +#[derive(Clone, PartialEq, Message)] +pub struct MyObj { + #[prost(int32, tag = "1")] + pub number: i32, + + #[prost(string, tag = "2")] + pub name: String, +} + +async fn index(msg: ProtoBuf<MyObj>) -> Result<HttpResponse> { + println!("model: {:?}", msg); + HttpResponse::Ok().protobuf(msg.0) // <- send response +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=debug,actix_server=info"); + env_logger::init(); + + HttpServer::new(|| { + App::new() + .wrap(middleware::Logger::default()) + .service(web::resource("/").route(web::post().to(index))) + }) + .bind("127.0.0.1:8081")? + .shutdown_timeout(1) + .run() + .await +} +