Newer
Older
use crate::{
model::{AdminAuthCredentials, Administrator},
AppState,
};
use actix_web::{cookie::SameSite, http::Cookie, web::Form, HttpMessage, HttpRequest};
/// Returns a Secure actix_web::http::Cookie.
pub fn get_auth_cookie(name: &'static str, value: String) -> Cookie<'static> {
Cookie::build(name, value)
.max_age(time::Duration::days(7))
/// This is not an actual middleware as it is meant to be executed in the endpoint service to perform a simple verification.
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
/// It must be registered in the actix App instance with app_data.
/// ```
/// App::new()
/// .app_data(Data::new(AuthenticatedAdminMiddleware::new("some-auth-cookie-name")))
/// ```
/// If a service need to perform an authentication before doing anything, this "pseudo-middleware" should be run before anything else in the function.
/// Example:
/// ```
/// #[post("/some-url")]
/// pub async fn some_service(
/// app_state: Data<AppState>,
/// middleware: Data<AuthenticatedAdminMiddleware<'_>>,
/// req: HttpRequest,
/// ) -> impl Responder {
/// if middleware.exec(&app_state, &req, None).await.is_err() {
/// return HttpResponse::Unauthorized().finish();
/// }
/// ... Authenticated action ....
/// }
/// ```
#[derive(Debug, Clone)]
pub struct AuthenticatedAdminMiddleware<'a> {
/// The name of the authentication cookie
pub cookie_name: &'a str,
}
impl<'a> AuthenticatedAdminMiddleware<'a> {
pub fn new(cookie_name: &'a str) -> Self {
AuthenticatedAdminMiddleware { cookie_name }
}
/// Performs Administrator authentication from form data with username and password
/// Returns an authentication Cookie instance if the authentication succeeds, or an error.
async fn try_auth_from_form_data(
&self,
app_state: &AppState,
form_data: Form<AdminAuthCredentials>,
) -> Result<Cookie<'static>, ()> {
match Administrator::authenticated(app_state, form_data.into_inner()).await {
Ok(ref mut admin) => {
let auth_token = app_state.encryption.random_ascii_lc_string(256);
admin.auth_token = Some(app_state.encryption.encrypt(&auth_token));
if admin
.save(&app_state.db, Some(doc!("_id": admin.id().unwrap())))
.await
.is_err()
{
println!("Failed to update admin auth_token");
return Err(());
}
let cookie = get_auth_cookie("kuadrado-admin-auth", auth_token.to_owned());
return Ok(cookie);
}
Err(_) => return Err(()),
}
}
/// Performs Administrator authentication from the authentication cookie value
async fn try_auth_from_auth_cookie(
&self,
app_state: &AppState,
cookie: &Cookie<'static>,
) -> Result<Cookie<'static>, ()> {
match Administrator::authenticated_with_cookie(app_state, &cookie).await {
Ok(_) => return Ok(cookie.clone()),
Err(_) => return Err(()),
}
}
/// The function that must be called in order to execute the verification.
/// Example :
/// ```
/// #[post("/some-url")]
/// pub async fn some_service(
/// app_state: Data<AppState>,
/// middleware: Data<AuthenticatedAdminMiddleware<'_>>,
/// req: HttpRequest,
/// ) -> impl Responder {
/// if middleware.exec(&app_state, &req, None).await.is_err() {
/// return HttpResponse::Unauthorized().finish();
/// }
/// ... Authenticated actions ....
/// }
/// ```
pub async fn exec(
&self,
app_state: &AppState,
req: &HttpRequest,
auth_form_data: Option<Form<AdminAuthCredentials>>,
) -> Result<Cookie<'static>, ()> {
let auth_cookie = req.cookie(self.cookie_name);
if let Some(form_data) = auth_form_data {
return self.try_auth_from_form_data(app_state, form_data).await;
} else if let Some(cookie) = auth_cookie {
return self.try_auth_from_auth_cookie(app_state, &cookie).await;
} else {
return Err(());
}
}
}