2021-03-23 05:05:03 +00:00
|
|
|
//! Opinionated request identity service for Actix Web apps.
|
2019-03-09 18:04:40 -08:00
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! [`IdentityService`] middleware can be used with different policies types to store
|
|
|
|
//! identity information.
|
2019-03-09 18:04:40 -08:00
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! A cookie based policy is provided. [`CookieIdentityPolicy`] uses cookies as identity storage.
|
2019-03-09 18:04:40 -08:00
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! To access current request identity, use the [`Identity`] extractor.
|
2019-03-09 18:04:40 -08:00
|
|
|
//!
|
2022-03-01 04:23:51 +00:00
|
|
|
//! ```
|
2019-03-09 18:04:40 -08:00
|
|
|
//! use actix_web::*;
|
2019-06-12 15:52:48 +06:00
|
|
|
//! use actix_identity::{Identity, CookieIdentityPolicy, IdentityService};
|
2019-03-09 18:04:40 -08:00
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! #[get("/")]
|
2019-11-22 11:49:35 +06:00
|
|
|
//! async fn index(id: Identity) -> String {
|
2019-03-09 18:04:40 -08:00
|
|
|
//! // access request identity
|
2019-03-09 20:40:09 -08:00
|
|
|
//! if let Some(id) = id.identity() {
|
|
|
|
//! format!("Welcome! {}", id)
|
2019-03-09 18:04:40 -08:00
|
|
|
//! } else {
|
2019-03-09 20:40:09 -08:00
|
|
|
//! "Welcome Anonymous!".to_owned()
|
2019-03-09 18:04:40 -08:00
|
|
|
//! }
|
|
|
|
//! }
|
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! #[post("/login")]
|
2019-11-22 11:49:35 +06:00
|
|
|
//! async fn login(id: Identity) -> HttpResponse {
|
2022-03-01 04:23:51 +00:00
|
|
|
//! // remember identity
|
|
|
|
//! id.remember("User1".to_owned());
|
2019-03-09 18:04:40 -08:00
|
|
|
//! HttpResponse::Ok().finish()
|
|
|
|
//! }
|
|
|
|
//!
|
2021-03-23 05:05:03 +00:00
|
|
|
//! #[post("/logout")]
|
2019-11-22 11:49:35 +06:00
|
|
|
//! async fn logout(id: Identity) -> HttpResponse {
|
2022-03-01 04:23:51 +00:00
|
|
|
//! // remove identity
|
|
|
|
//! id.forget();
|
2019-03-09 18:04:40 -08:00
|
|
|
//! HttpResponse::Ok().finish()
|
|
|
|
//! }
|
|
|
|
//!
|
2022-03-01 04:23:51 +00:00
|
|
|
//! HttpServer::new(move || {
|
|
|
|
//! // create cookie identity backend (inside closure, since policy is not Clone)
|
|
|
|
//! let policy = CookieIdentityPolicy::new(&[0; 32])
|
|
|
|
//! .name("auth-cookie")
|
|
|
|
//! .secure(false);
|
|
|
|
//!
|
|
|
|
//! App::new()
|
|
|
|
//! // wrap policy into middleware identity middleware
|
|
|
|
//! .wrap(IdentityService::new(policy))
|
|
|
|
//! .service(services![index, login, logout])
|
|
|
|
//! })
|
|
|
|
//! # ;
|
2019-03-09 18:04:40 -08:00
|
|
|
//! ```
|
2020-07-06 15:47:22 +09:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
2021-12-08 06:11:13 +00:00
|
|
|
#![warn(future_incompatible)]
|
2020-07-06 15:47:22 +09:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
use std::future::Future;
|
2019-03-09 18:04:40 -08:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
use actix_web::{
|
|
|
|
dev::{ServiceRequest, ServiceResponse},
|
|
|
|
Error, HttpMessage, Result,
|
|
|
|
};
|
2019-03-09 18:04:40 -08:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
mod cookie;
|
|
|
|
mod identity;
|
|
|
|
mod middleware;
|
2019-03-09 18:04:40 -08:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
pub use self::cookie::CookieIdentityPolicy;
|
|
|
|
pub use self::identity::Identity;
|
|
|
|
pub use self::middleware::IdentityService;
|
2019-03-09 20:40:09 -08:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
/// Identity policy.
|
2019-03-09 20:40:09 -08:00
|
|
|
pub trait IdentityPolicy: Sized + 'static {
|
|
|
|
/// The return type of the middleware
|
2019-11-21 10:31:52 +06:00
|
|
|
type Future: Future<Output = Result<Option<String>, Error>>;
|
2019-03-09 18:04:40 -08:00
|
|
|
|
|
|
|
/// The return type of the middleware
|
2019-11-21 10:31:52 +06:00
|
|
|
type ResponseFuture: Future<Output = Result<(), Error>>;
|
2019-03-09 18:04:40 -08:00
|
|
|
|
|
|
|
/// Parse the session from request and load data from a service identity.
|
2022-03-15 18:04:13 +00:00
|
|
|
#[allow(clippy::wrong_self_convention)]
|
2021-03-23 05:05:03 +00:00
|
|
|
fn from_request(&self, req: &mut ServiceRequest) -> Self::Future;
|
2019-03-09 20:40:09 -08:00
|
|
|
|
|
|
|
/// Write changes to response
|
|
|
|
fn to_response<B>(
|
|
|
|
&self,
|
|
|
|
identity: Option<String>,
|
|
|
|
changed: bool,
|
|
|
|
response: &mut ServiceResponse<B>,
|
|
|
|
) -> Self::ResponseFuture;
|
2019-03-09 18:04:40 -08:00
|
|
|
}
|
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
/// Helper trait that allows to get Identity.
|
2019-03-09 18:04:40 -08:00
|
|
|
///
|
2021-03-23 05:05:03 +00:00
|
|
|
/// 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>;
|
2020-01-10 11:26:54 +06:00
|
|
|
}
|
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
impl<T> RequestIdentity for T
|
2019-03-09 20:40:09 -08:00
|
|
|
where
|
2021-03-23 05:05:03 +00:00
|
|
|
T: HttpMessage,
|
2019-03-09 20:40:09 -08:00
|
|
|
{
|
2021-03-23 05:05:03 +00:00
|
|
|
fn get_identity(&self) -> Option<String> {
|
|
|
|
Identity::get_identity(&self.extensions())
|
2019-03-09 18:04:40 -08:00
|
|
|
}
|
|
|
|
}
|
2019-03-10 17:10:41 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-03-23 05:05:03 +00:00
|
|
|
use std::time::SystemTime;
|
2019-04-24 12:49:56 -07:00
|
|
|
|
2021-12-11 16:05:21 +00:00
|
|
|
use actix_web::{
|
|
|
|
body::{BoxBody, EitherBody},
|
|
|
|
dev::ServiceResponse,
|
|
|
|
test, web, App, Error,
|
|
|
|
};
|
2019-11-26 11:25:50 +06:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
use super::*;
|
2020-09-17 18:10:27 +01:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
pub(crate) const COOKIE_KEY_MASTER: [u8; 32] = [0; 32];
|
|
|
|
pub(crate) const COOKIE_NAME: &str = "actix_auth";
|
|
|
|
pub(crate) const COOKIE_LOGIN: &str = "test";
|
2020-09-17 18:10:27 +01:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
#[allow(clippy::enum_variant_names)]
|
|
|
|
pub(crate) enum LoginTimestampCheck {
|
|
|
|
NoTimestamp,
|
|
|
|
NewTimestamp,
|
|
|
|
OldTimestamp(SystemTime),
|
2020-09-17 18:10:27 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
#[allow(clippy::enum_variant_names)]
|
|
|
|
pub(crate) enum VisitTimeStampCheck {
|
|
|
|
NoTimestamp,
|
|
|
|
NewTimestamp,
|
2019-04-20 04:54:44 +08:00
|
|
|
}
|
2019-04-24 12:49:56 -07:00
|
|
|
|
2021-03-23 05:05:03 +00:00
|
|
|
pub(crate) async fn create_identity_server<
|
2019-04-24 15:29:15 -07:00
|
|
|
F: Fn(CookieIdentityPolicy) -> CookieIdentityPolicy + Sync + Send + Clone + 'static,
|
|
|
|
>(
|
|
|
|
f: F,
|
|
|
|
) -> impl actix_service::Service<
|
2021-03-21 23:50:26 +01:00
|
|
|
actix_http::Request,
|
2021-12-11 16:05:21 +00:00
|
|
|
Response = ServiceResponse<EitherBody<BoxBody>>,
|
2019-06-12 15:52:48 +06:00
|
|
|
Error = Error,
|
2019-04-24 15:29:15 -07:00
|
|
|
> {
|
2019-04-24 12:49:56 -07:00
|
|
|
test::init_service(
|
|
|
|
App::new()
|
2019-04-24 15:29:15 -07:00
|
|
|
.wrap(IdentityService::new(f(CookieIdentityPolicy::new(
|
|
|
|
&COOKIE_KEY_MASTER,
|
|
|
|
)
|
|
|
|
.secure(false)
|
|
|
|
.name(COOKIE_NAME))))
|
2020-07-06 15:47:22 +09:00
|
|
|
.service(web::resource("/").to(|id: Identity| async move {
|
|
|
|
let identity = id.identity();
|
|
|
|
if identity.is_none() {
|
|
|
|
id.remember(COOKIE_LOGIN.to_string())
|
2019-04-24 12:49:56 -07:00
|
|
|
}
|
2020-07-06 15:47:22 +09:00
|
|
|
web::Json(identity)
|
2019-04-24 15:29:15 -07:00
|
|
|
})),
|
2019-04-24 12:49:56 -07:00
|
|
|
)
|
2019-11-21 10:31:52 +06:00
|
|
|
.await
|
2019-04-24 12:49:56 -07:00
|
|
|
}
|
2019-03-10 17:10:41 -07:00
|
|
|
}
|