mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-23 15:51:06 +01:00
improve httpauth ergonomics (#264)
* improve httpauth ergonomics * update changelog * code and docs cleanup * docs * docs clean * remove AuthExtractor trait * update changelog
This commit is contained in:
parent
4d2f4d58b4
commit
ff06958b32
@ -1,10 +1,15 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2022-xx-xx
|
## Unreleased - 2022-xx-xx
|
||||||
- `BasicAuth::user_id()` now returns a `&str`. [#249]
|
- Removed `AuthExtractor` trait; implement `FromRequest` for your custom auth types. [#264]
|
||||||
- `BasicAuth::password()` now returns a `&str`. [#249]
|
- `BasicAuth::user_id()` now returns `&str`. [#249]
|
||||||
|
- `BasicAuth::password()` now returns `Option<&str>`. [#249]
|
||||||
|
- `Basic::user_id()` now returns `&str`. [#264]
|
||||||
|
- `Basic::password()` now returns `Option<&str>`. [#264]
|
||||||
|
- `Bearer::token()` now returns `&str`. [#264]
|
||||||
|
|
||||||
[#249]: https://github.com/actix/actix-extras/pull/249
|
[#249]: https://github.com/actix/actix-extras/pull/249
|
||||||
|
[#264]: https://github.com/actix/actix-extras/pull/264
|
||||||
|
|
||||||
|
|
||||||
## 0.7.0 - 2022-07-19
|
## 0.7.0 - 2022-07-19
|
||||||
|
@ -18,15 +18,16 @@ name = "actix_web_httpauth"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2"
|
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4", default_features = false }
|
actix-web = { version = "4.1", default_features = false }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-core = "0.3.7"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
|
||||||
|
log = "0.4"
|
||||||
pin-project-lite = "0.2.7"
|
pin-project-lite = "0.2.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-cors = "0.6.0"
|
actix-cors = "0.6"
|
||||||
actix-web = { version = "4", default_features = false, features = ["macros"] }
|
actix-service = "2"
|
||||||
|
actix-web = { version = "4.1", default_features = false, features = ["macros"] }
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
//! Extractor for the "Basic" HTTP Authentication Scheme
|
//! Extractor for the "Basic" HTTP Authentication Scheme.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use actix_web::dev::{Payload, ServiceRequest};
|
use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest};
|
||||||
use actix_web::http::header::Header;
|
|
||||||
use actix_web::{FromRequest, HttpRequest};
|
|
||||||
|
|
||||||
use super::config::AuthExtractorConfig;
|
use super::{config::AuthExtractorConfig, errors::AuthenticationError};
|
||||||
use super::errors::AuthenticationError;
|
use crate::headers::{
|
||||||
use super::AuthExtractor;
|
authorization::{Authorization, Basic},
|
||||||
use crate::headers::authorization::{Authorization, Basic};
|
www_authenticate::basic::Basic as Challenge,
|
||||||
use crate::headers::www_authenticate::basic::Basic as Challenge;
|
};
|
||||||
|
|
||||||
/// [`BasicAuth`] extractor configuration,
|
/// [`BasicAuth`] extractor configuration used for [`WWW-Authenticate`] header later.
|
||||||
/// used for [`WWW-Authenticate`] header later.
|
|
||||||
///
|
///
|
||||||
/// [`BasicAuth`]: ./struct.BasicAuth.html
|
/// [`WWW-Authenticate`]: crate::headers::www_authenticate::WwwAuthenticate
|
||||||
/// [`WWW-Authenticate`]:
|
|
||||||
/// ../../headers/www_authenticate/struct.WwwAuthenticate.html
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Config(Challenge);
|
pub struct Config(Challenge);
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Set challenge `realm` attribute.
|
/// Set challenge `realm` attribute.
|
||||||
///
|
///
|
||||||
/// The "realm" attribute indicates the scope of protection in the manner
|
/// The "realm" attribute indicates the scope of protection in the manner described in HTTP/1.1
|
||||||
/// described in HTTP/1.1 [RFC2617](https://tools.ietf.org/html/rfc2617#section-1.2).
|
/// [RFC 2617 §1.2](https://tools.ietf.org/html/rfc2617#section-1.2).
|
||||||
pub fn realm<T>(mut self, value: T) -> Config
|
pub fn realm<T>(mut self, value: T) -> Config
|
||||||
where
|
where
|
||||||
T: Into<Cow<'static, str>>,
|
T: Into<Cow<'static, str>>,
|
||||||
@ -50,14 +45,10 @@ impl AuthExtractorConfig for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs `fn main` to display complete example.
|
|
||||||
#[allow(clippy::needless_doctest_main)]
|
|
||||||
/// Extractor for HTTP Basic auth.
|
/// Extractor for HTTP Basic auth.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::Result;
|
|
||||||
/// use actix_web_httpauth::extractors::basic::BasicAuth;
|
/// use actix_web_httpauth::extractors::basic::BasicAuth;
|
||||||
///
|
///
|
||||||
/// async fn index(auth: BasicAuth) -> String {
|
/// async fn index(auth: BasicAuth) -> String {
|
||||||
@ -65,41 +56,36 @@ impl AuthExtractorConfig for Config {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// If authentication fails, this extractor fetches the [`Config`] instance
|
/// If authentication fails, this extractor fetches the [`Config`] instance from the [app data] in
|
||||||
/// from the [app data] in order to properly form the `WWW-Authenticate`
|
/// order to properly form the `WWW-Authenticate` response header.
|
||||||
/// response header.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
///
|
||||||
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::{web, App};
|
/// use actix_web::{web, App};
|
||||||
/// use actix_web_httpauth::extractors::basic::{BasicAuth, Config};
|
/// use actix_web_httpauth::extractors::basic::{self, BasicAuth};
|
||||||
///
|
///
|
||||||
/// async fn index(auth: BasicAuth) -> String {
|
/// async fn index(auth: BasicAuth) -> String {
|
||||||
/// format!("Hello, {}!", auth.user_id())
|
/// format!("Hello, {}!", auth.user_id())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// App::new()
|
||||||
/// let app = App::new()
|
/// .app_data(basic::Config::default().realm("Restricted area"))
|
||||||
/// .app_data(Config::default().realm("Restricted area"))
|
/// .service(web::resource("/index.html").route(web::get().to(index)));
|
||||||
/// .service(web::resource("/index.html").route(web::get().to(index)));
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Config`]: ./struct.Config.html
|
/// [app data]: https://docs.rs/actix-web/4/actix_web/struct.App.html#method.app_data
|
||||||
/// [app data]: https://docs.rs/actix-web/1.0.0-beta.5/actix_web/struct.App.html#method.data
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BasicAuth(Basic);
|
pub struct BasicAuth(Basic);
|
||||||
|
|
||||||
impl BasicAuth {
|
impl BasicAuth {
|
||||||
/// Returns client's user-ID.
|
/// Returns client's user-ID.
|
||||||
pub fn user_id(&self) -> &str {
|
pub fn user_id(&self) -> &str {
|
||||||
self.0.user_id().as_ref()
|
self.0.user_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns client's password.
|
/// Returns client's password.
|
||||||
pub fn password(&self) -> Option<&str> {
|
pub fn password(&self) -> Option<&str> {
|
||||||
self.0.password().map(|s| s.as_ref())
|
self.0.password()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,35 +97,13 @@ impl FromRequest for BasicAuth {
|
|||||||
ready(
|
ready(
|
||||||
Authorization::<Basic>::parse(req)
|
Authorization::<Basic>::parse(req)
|
||||||
.map(|auth| BasicAuth(auth.into_scheme()))
|
.map(|auth| BasicAuth(auth.into_scheme()))
|
||||||
.map_err(|_| {
|
.map_err(|err| {
|
||||||
// TODO: debug! the original error
|
log::debug!("`BasicAuth` extract error: {}", err);
|
||||||
|
|
||||||
let challenge = req
|
let challenge = req
|
||||||
.app_data::<Config>()
|
.app_data::<Config>()
|
||||||
.map(|config| config.0.clone())
|
.map(|config| config.0.clone())
|
||||||
// TODO: Add trace! about `Default::default` call
|
.unwrap_or_default();
|
||||||
.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)
|
AuthenticationError::new(challenge)
|
||||||
}),
|
}),
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
//! Extractor for the "Bearer" HTTP Authentication Scheme
|
//! Extractor for the "Bearer" HTTP Authentication Scheme.
|
||||||
|
|
||||||
use std::{borrow::Cow, default::Default};
|
use std::{borrow::Cow, default::Default};
|
||||||
|
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use actix_web::{
|
use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest};
|
||||||
dev::{Payload, ServiceRequest},
|
|
||||||
http::header::Header,
|
|
||||||
FromRequest, HttpRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{config::AuthExtractorConfig, errors::AuthenticationError, AuthExtractor};
|
use super::{config::AuthExtractorConfig, errors::AuthenticationError};
|
||||||
pub use crate::headers::www_authenticate::bearer::Error;
|
pub use crate::headers::www_authenticate::bearer::Error;
|
||||||
use crate::headers::{authorization, www_authenticate::bearer};
|
use crate::headers::{authorization, www_authenticate::bearer};
|
||||||
|
|
||||||
/// [BearerAuth](./struct/BearerAuth.html) extractor configuration.
|
/// [`BearerAuth`] extractor configuration.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Config(bearer::Bearer);
|
pub struct Config(bearer::Bearer);
|
||||||
|
|
||||||
@ -31,7 +27,7 @@ impl Config {
|
|||||||
/// Set challenge `realm` attribute.
|
/// Set challenge `realm` attribute.
|
||||||
///
|
///
|
||||||
/// The "realm" attribute indicates the scope of protection in the manner
|
/// 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).
|
/// described in HTTP/1.1 [RFC 2617](https://tools.ietf.org/html/rfc2617#section-1.2).
|
||||||
pub fn realm<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config {
|
pub fn realm<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config {
|
||||||
self.0.realm = Some(value.into());
|
self.0.realm = Some(value.into());
|
||||||
self
|
self
|
||||||
@ -52,12 +48,9 @@ impl AuthExtractorConfig for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs `fn main` to display complete example.
|
|
||||||
#[allow(clippy::needless_doctest_main)]
|
|
||||||
/// Extractor for HTTP Bearer auth
|
/// Extractor for HTTP Bearer auth
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web_httpauth::extractors::bearer::BearerAuth;
|
/// use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
///
|
///
|
||||||
@ -70,25 +63,22 @@ impl AuthExtractorConfig for Config {
|
|||||||
/// from the [app data] in order to properly form the `WWW-Authenticate`
|
/// from the [app data] in order to properly form the `WWW-Authenticate`
|
||||||
/// response header.
|
/// response header.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::{web, App};
|
/// use actix_web::{web, App};
|
||||||
/// use actix_web_httpauth::extractors::bearer::{BearerAuth, Config};
|
/// use actix_web_httpauth::extractors::bearer::{self, BearerAuth};
|
||||||
///
|
///
|
||||||
/// async fn index(auth: BearerAuth) -> String {
|
/// async fn index(auth: BearerAuth) -> String {
|
||||||
/// format!("Hello, {}!", auth.token())
|
/// format!("Hello, {}!", auth.token())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// App::new()
|
||||||
/// let app = App::new()
|
/// .app_data(
|
||||||
/// .app_data(
|
/// bearer::Config::default()
|
||||||
/// Config::default()
|
/// .realm("Restricted area")
|
||||||
/// .realm("Restricted area")
|
/// .scope("email photo"),
|
||||||
/// .scope("email photo"),
|
/// )
|
||||||
/// )
|
/// .service(web::resource("/index.html").route(web::get().to(index)));
|
||||||
/// .service(web::resource("/index.html").route(web::get().to(index)));
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BearerAuth(authorization::Bearer);
|
pub struct BearerAuth(authorization::Bearer);
|
||||||
@ -120,26 +110,6 @@ impl FromRequest for BearerAuth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Extended error customization for HTTP `Bearer` auth.
|
||||||
impl AuthenticationError<bearer::Bearer> {
|
impl AuthenticationError<bearer::Bearer> {
|
||||||
/// Attach `Error` to the current Authentication error.
|
/// Attach `Error` to the current Authentication error.
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
use super::AuthenticationError;
|
use super::AuthenticationError;
|
||||||
use crate::headers::www_authenticate::Challenge;
|
use crate::headers::www_authenticate::Challenge;
|
||||||
|
|
||||||
/// Trait implemented for types that provides configuration
|
/// Trait implemented for types that provides configuration for the authentication
|
||||||
/// for the authentication [extractors].
|
/// [extractors](super::AuthExtractor).
|
||||||
///
|
|
||||||
/// [extractors]: ./trait.AuthExtractor.html
|
|
||||||
pub trait AuthExtractorConfig {
|
pub trait AuthExtractorConfig {
|
||||||
/// Associated challenge type.
|
/// Associated challenge type.
|
||||||
type Inner: Challenge;
|
type Inner: Challenge;
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
use std::error::Error;
|
use std::{error::Error, fmt};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::{http::StatusCode, HttpResponse, ResponseError};
|
||||||
use actix_web::{HttpResponse, ResponseError};
|
|
||||||
|
|
||||||
use crate::headers::www_authenticate::Challenge;
|
use crate::headers::www_authenticate::{Challenge, WwwAuthenticate};
|
||||||
use crate::headers::www_authenticate::WwwAuthenticate;
|
|
||||||
|
|
||||||
/// Authentication error returned by authentication extractors.
|
/// Authentication error returned by authentication extractors.
|
||||||
///
|
///
|
||||||
/// Different extractors may extend `AuthenticationError` implementation
|
/// Different extractors may extend `AuthenticationError` implementation in order to provide access
|
||||||
/// in order to provide access to inner challenge fields.
|
/// inner challenge fields.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AuthenticationError<C: Challenge> {
|
pub struct AuthenticationError<C: Challenge> {
|
||||||
challenge: C,
|
challenge: C,
|
||||||
@ -35,8 +32,8 @@ impl<C: Challenge> AuthenticationError<C> {
|
|||||||
|
|
||||||
/// Returns mutable reference to the inner status code.
|
/// Returns mutable reference to the inner status code.
|
||||||
///
|
///
|
||||||
/// Can be used to override returned status code, but by default
|
/// Can be used to override returned status code, but by default this lib tries to stick to the
|
||||||
/// this lib tries to stick to the RFC, so it might be unreasonable.
|
/// RFC, so it might be unreasonable.
|
||||||
pub fn status_code_mut(&mut self) -> &mut StatusCode {
|
pub fn status_code_mut(&mut self) -> &mut StatusCode {
|
||||||
&mut self.status_code
|
&mut self.status_code
|
||||||
}
|
}
|
||||||
@ -48,19 +45,18 @@ impl<C: Challenge> fmt::Display for AuthenticationError<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: 'static + Challenge> Error for AuthenticationError<C> {}
|
impl<C: Challenge + 'static> 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
impl<C: Challenge + 'static> ResponseError for AuthenticationError<C> {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
self.status_code
|
self.status_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::build(self.status_code())
|
||||||
|
.insert_header(WwwAuthenticate(self.challenge.clone()))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -72,12 +68,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_status_code_is_preserved_across_error_conversions() {
|
fn test_status_code_is_preserved_across_error_conversions() {
|
||||||
let ae: AuthenticationError<Basic> = AuthenticationError::new(Basic::default());
|
let ae = AuthenticationError::new(Basic::default());
|
||||||
let expected = ae.status_code;
|
let expected = ae.status_code;
|
||||||
|
|
||||||
// Converting the AuthenticationError into a ResponseError should preserve the status code.
|
// Converting the AuthenticationError into a ResponseError should preserve the status code.
|
||||||
let e = Error::from(ae);
|
let err = Error::from(ae);
|
||||||
let re = e.as_response_error();
|
let res_err = err.as_response_error();
|
||||||
assert_eq!(expected, re.status_code());
|
assert_eq!(expected, res_err.status_code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,4 @@
|
|||||||
//! Type-safe authentication information extractors
|
//! Type-safe authentication information extractors.
|
||||||
|
|
||||||
use std::{
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_web::dev::ServiceRequest;
|
|
||||||
use actix_web::Error;
|
|
||||||
use futures_core::ready;
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
|
|
||||||
pub mod basic;
|
pub mod basic;
|
||||||
pub mod bearer;
|
pub mod bearer;
|
||||||
@ -18,86 +7,3 @@ mod errors;
|
|||||||
|
|
||||||
pub use self::config::AuthExtractorConfig;
|
pub use self::config::AuthExtractorConfig;
|
||||||
pub use self::errors::AuthenticationError;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AuthExtractor> AuthExtractor for Option<T> {
|
|
||||||
type Error = T::Error;
|
|
||||||
|
|
||||||
type Future = AuthExtractorOptFut<T::Future>;
|
|
||||||
|
|
||||||
fn from_service_request(req: &ServiceRequest) -> Self::Future {
|
|
||||||
let fut = T::from_service_request(req);
|
|
||||||
AuthExtractorOptFut { fut }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct AuthExtractorOptFut<F> {
|
|
||||||
#[pin]
|
|
||||||
fut: F
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, T, E> Future for AuthExtractorOptFut<F>
|
|
||||||
where
|
|
||||||
F: Future<Output = Result<T, E>>,
|
|
||||||
{
|
|
||||||
type Output = Result<Option<T>, E>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let res = ready!(self.project().fut.poll(cx));
|
|
||||||
Poll::Ready(Ok(res.ok()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AuthExtractor> AuthExtractor for Result<T, T::Error> {
|
|
||||||
type Error = T::Error;
|
|
||||||
|
|
||||||
type Future = AuthExtractorResFut<T::Future>;
|
|
||||||
|
|
||||||
fn from_service_request(req: &ServiceRequest) -> Self::Future {
|
|
||||||
AuthExtractorResFut {
|
|
||||||
fut: T::from_service_request(req),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct AuthExtractorResFut<F> {
|
|
||||||
#[pin]
|
|
||||||
fut: F
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, T, E> Future for AuthExtractorResFut<F>
|
|
||||||
where
|
|
||||||
F: Future<Output = Result<T, E>>,
|
|
||||||
{
|
|
||||||
type Output = Result<F::Output, E>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let res = ready!(self.project().fut.poll(cx));
|
|
||||||
Poll::Ready(Ok(res))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,41 +1,42 @@
|
|||||||
use std::convert::From;
|
use std::{convert::From, error::Error, fmt, str};
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
|
|
||||||
/// Possible errors while parsing `Authorization` header.
|
/// Possible errors while parsing `Authorization` header.
|
||||||
///
|
///
|
||||||
/// Should not be used directly unless you are implementing
|
/// Should not be used directly unless you are implementing your own
|
||||||
/// your own [authentication scheme](./trait.Scheme.html).
|
/// [authentication scheme](super::Scheme).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
/// Header value is malformed
|
/// Header value is malformed.
|
||||||
Invalid,
|
Invalid,
|
||||||
/// Authentication scheme is missing
|
|
||||||
|
/// Authentication scheme is missing.
|
||||||
MissingScheme,
|
MissingScheme,
|
||||||
/// Required authentication field is missing
|
|
||||||
|
/// Required authentication field is missing.
|
||||||
MissingField(&'static str),
|
MissingField(&'static str),
|
||||||
/// Unable to convert header into the str
|
|
||||||
|
/// Unable to convert header into the str.
|
||||||
ToStrError(header::ToStrError),
|
ToStrError(header::ToStrError),
|
||||||
/// Malformed base64 string
|
|
||||||
|
/// Malformed base64 string.
|
||||||
Base64DecodeError(base64::DecodeError),
|
Base64DecodeError(base64::DecodeError),
|
||||||
/// Malformed UTF-8 string
|
|
||||||
|
/// Malformed UTF-8 string.
|
||||||
Utf8Error(str::Utf8Error),
|
Utf8Error(str::Utf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let display = match self {
|
match self {
|
||||||
ParseError::Invalid => "Invalid header value".to_string(),
|
ParseError::Invalid => f.write_str("Invalid header value"),
|
||||||
ParseError::MissingScheme => "Missing authorization scheme".to_string(),
|
ParseError::MissingScheme => f.write_str("Missing authorization scheme"),
|
||||||
ParseError::MissingField(_) => "Missing header field".to_string(),
|
ParseError::MissingField(field) => write!(f, "Missing header field ({})", field),
|
||||||
ParseError::ToStrError(e) => e.to_string(),
|
ParseError::ToStrError(err) => fmt::Display::fmt(err, f),
|
||||||
ParseError::Base64DecodeError(e) => e.to_string(),
|
ParseError::Base64DecodeError(err) => fmt::Display::fmt(err, f),
|
||||||
ParseError::Utf8Error(e) => e.to_string(),
|
ParseError::Utf8Error(err) => fmt::Display::fmt(err, f),
|
||||||
};
|
}
|
||||||
f.write_str(&display)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,25 +46,27 @@ impl Error for ParseError {
|
|||||||
ParseError::Invalid => None,
|
ParseError::Invalid => None,
|
||||||
ParseError::MissingScheme => None,
|
ParseError::MissingScheme => None,
|
||||||
ParseError::MissingField(_) => None,
|
ParseError::MissingField(_) => None,
|
||||||
ParseError::ToStrError(e) => Some(e),
|
ParseError::ToStrError(err) => Some(err),
|
||||||
ParseError::Base64DecodeError(e) => Some(e),
|
ParseError::Base64DecodeError(err) => Some(err),
|
||||||
ParseError::Utf8Error(e) => Some(e),
|
ParseError::Utf8Error(err) => Some(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<header::ToStrError> for ParseError {
|
impl From<header::ToStrError> for ParseError {
|
||||||
fn from(e: header::ToStrError) -> Self {
|
fn from(err: header::ToStrError) -> Self {
|
||||||
ParseError::ToStrError(e)
|
ParseError::ToStrError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<base64::DecodeError> for ParseError {
|
impl From<base64::DecodeError> for ParseError {
|
||||||
fn from(e: base64::DecodeError) -> Self {
|
fn from(err: base64::DecodeError) -> Self {
|
||||||
ParseError::Base64DecodeError(e)
|
ParseError::Base64DecodeError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<str::Utf8Error> for ParseError {
|
impl From<str::Utf8Error> for ParseError {
|
||||||
fn from(e: str::Utf8Error) -> Self {
|
fn from(err: str::Utf8Error) -> Self {
|
||||||
ParseError::Utf8Error(e)
|
ParseError::Utf8Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,25 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use actix_web::error::ParseError;
|
use actix_web::{
|
||||||
use actix_web::http::header::{Header, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION};
|
error::ParseError,
|
||||||
use actix_web::HttpMessage;
|
http::header::{Header, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION},
|
||||||
|
HttpMessage,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::headers::authorization::scheme::Scheme;
|
use crate::headers::authorization::scheme::Scheme;
|
||||||
|
|
||||||
/// `Authorization` header, defined in [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.2)
|
/// `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
|
/// The "Authorization" header field allows a user agent to authenticate itself with an origin
|
||||||
/// itself with an origin server -- usually, but not necessarily, after
|
/// server—usually, but not necessarily, after receiving a 401 (Unauthorized) response. Its value
|
||||||
/// receiving a 401 (Unauthorized) response. Its value consists of
|
/// consists of credentials containing the authentication information of the user agent for the
|
||||||
/// credentials containing the authentication information of the user
|
/// realm of the resource being requested.
|
||||||
/// agent for the realm of the resource being requested.
|
|
||||||
///
|
///
|
||||||
/// `Authorization` header is generic over [authentication
|
/// `Authorization` is generic over an [authentication scheme](Scheme).
|
||||||
/// scheme](./trait.Scheme.html).
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
///
|
||||||
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::http::header::Header;
|
/// # use actix_web::{HttpRequest, Result, http::header::Header};
|
||||||
/// # use actix_web::{HttpRequest, Result};
|
|
||||||
/// # use actix_web_httpauth::headers::authorization::{Authorization, Basic};
|
/// # use actix_web_httpauth::headers::authorization::{Authorization, Basic};
|
||||||
/// fn handler(req: HttpRequest) -> Result<String> {
|
/// fn handler(req: HttpRequest) -> Result<String> {
|
||||||
/// let auth = Authorization::<Basic>::parse(&req)?;
|
/// let auth = Authorization::<Basic>::parse(&req)?;
|
||||||
@ -29,49 +27,40 @@ use crate::headers::authorization::scheme::Scheme;
|
|||||||
/// Ok(format!("Hello, {}!", auth.as_ref().user_id()))
|
/// Ok(format!("Hello, {}!", auth.as_ref().user_id()))
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Authorization<S: Scheme>(S);
|
pub struct Authorization<S: Scheme>(S);
|
||||||
|
|
||||||
impl<S> Authorization<S>
|
impl<S: Scheme> Authorization<S> {
|
||||||
where
|
/// Consumes `Authorization` header and returns inner [`Scheme`] implementation.
|
||||||
S: Scheme,
|
|
||||||
{
|
|
||||||
/// Consumes `Authorization` header and returns inner [`Scheme`]
|
|
||||||
/// implementation.
|
|
||||||
///
|
|
||||||
/// [`Scheme`]: ./trait.Scheme.html
|
|
||||||
pub fn into_scheme(self) -> S {
|
pub fn into_scheme(self) -> S {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<S> for Authorization<S>
|
impl<S: Scheme> From<S> for Authorization<S> {
|
||||||
where
|
|
||||||
S: Scheme,
|
|
||||||
{
|
|
||||||
fn from(scheme: S) -> Authorization<S> {
|
fn from(scheme: S) -> Authorization<S> {
|
||||||
Authorization(scheme)
|
Authorization(scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> AsRef<S> for Authorization<S>
|
impl<S: Scheme> AsRef<S> for Authorization<S> {
|
||||||
where
|
|
||||||
S: Scheme,
|
|
||||||
{
|
|
||||||
fn as_ref(&self) -> &S {
|
fn as_ref(&self) -> &S {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> AsMut<S> for Authorization<S>
|
impl<S: Scheme> AsMut<S> for Authorization<S> {
|
||||||
where
|
|
||||||
S: Scheme,
|
|
||||||
{
|
|
||||||
fn as_mut(&mut self) -> &mut S {
|
fn as_mut(&mut self) -> &mut S {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: Scheme> fmt::Display for Authorization<S> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: Scheme> Header for Authorization<S> {
|
impl<S: Scheme> Header for Authorization<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name() -> HeaderName {
|
fn name() -> HeaderName {
|
||||||
@ -79,7 +68,7 @@ impl<S: Scheme> Header for Authorization<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {
|
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {
|
||||||
let header = msg.headers().get(AUTHORIZATION).ok_or(ParseError::Header)?;
|
let header = msg.headers().get(Self::name()).ok_or(ParseError::Header)?;
|
||||||
let scheme = S::parse(header).map_err(|_| ParseError::Header)?;
|
let scheme = S::parse(header).map_err(|_| ParseError::Header)?;
|
||||||
|
|
||||||
Ok(Authorization(scheme))
|
Ok(Authorization(scheme))
|
||||||
@ -93,9 +82,3 @@ impl<S: Scheme> TryIntoHeaderValue for Authorization<S> {
|
|||||||
self.0.try_into_value()
|
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,4 +1,4 @@
|
|||||||
//! `Authorization` header and various auth schemes
|
//! `Authorization` header and various auth schemes.
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod header;
|
mod header;
|
||||||
@ -6,6 +6,4 @@ mod scheme;
|
|||||||
|
|
||||||
pub use self::errors::ParseError;
|
pub use self::errors::ParseError;
|
||||||
pub use self::header::Authorization;
|
pub use self::header::Authorization;
|
||||||
pub use self::scheme::basic::Basic;
|
pub use self::scheme::{basic::Basic, bearer::Bearer, Scheme};
|
||||||
pub use self::scheme::bearer::Bearer;
|
|
||||||
pub use self::scheme::Scheme;
|
|
||||||
|
@ -8,7 +8,7 @@ use actix_web::{
|
|||||||
use crate::headers::authorization::{errors::ParseError, Scheme};
|
use crate::headers::authorization::{errors::ParseError, Scheme};
|
||||||
|
|
||||||
/// Credentials for `Basic` authentication scheme, defined in [RFC 7617](https://tools.ietf.org/html/rfc7617)
|
/// Credentials for `Basic` authentication scheme, defined in [RFC 7617](https://tools.ietf.org/html/rfc7617)
|
||||||
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Basic {
|
pub struct Basic {
|
||||||
user_id: Cow<'static, str>,
|
user_id: Cow<'static, str>,
|
||||||
password: Option<Cow<'static, str>>,
|
password: Option<Cow<'static, str>>,
|
||||||
@ -18,8 +18,7 @@ impl Basic {
|
|||||||
/// Creates `Basic` credentials with provided `user_id` and optional
|
/// Creates `Basic` credentials with provided `user_id` and optional
|
||||||
/// `password`.
|
/// `password`.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web_httpauth::headers::authorization::Basic;
|
/// # use actix_web_httpauth::headers::authorization::Basic;
|
||||||
/// let credentials = Basic::new("Alladin", Some("open sesame"));
|
/// let credentials = Basic::new("Alladin", Some("open sesame"));
|
||||||
@ -36,13 +35,13 @@ impl Basic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns client's user-ID.
|
/// Returns client's user-ID.
|
||||||
pub fn user_id(&self) -> &Cow<'static, str> {
|
pub fn user_id(&self) -> &str {
|
||||||
&self.user_id
|
self.user_id.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns client's password if provided.
|
/// Returns client's password if provided.
|
||||||
pub fn password(&self) -> Option<&Cow<'static, str>> {
|
pub fn password(&self) -> Option<&str> {
|
||||||
self.password.as_ref()
|
self.password.as_deref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +65,7 @@ impl Scheme for Basic {
|
|||||||
.next()
|
.next()
|
||||||
.ok_or(ParseError::MissingField("user_id"))
|
.ok_or(ParseError::MissingField("user_id"))
|
||||||
.map(|user_id| user_id.to_string().into())?;
|
.map(|user_id| user_id.to_string().into())?;
|
||||||
|
|
||||||
let password = credentials
|
let password = credentials
|
||||||
.next()
|
.next()
|
||||||
.ok_or(ParseError::MissingField("password"))
|
.ok_or(ParseError::MissingField("password"))
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, fmt};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use actix_web::http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue};
|
use actix_web::{
|
||||||
use actix_web::web::{BufMut, BytesMut};
|
http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
|
||||||
|
web::{BufMut, BytesMut},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::headers::authorization::errors::ParseError;
|
use crate::headers::authorization::{errors::ParseError, scheme::Scheme};
|
||||||
use crate::headers::authorization::scheme::Scheme;
|
|
||||||
|
|
||||||
/// Credentials for `Bearer` authentication scheme, defined in [RFC6750](https://tools.ietf.org/html/rfc6750)
|
/// Credentials for `Bearer` authentication scheme, defined in [RFC 6750].
|
||||||
///
|
///
|
||||||
/// Should be used in combination with
|
/// Should be used in combination with [`Authorization`](super::Authorization) header.
|
||||||
/// [`Authorization`](./struct.Authorization.html) header.
|
///
|
||||||
|
/// [RFC 6750]: https://tools.ietf.org/html/rfc6750
|
||||||
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Bearer {
|
pub struct Bearer {
|
||||||
token: Cow<'static, str>,
|
token: Cow<'static, str>,
|
||||||
@ -19,8 +20,7 @@ pub struct Bearer {
|
|||||||
impl Bearer {
|
impl Bearer {
|
||||||
/// Creates new `Bearer` credentials with the token provided.
|
/// Creates new `Bearer` credentials with the token provided.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Example
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web_httpauth::headers::authorization::Bearer;
|
/// # use actix_web_httpauth::headers::authorization::Bearer;
|
||||||
/// let credentials = Bearer::new("mF_9.B5f-4.1JqM");
|
/// let credentials = Bearer::new("mF_9.B5f-4.1JqM");
|
||||||
@ -35,8 +35,8 @@ impl Bearer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets reference to the credentials token.
|
/// Gets reference to the credentials token.
|
||||||
pub fn token(&self) -> &Cow<'static, str> {
|
pub fn token(&self) -> &str {
|
||||||
&self.token
|
self.token.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +48,9 @@ impl Scheme for Bearer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut parts = header.to_str()?.splitn(2, ' ');
|
let mut parts = header.to_str()?.splitn(2, ' ');
|
||||||
|
|
||||||
match parts.next() {
|
match parts.next() {
|
||||||
Some(scheme) if scheme == "Bearer" => (),
|
Some(scheme) if scheme == "Bearer" => {}
|
||||||
_ => return Err(ParseError::MissingScheme),
|
_ => return Err(ParseError::MissingScheme),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,8 @@ pub mod bearer;
|
|||||||
|
|
||||||
use crate::headers::authorization::errors::ParseError;
|
use crate::headers::authorization::errors::ParseError;
|
||||||
|
|
||||||
/// Authentication scheme for [`Authorization`](./struct.Authorization.html)
|
/// Authentication scheme for [`Authorization`](super::Authorization) header.
|
||||||
/// header.
|
|
||||||
pub trait Scheme: TryIntoHeaderValue + Debug + Display + Clone + Send + Sync {
|
pub trait Scheme: TryIntoHeaderValue + Debug + Display + Clone + Send + Sync {
|
||||||
/// Try to parse the authentication scheme from the `Authorization` header.
|
/// Try to parse an authentication scheme from the `Authorization` header.
|
||||||
fn parse(header: &HeaderValue) -> Result<Self, ParseError>;
|
fn parse(header: &HeaderValue) -> Result<Self, ParseError>;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Typed HTTP headers
|
//! Typed HTTP headers.
|
||||||
|
|
||||||
pub mod authorization;
|
pub mod authorization;
|
||||||
pub mod www_authenticate;
|
pub mod www_authenticate;
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
//! Challenge for the "Basic" HTTP Authentication Scheme
|
//! Challenge for the "Basic" HTTP Authentication Scheme.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, fmt, str};
|
||||||
use std::default::Default;
|
|
||||||
use std::fmt;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use actix_web::http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue};
|
use actix_web::{
|
||||||
use actix_web::web::{BufMut, Bytes, BytesMut};
|
http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
|
||||||
|
web::{BufMut, Bytes, BytesMut},
|
||||||
|
};
|
||||||
|
|
||||||
use super::Challenge;
|
use super::Challenge;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
@ -14,8 +13,7 @@ use crate::utils;
|
|||||||
/// Challenge for [`WWW-Authenticate`] header with HTTP Basic auth scheme,
|
/// Challenge for [`WWW-Authenticate`] header with HTTP Basic auth scheme,
|
||||||
/// described in [RFC 7617](https://tools.ietf.org/html/rfc7617)
|
/// described in [RFC 7617](https://tools.ietf.org/html/rfc7617)
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
||||||
/// use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
/// use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
||||||
@ -40,8 +38,7 @@ pub struct Basic {
|
|||||||
impl Basic {
|
impl Basic {
|
||||||
/// Creates new `Basic` challenge with an empty `realm` field.
|
/// Creates new `Basic` challenge with an empty `realm` field.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
/// # use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
||||||
/// let challenge = Basic::new();
|
/// let challenge = Basic::new();
|
||||||
@ -52,7 +49,7 @@ impl Basic {
|
|||||||
|
|
||||||
/// Creates new `Basic` challenge from the provided `realm` field value.
|
/// Creates new `Basic` challenge from the provided `realm` field value.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
/// # use actix_web_httpauth::headers::www_authenticate::basic::Basic;
|
||||||
|
@ -4,15 +4,15 @@ use super::{Bearer, Error};
|
|||||||
|
|
||||||
/// Builder for the [`Bearer`] challenge.
|
/// Builder for the [`Bearer`] challenge.
|
||||||
///
|
///
|
||||||
/// It is up to implementor to fill all required fields,
|
/// It is up to implementor to fill all required fields, neither this `Builder` nor [`Bearer`]
|
||||||
/// neither this `Builder` or [`Bearer`] does not provide any validation.
|
/// provide any validation.
|
||||||
///
|
|
||||||
/// [`Bearer`]: struct.Bearer.html
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct BearerBuilder(Bearer);
|
pub struct BearerBuilder(Bearer);
|
||||||
|
|
||||||
impl BearerBuilder {
|
impl BearerBuilder {
|
||||||
/// Provides the `scope` attribute, as defined in [RFC6749, Section 3.3](https://tools.ietf.org/html/rfc6749#section-3.3)
|
/// Provides the `scope` attribute, as defined in [RFC 6749 §3.3].
|
||||||
|
///
|
||||||
|
/// [RFC 6749 §3.3]: https://tools.ietf.org/html/rfc6749#section-3.3
|
||||||
pub fn scope<T>(mut self, value: T) -> Self
|
pub fn scope<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Cow<'static, str>>,
|
T: Into<Cow<'static, str>>,
|
||||||
@ -21,7 +21,9 @@ impl BearerBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides the `realm` attribute, as defined in [RFC2617](https://tools.ietf.org/html/rfc2617)
|
/// Provides the `realm` attribute, as defined in [RFC 2617].
|
||||||
|
///
|
||||||
|
/// [RFC 2617]: https://tools.ietf.org/html/rfc2617
|
||||||
pub fn realm<T>(mut self, value: T) -> Self
|
pub fn realm<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Cow<'static, str>>,
|
T: Into<Cow<'static, str>>,
|
||||||
@ -30,13 +32,17 @@ impl BearerBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides the `error` attribute, as defined in [RFC6750, Section 3.1](https://tools.ietf.org/html/rfc6750#section-3.1)
|
/// Provides the `error` attribute, as defined in [RFC 6750, Section 3.1].
|
||||||
|
///
|
||||||
|
/// [RFC 6750 §3.1]: https://tools.ietf.org/html/rfc6750#section-3.1
|
||||||
pub fn error(mut self, value: Error) -> Self {
|
pub fn error(mut self, value: Error) -> Self {
|
||||||
self.0.error = Some(value);
|
self.0.error = Some(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides the `error_description` attribute, as defined in [RFC6750, Section 3](https://tools.ietf.org/html/rfc6750#section-3)
|
/// Provides the `error_description` attribute, as defined in [RFC 6750, Section 3].
|
||||||
|
///
|
||||||
|
/// [RFC 6750 §3]: https://tools.ietf.org/html/rfc6750#section-3
|
||||||
pub fn error_description<T>(mut self, value: T) -> Self
|
pub fn error_description<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Cow<'static, str>>,
|
T: Into<Cow<'static, str>>,
|
||||||
@ -45,9 +51,11 @@ impl BearerBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides the `error_uri` attribute, as defined in [RFC6750, Section 3](https://tools.ietf.org/html/rfc6750#section-3)
|
/// Provides the `error_uri` attribute, as defined in [RFC 6750 §3].
|
||||||
///
|
///
|
||||||
/// It is up to implementor to provide properly-formed absolute URI.
|
/// It is up to implementor to provide properly-formed absolute URI.
|
||||||
|
///
|
||||||
|
/// [RFC 6750 §3](https://tools.ietf.org/html/rfc6750#section-3)
|
||||||
pub fn error_uri<T>(mut self, value: T) -> Self
|
pub fn error_uri<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Cow<'static, str>>,
|
T: Into<Cow<'static, str>>,
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, fmt, str};
|
||||||
use std::fmt;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use actix_web::http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue};
|
use actix_web::{
|
||||||
use actix_web::web::{BufMut, Bytes, BytesMut};
|
http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
|
||||||
|
web::{BufMut, Bytes, BytesMut},
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::Challenge;
|
use super::super::Challenge;
|
||||||
use super::{BearerBuilder, Error};
|
use super::{BearerBuilder, Error};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
/// Challenge for [`WWW-Authenticate`] header with HTTP Bearer auth scheme,
|
/// Challenge for [`WWW-Authenticate`] header with HTTP Bearer auth scheme, described in [RFC 6750].
|
||||||
/// described in [RFC 6750](https://tools.ietf.org/html/rfc6750#section-3)
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
///
|
||||||
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
/// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
||||||
/// use actix_web_httpauth::headers::www_authenticate::bearer::{
|
/// use actix_web_httpauth::headers::www_authenticate::bearer::{
|
||||||
@ -36,8 +34,9 @@ use crate::utils;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`WWW-Authenticate`]: ../struct.WwwAuthenticate.html
|
/// [`WWW-Authenticate`]: crate::headers::www_authenticate::WwwAuthenticate
|
||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)]
|
/// [RFC 6750]: https://tools.ietf.org/html/rfc6750#section-3
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Bearer {
|
pub struct Bearer {
|
||||||
pub(crate) scope: Option<Cow<'static, str>>,
|
pub(crate) scope: Option<Cow<'static, str>>,
|
||||||
pub(crate) realm: Option<Cow<'static, str>>,
|
pub(crate) realm: Option<Cow<'static, str>>,
|
||||||
@ -49,8 +48,7 @@ pub struct Bearer {
|
|||||||
impl Bearer {
|
impl Bearer {
|
||||||
/// Creates the builder for `Bearer` challenge.
|
/// Creates the builder for `Bearer` challenge.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web_httpauth::headers::www_authenticate::bearer::{Bearer};
|
/// # use actix_web_httpauth::headers::www_authenticate::bearer::{Bearer};
|
||||||
/// let challenge = Bearer::build()
|
/// let challenge = Bearer::build()
|
||||||
@ -71,10 +69,12 @@ impl Challenge for Bearer {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(0, |desc| desc.len() + 20)
|
.map_or(0, |desc| desc.len() + 20)
|
||||||
+ self.error_uri.as_ref().map_or(0, |url| url.len() + 12);
|
+ self.error_uri.as_ref().map_or(0, |url| url.len() + 12);
|
||||||
|
|
||||||
let capacity = 6
|
let capacity = 6
|
||||||
+ self.realm.as_ref().map_or(0, |realm| realm.len() + 9)
|
+ self.realm.as_ref().map_or(0, |realm| realm.len() + 9)
|
||||||
+ self.scope.as_ref().map_or(0, |scope| scope.len() + 9)
|
+ self.scope.as_ref().map_or(0, |scope| scope.len() + 9)
|
||||||
+ desc_uri_required;
|
+ desc_uri_required;
|
||||||
|
|
||||||
let mut buffer = BytesMut::with_capacity(capacity);
|
let mut buffer = BytesMut::with_capacity(capacity);
|
||||||
buffer.put(&b"Bearer"[..]);
|
buffer.put(&b"Bearer"[..]);
|
||||||
|
|
||||||
@ -94,9 +94,11 @@ impl Challenge for Bearer {
|
|||||||
let error_repr = error.as_str();
|
let error_repr = error.as_str();
|
||||||
let remaining = buffer.remaining_mut();
|
let remaining = buffer.remaining_mut();
|
||||||
let required = desc_uri_required + error_repr.len() + 9; // 9 is for `" error=\"\""`
|
let required = desc_uri_required + error_repr.len() + 9; // 9 is for `" error=\"\""`
|
||||||
|
|
||||||
if remaining < required {
|
if remaining < required {
|
||||||
buffer.reserve(required);
|
buffer.reserve(required);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.put(&b" error=\""[..]);
|
buffer.put(&b" error=\""[..]);
|
||||||
utils::put_quoted(&mut buffer, error_repr);
|
utils::put_quoted(&mut buffer, error_repr);
|
||||||
buffer.put_u8(b'"')
|
buffer.put_u8(b'"')
|
||||||
@ -121,6 +123,7 @@ impl Challenge for Bearer {
|
|||||||
impl fmt::Display for Bearer {
|
impl fmt::Display for Bearer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
let bytes = self.to_bytes();
|
let bytes = self.to_bytes();
|
||||||
|
|
||||||
let repr = str::from_utf8(&bytes)
|
let repr = str::from_utf8(&bytes)
|
||||||
// Should not happen since challenges are crafted manually
|
// Should not happen since challenges are crafted manually
|
||||||
// from `&'static str`'s and Strings
|
// from `&'static str`'s and Strings
|
||||||
|
@ -2,21 +2,20 @@ use std::fmt;
|
|||||||
|
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
|
|
||||||
/// Bearer authorization error types, described in [RFC 6750](https://tools.ietf.org/html/rfc6750#section-3.1)
|
/// Bearer authorization error types, described in [RFC 6750].
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
///
|
||||||
|
/// [RFC 6750]: https://tools.ietf.org/html/rfc6750#section-3.1
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// The request is missing a required parameter, includes an unsupported
|
/// The request is missing a required parameter, includes an unsupported parameter or parameter
|
||||||
/// parameter or parameter value, repeats the same parameter, uses more
|
/// value, repeats the same parameter, uses more than one method for including an access token,
|
||||||
/// than one method for including an access token, or is otherwise
|
/// or is otherwise malformed.
|
||||||
/// malformed.
|
|
||||||
InvalidRequest,
|
InvalidRequest,
|
||||||
|
|
||||||
/// The access token provided is expired, revoked, malformed, or invalid
|
/// The access token provided is expired, revoked, malformed, or invalid for other reasons.
|
||||||
/// for other reasons.
|
|
||||||
InvalidToken,
|
InvalidToken,
|
||||||
|
|
||||||
/// The request requires higher privileges than provided by the access
|
/// The request requires higher privileges than provided by the access token.
|
||||||
/// token.
|
|
||||||
InsufficientScope,
|
InsufficientScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Challenge for the "Bearer" HTTP Authentication Scheme
|
//! Challenge for the "Bearer" HTTP Authentication Scheme.
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
mod challenge;
|
mod challenge;
|
||||||
@ -9,4 +9,19 @@ pub use self::challenge::Bearer;
|
|||||||
pub use self::errors::Error;
|
pub use self::errors::Error;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_bytes() {
|
||||||
|
let b = Bearer::build()
|
||||||
|
.error(Error::InvalidToken)
|
||||||
|
.error_description("Subject 8740827c-2e0a-447b-9716-d73042e4039d not found")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"Bearer error=\"invalid_token\" error_description=\"Subject 8740827c-2e0a-447b-9716-d73042e4039d not found\"",
|
||||||
|
format!("{}", b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_bytes() {
|
|
||||||
let b = Bearer::build()
|
|
||||||
.error(Error::InvalidToken)
|
|
||||||
.error_description("Subject 8740827c-2e0a-447b-9716-d73042e4039d not found")
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
"Bearer error=\"invalid_token\" error_description=\"Subject 8740827c-2e0a-447b-9716-d73042e4039d not found\"",
|
|
||||||
format!("{}", b)
|
|
||||||
);
|
|
||||||
}
|
|
@ -6,15 +6,17 @@ use actix_web::{
|
|||||||
|
|
||||||
use super::Challenge;
|
use super::Challenge;
|
||||||
|
|
||||||
/// `WWW-Authenticate` header, described in [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.1)
|
/// `WWW-Authenticate` header, described in [RFC 7235].
|
||||||
///
|
///
|
||||||
/// This header is generic over [Challenge](./trait.Challenge.html) trait,
|
/// This header is generic over the [`Challenge`] trait, see [`Basic`](super::basic::Basic) and
|
||||||
/// see [Basic](./basic/struct.Basic.html) and
|
/// [`Bearer`](super::bearer::Bearer) challenges for details.
|
||||||
/// [Bearer](./bearer/struct.Bearer.html) challenges for details.
|
///
|
||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)]
|
/// [RFC 7235]: https://tools.ietf.org/html/rfc7235#section-4.1
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct WwwAuthenticate<C: Challenge>(pub C);
|
pub struct WwwAuthenticate<C: Challenge>(pub C);
|
||||||
|
|
||||||
impl<C: Challenge> Header for WwwAuthenticate<C> {
|
impl<C: Challenge> Header for WwwAuthenticate<C> {
|
||||||
|
#[inline]
|
||||||
fn name() -> HeaderName {
|
fn name() -> HeaderName {
|
||||||
WWW_AUTHENTICATE
|
WWW_AUTHENTICATE
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
//! `WWW-Authenticate` header and various auth challenges
|
//! `WWW-Authenticate` header and various auth challenges.
|
||||||
|
|
||||||
mod challenge;
|
mod challenge;
|
||||||
mod header;
|
mod header;
|
||||||
|
|
||||||
pub use self::challenge::basic;
|
pub use self::challenge::{basic, bearer, Challenge};
|
||||||
pub use self::challenge::bearer;
|
|
||||||
pub use self::challenge::Challenge;
|
|
||||||
pub use self::header::WwwAuthenticate;
|
pub use self::header::WwwAuthenticate;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! HTTP authentication schemes for [actix-web](https://actix.rs).
|
//! HTTP authentication schemes for [Actix Web](https://actix.rs).
|
||||||
//!
|
//!
|
||||||
//! Provides:
|
//! Provides:
|
||||||
//! - Typed [Authorization] and [WWW-Authenticate] headers
|
//! - Typed [Authorization] and [WWW-Authenticate] headers
|
||||||
@ -6,14 +6,13 @@
|
|||||||
//! - [Middleware] for easier authorization checking
|
//! - [Middleware] for easier authorization checking
|
||||||
//!
|
//!
|
||||||
//! ## Supported schemes
|
//! ## Supported schemes
|
||||||
|
//! - `Bearer` as defined in [RFC 6750](https://tools.ietf.org/html/rfc6750).
|
||||||
|
//! - `Basic` as defined in [RFC 7617](https://tools.ietf.org/html/rfc7617).
|
||||||
//!
|
//!
|
||||||
//! - `Basic`, as defined in [RFC7617](https://tools.ietf.org/html/rfc7617)
|
//! [Authorization]: `self::headers::authorization::Authorization`
|
||||||
//! - `Bearer`, as defined in [RFC6750](https://tools.ietf.org/html/rfc6750)
|
//! [WWW-Authenticate]: `self::headers::www_authenticate::WwwAuthenticate`
|
||||||
//!
|
//! [Extractors]: https://actix.rs/docs/extractors
|
||||||
//! [Authorization]: `crate::headers::authorization::Authorization`
|
//! [Middleware]: self::middleware
|
||||||
//! [WWW-Authenticate]: `crate::headers::www_authenticate::WwwAuthenticate`
|
|
||||||
//! [Extractors]: https://actix.rs/docs/extractors/
|
|
||||||
//! [Middleware]: ./middleware
|
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||||
|
@ -12,12 +12,12 @@ use std::{
|
|||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{EitherBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
Error,
|
Error, FromRequest,
|
||||||
};
|
};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use futures_util::future::{self, FutureExt as _, LocalBoxFuture, TryFutureExt as _};
|
use futures_util::future::{self, FutureExt as _, LocalBoxFuture, TryFutureExt as _};
|
||||||
|
|
||||||
use crate::extractors::{basic, bearer, AuthExtractor};
|
use crate::extractors::{basic, bearer};
|
||||||
|
|
||||||
/// Middleware for checking HTTP authentication.
|
/// Middleware for checking HTTP authentication.
|
||||||
///
|
///
|
||||||
@ -29,7 +29,7 @@ use crate::extractors::{basic, bearer, AuthExtractor};
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct HttpAuthentication<T, F>
|
pub struct HttpAuthentication<T, F>
|
||||||
where
|
where
|
||||||
T: AuthExtractor,
|
T: FromRequest,
|
||||||
{
|
{
|
||||||
process_fn: Arc<F>,
|
process_fn: Arc<F>,
|
||||||
_extractor: PhantomData<T>,
|
_extractor: PhantomData<T>,
|
||||||
@ -37,7 +37,7 @@ where
|
|||||||
|
|
||||||
impl<T, F, O> HttpAuthentication<T, F>
|
impl<T, F, O> HttpAuthentication<T, F>
|
||||||
where
|
where
|
||||||
T: AuthExtractor,
|
T: FromRequest,
|
||||||
F: Fn(ServiceRequest, T) -> O,
|
F: Fn(ServiceRequest, T) -> O,
|
||||||
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>,
|
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>,
|
||||||
{
|
{
|
||||||
@ -58,12 +58,10 @@ where
|
|||||||
{
|
{
|
||||||
/// Construct `HttpAuthentication` middleware for the HTTP "Basic" authentication scheme.
|
/// Construct `HttpAuthentication` middleware for the HTTP "Basic" authentication scheme.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::Error;
|
/// # use actix_web::{Error, dev::ServiceRequest};
|
||||||
/// # use actix_web::dev::ServiceRequest;
|
/// # use actix_web_httpauth::{extractors::basic::BasicAuth, middleware::HttpAuthentication};
|
||||||
/// # 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
|
/// // 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
|
/// // anything that implements `IntoFuture` trait, it can be extended to query database or to
|
||||||
/// // do something else in a async manner.
|
/// // do something else in a async manner.
|
||||||
@ -89,20 +87,23 @@ where
|
|||||||
{
|
{
|
||||||
/// Construct `HttpAuthentication` middleware for the HTTP "Bearer" authentication scheme.
|
/// Construct `HttpAuthentication` middleware for the HTTP "Bearer" authentication scheme.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::Error;
|
/// # use actix_web::{Error, dev::ServiceRequest};
|
||||||
/// # use actix_web::dev::ServiceRequest;
|
/// # use actix_web_httpauth::{
|
||||||
/// # use actix_web_httpauth::middleware::HttpAuthentication;
|
/// # extractors::{AuthenticationError, AuthExtractorConfig, bearer::{self, BearerAuth}},
|
||||||
/// # use actix_web_httpauth::extractors::bearer::{Config, BearerAuth};
|
/// # middleware::HttpAuthentication,
|
||||||
/// # use actix_web_httpauth::extractors::{AuthenticationError, AuthExtractorConfig};
|
/// # };
|
||||||
/// async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, (Error, ServiceRequest)> {
|
/// async fn validator(
|
||||||
|
/// req: ServiceRequest,
|
||||||
|
/// credentials: BearerAuth
|
||||||
|
/// ) -> Result<ServiceRequest, (Error, ServiceRequest)> {
|
||||||
/// if credentials.token() == "mF_9.B5f-4.1JqM" {
|
/// if credentials.token() == "mF_9.B5f-4.1JqM" {
|
||||||
/// Ok(req)
|
/// Ok(req)
|
||||||
/// } else {
|
/// } else {
|
||||||
/// let config = req.app_data::<Config>()
|
/// let config = req.app_data::<bearer::Config>()
|
||||||
/// .map(|data| data.clone())
|
/// .cloned()
|
||||||
/// .unwrap_or_else(Default::default)
|
/// .unwrap_or_default()
|
||||||
/// .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13");
|
/// .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13");
|
||||||
///
|
///
|
||||||
/// Err((AuthenticationError::from(config).into(), req))
|
/// Err((AuthenticationError::from(config).into(), req))
|
||||||
@ -122,7 +123,7 @@ where
|
|||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
F: Fn(ServiceRequest, T) -> O + 'static,
|
F: Fn(ServiceRequest, T) -> O + 'static,
|
||||||
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>> + 'static,
|
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>> + 'static,
|
||||||
T: AuthExtractor + 'static,
|
T: FromRequest + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse<EitherBody<B>>;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
@ -143,7 +144,7 @@ where
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct AuthenticationMiddleware<S, F, T>
|
pub struct AuthenticationMiddleware<S, F, T>
|
||||||
where
|
where
|
||||||
T: AuthExtractor,
|
T: FromRequest,
|
||||||
{
|
{
|
||||||
service: Rc<S>,
|
service: Rc<S>,
|
||||||
process_fn: Arc<F>,
|
process_fn: Arc<F>,
|
||||||
@ -156,18 +157,17 @@ where
|
|||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
F: Fn(ServiceRequest, T) -> O + 'static,
|
F: Fn(ServiceRequest, T) -> O + 'static,
|
||||||
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>> + 'static,
|
O: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>> + 'static,
|
||||||
T: AuthExtractor + 'static,
|
T: FromRequest + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse<EitherBody<B>>;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_web::dev::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let process_fn = Arc::clone(&self.process_fn);
|
let process_fn = Arc::clone(&self.process_fn);
|
||||||
|
|
||||||
let service = Rc::clone(&self.service);
|
let service = Rc::clone(&self.service);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
@ -193,7 +193,7 @@ where
|
|||||||
|
|
||||||
struct Extract<T> {
|
struct Extract<T> {
|
||||||
req: Option<ServiceRequest>,
|
req: Option<ServiceRequest>,
|
||||||
f: Option<LocalBoxFuture<'static, Result<T, Error>>>,
|
fut: Option<LocalBoxFuture<'static, Result<T, Error>>>,
|
||||||
_extractor: PhantomData<fn() -> T>,
|
_extractor: PhantomData<fn() -> T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ impl<T> Extract<T> {
|
|||||||
pub fn new(req: ServiceRequest) -> Self {
|
pub fn new(req: ServiceRequest) -> Self {
|
||||||
Extract {
|
Extract {
|
||||||
req: Some(req),
|
req: Some(req),
|
||||||
f: None,
|
fut: None,
|
||||||
_extractor: PhantomData,
|
_extractor: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,25 +209,25 @@ impl<T> Extract<T> {
|
|||||||
|
|
||||||
impl<T> Future for Extract<T>
|
impl<T> Future for Extract<T>
|
||||||
where
|
where
|
||||||
T: AuthExtractor,
|
T: FromRequest,
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
T::Error: 'static,
|
T::Error: 'static,
|
||||||
{
|
{
|
||||||
type Output = Result<(ServiceRequest, T), (Error, ServiceRequest)>;
|
type Output = Result<(ServiceRequest, T), (Error, ServiceRequest)>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
if self.f.is_none() {
|
if self.fut.is_none() {
|
||||||
let req = self.req.as_ref().expect("Extract future was polled twice!");
|
let req = self.req.as_mut().expect("Extract future was polled twice!");
|
||||||
let f = T::from_service_request(req).map_err(Into::into);
|
let fut = req.extract::<T>().map_err(Into::into);
|
||||||
self.f = Some(f.boxed_local());
|
self.fut = Some(fut.boxed_local());
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = self
|
let fut = self
|
||||||
.f
|
.fut
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("Extraction future should be initialized at this point");
|
.expect("Extraction future should be initialized at this point");
|
||||||
|
|
||||||
let credentials = ready!(f.as_mut().poll(ctx)).map_err(|err| {
|
let credentials = ready!(fut.as_mut().poll(ctx)).map_err(|err| {
|
||||||
(
|
(
|
||||||
err,
|
err,
|
||||||
// returning request allows a proper error response to be created
|
// returning request allows a proper error response to be created
|
||||||
@ -242,15 +242,17 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_service::{into_service, Service};
|
use actix_service::into_service;
|
||||||
use actix_web::error::ErrorForbidden;
|
use actix_web::{
|
||||||
use actix_web::http::StatusCode;
|
dev::Service,
|
||||||
use actix_web::test::TestRequest;
|
error::{self, ErrorForbidden},
|
||||||
use actix_web::{error, web, App, HttpResponse};
|
http::StatusCode,
|
||||||
|
test::TestRequest,
|
||||||
|
web, App, HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::extractors::basic::BasicAuth;
|
use crate::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
||||||
use crate::extractors::bearer::BearerAuth;
|
|
||||||
|
|
||||||
/// This is a test for https://github.com/actix/actix-extras/issues/10
|
/// This is a test for https://github.com/actix/actix-extras/issues/10
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
@ -343,7 +345,7 @@ mod tests {
|
|||||||
Ok::<ServiceResponse, _>(req.into_response(HttpResponse::Ok().finish()))
|
Ok::<ServiceResponse, _>(req.into_response(HttpResponse::Ok().finish()))
|
||||||
})),
|
})),
|
||||||
process_fn: Arc::new(
|
process_fn: Arc::new(
|
||||||
|req, auth: Result<BearerAuth, <BearerAuth as AuthExtractor>::Error>| {
|
|req, auth: Result<BearerAuth, <BearerAuth as FromRequest>::Error>| {
|
||||||
assert!(auth.is_err());
|
assert!(auth.is_err());
|
||||||
async { Ok(req) }
|
async { Ok(req) }
|
||||||
},
|
},
|
||||||
|
@ -27,12 +27,13 @@ impl<'a> Iterator for Quoted<'a> {
|
|||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::YieldStr => match self.inner.next() {
|
State::YieldStr => match self.inner.next() {
|
||||||
Some(s) => {
|
Some(val) => {
|
||||||
self.state = State::YieldQuote;
|
self.state = State::YieldQuote;
|
||||||
Some(s)
|
Some(val)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
State::YieldQuote => match self.inner.peek() {
|
State::YieldQuote => match self.inner.peek() {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
self.state = State::YieldStr;
|
self.state = State::YieldStr;
|
||||||
@ -44,9 +45,9 @@ impl<'a> Iterator for Quoted<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to quote the quotes in the passed `value`
|
/// Escapes the quotes in `val`.
|
||||||
pub fn put_quoted(buf: &mut BytesMut, value: &str) {
|
pub fn put_quoted(buf: &mut BytesMut, val: &str) {
|
||||||
for part in Quoted::new(value) {
|
for part in Quoted::new(val) {
|
||||||
buf.extend_from_slice(part.as_bytes());
|
buf.extend_from_slice(part.as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user