use crate::AppState; use actix_web::http::Cookie; use serde::{Deserialize, Serialize}; use wither::{ bson::{doc, oid::ObjectId}, prelude::Model, }; #[derive(Debug, Serialize, Deserialize)] /// The data type that must sent by form data POST to authenticate an administrator. pub struct AdminAuthCredentials { pub username: String, pub password: String, } #[derive(Debug, Deserialize, Serialize, Model)] #[model(index( keys = r#"doc!{"email": 1, "username": 1}"#, options = r#"doc!{"unique": true}"# ))] /// An administrator is a user with registered authentication credentials access right to the admin-panel and has ability to perform admin actions such as gam review, moderation, etc. pub struct Administrator { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option<ObjectId>, pub username: String, pub password_hash: String, pub auth_token: Option<String>, } impl Administrator { /// Creates an administrator with values for username and password. /// The auth_token fields remains None as it must be created if the user authenticates itself with the provided credentials /// The password is stored as password_hash, it is encrypted with the AppState::Encryption. pub fn from_values(app_state: &AppState, username: String, password: String) -> Self { Administrator { id: None, password_hash: app_state.encryption.encrypt(&password), username, auth_token: None, } } /// Performs authentication with form data <username, password>. /// Returns a Result with either an authenticated Administrator instance or an error. pub async fn authenticated( app_state: &AppState, credentials: AdminAuthCredentials, ) -> Result<Self, ()> { let filter_doc = doc! { "password_hash": app_state.encryption.encrypt(&credentials.password), "username": credentials.username }; match Administrator::find_one(&app_state.db, filter_doc, None).await { Ok(user_option) => match user_option { Some(admin) => Ok(admin), None => Err(()), }, Err(_) => Err(()), } } /// Performs authenticattion with auth cookie. The cookie value must match the Administrator auth_token value. /// Returns a result with either the authenticated admin, or an empty Err. pub async fn authenticated_with_cookie( app_state: &AppState, auth_cookie: &Cookie<'_>, ) -> Result<Self, ()> { let cookie_value = auth_cookie.value().to_string(); let filter_doc = doc! { "auth_token": app_state.encryption.encrypt(&cookie_value), }; match Administrator::find_one(&app_state.db, filter_doc, None).await { Ok(user_option) => match user_option { Some(admin) => Ok(admin), None => Err(()), }, Err(_) => Err(()), } } }