157 lines
3.9 KiB
Rust
157 lines
3.9 KiB
Rust
use axum::{http::HeaderMap, Json};
|
|
use futures::Future;
|
|
use result_extended::{error, ok, return_error, ResultExt};
|
|
use serde::{Deserialize, Serialize};
|
|
use typeshare::typeshare;
|
|
|
|
use crate::{
|
|
core::{AuthResponse, Core},
|
|
database::{SessionId, UserId},
|
|
types::{AppError, FatalError, User},
|
|
};
|
|
|
|
use super::UserOverview;
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
#[typeshare]
|
|
pub struct AuthRequest {
|
|
pub username: String,
|
|
pub password: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[typeshare]
|
|
pub struct CreateUserRequest {
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[typeshare]
|
|
pub struct SetPasswordRequest {
|
|
pub password_1: String,
|
|
pub password_2: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[typeshare]
|
|
pub struct SetAdminPasswordRequest {
|
|
pub password: String,
|
|
}
|
|
|
|
async fn check_session(
|
|
core: &Core,
|
|
headers: HeaderMap,
|
|
) -> ResultExt<Option<User>, AppError, FatalError> {
|
|
match headers.get("Authorization") {
|
|
Some(token) => {
|
|
println!("check_session: {:?}", token);
|
|
match token
|
|
.to_str()
|
|
.unwrap()
|
|
.split(" ")
|
|
.collect::<Vec<&str>>()
|
|
.as_slice()
|
|
{
|
|
[_schema, token] => core.session(&SessionId::from(token.to_owned())).await,
|
|
_ => error(AppError::BadRequest),
|
|
}
|
|
}
|
|
None => ok(None),
|
|
}
|
|
}
|
|
|
|
pub async fn auth_required<F, A, Fut>(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
f: F,
|
|
) -> ResultExt<A, AppError, FatalError>
|
|
where
|
|
F: FnOnce(User) -> Fut,
|
|
Fut: Future<Output = ResultExt<A, AppError, FatalError>>,
|
|
{
|
|
match return_error!(check_session(&core, headers).await) {
|
|
Some(user) => f(user).await,
|
|
None => error(AppError::AuthFailed),
|
|
}
|
|
}
|
|
|
|
pub async fn admin_required<F, A, Fut>(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
f: F,
|
|
) -> ResultExt<A, AppError, FatalError>
|
|
where
|
|
F: FnOnce(User) -> Fut,
|
|
Fut: Future<Output = ResultExt<A, AppError, FatalError>>,
|
|
{
|
|
match return_error!(check_session(&core, headers).await) {
|
|
Some(user) => {
|
|
if user.admin {
|
|
f(user).await
|
|
} else {
|
|
error(AppError::PermissionDenied)
|
|
}
|
|
}
|
|
None => error(AppError::AuthFailed),
|
|
}
|
|
}
|
|
|
|
pub async fn check_password(
|
|
core: Core,
|
|
req: Json<AuthRequest>,
|
|
) -> ResultExt<AuthResponse, AppError, FatalError> {
|
|
let Json(AuthRequest { username, password }) = req;
|
|
core.auth(&username, &password).await
|
|
}
|
|
|
|
pub async fn get_user(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
user_id: Option<UserId>,
|
|
) -> ResultExt<Option<UserOverview>, AppError, FatalError> {
|
|
auth_required(core.clone(), headers, |user| async move {
|
|
match user_id {
|
|
Some(user_id) => core.user(user_id).await,
|
|
None => core.user(user.id).await,
|
|
}
|
|
.map(|maybe_user| maybe_user.map(|user| UserOverview::from(user)))
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn get_users(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
) -> ResultExt<Vec<UserOverview>, AppError, FatalError> {
|
|
auth_required(core.clone(), headers, |_user| async move {
|
|
core.list_users().await.map(|users| users.into_iter().map(|user| UserOverview::from(user)).collect::<Vec<UserOverview>>())
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn create_user(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
req: CreateUserRequest,
|
|
) -> ResultExt<UserId, AppError, FatalError> {
|
|
admin_required(core.clone(), headers, |_admin| async {
|
|
core.create_user(&req.username).await
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn set_password(
|
|
core: Core,
|
|
headers: HeaderMap,
|
|
req: SetPasswordRequest,
|
|
) -> ResultExt<(), AppError, FatalError> {
|
|
auth_required(core.clone(), headers, |user| async {
|
|
if req.password_1 == req.password_2 {
|
|
core.set_password(user.id, req.password_1).await
|
|
} else {
|
|
error(AppError::BadRequest)
|
|
}
|
|
})
|
|
.await
|
|
}
|