dialtone_axum 0.1.0

Dialtone Axum Back-end
Documentation
use axum::extract::{Extension, Query};
use axum::response::IntoResponse;
use axum::Json;
use dialtone_common::ap::pun::is_valid_pun;
use dialtone_common::rest::sites::site_data::{RegistrationMethod, SiteInfo};
use dialtone_common::rest::users::user_exchanges::{GetUser, PostUser, PostUserResponse};

use dialtone_common::rest::users::web_user::UserStatus;
use dialtone_common::utils::make_acct::make_acct;
use dialtone_common::utils::parse_acct::parse_user_acct;
use dialtone_sqlx::control::user::check_name_availability::check_user_name_availability;
use dialtone_sqlx::control::user::create_with_actor::create_user_with_default_actor;
use dialtone_sqlx::db::site_info::fetch_site;

use dialtone_sqlx::db::user_principal::change_status::change_user_status;
use sqlx::{Pool, Postgres};
use tokio::time::{sleep, Duration};

use crate::authz::authz_user::authz_acct_or_useradmin;
use crate::{err_bad_request, err_not_found, err_unauthorized};
use dialtone_sqlx::db::user_principal::fetch_info::fetch_user_info;

use crate::response::response_error::ResponseError;
use crate::start_server::{Claims, SharedState};

pub async fn get_user_handler(
    claims: Claims,
    Query(params): Query<GetUser>,
    Extension(pool): Extension<Pool<Postgres>>,
    Extension(state): Extension<SharedState>,
) -> Result<impl IntoResponse, ResponseError> {
    authz_acct_or_useradmin(&params.acct, &claims, &state).await?;
    let user = fetch_user_info(&pool, &params.acct).await?;
    user.map_or_else(|| err_unauthorized!(), |v| Ok(Json(v)))
}

pub async fn post_user_handler(
    Json(request): Json<PostUser>,
    Extension(pool): Extension<Pool<Postgres>>,
) -> Result<impl IntoResponse, ResponseError> {
    // a human will not care about this. a spam bot would.
    sleep(Duration::from_millis(750)).await;
    let user_host = match request {
        PostUser::OpenRegistration(ref user_creds) => parse_user_acct(&user_creds.user_acct),
        PostUser::SimpleCode {
            ref user_creds,
            simple_code_for_registration: _,
        } => parse_user_acct(&user_creds.user_acct),
    };
    if let Ok(acct_parts) = user_host {
        // since this code path creates a default actors,
        // the names needs to be checked.
        if !is_valid_pun(&acct_parts.user_name, false) {
            return err_bad_request!();
        }

        // check that the sites allows registration
        let site_info = fetch_site(&pool, &acct_parts.host_name).await?;
        if let Some(site_info) = site_info {
            match request {
                PostUser::OpenRegistration(user_creds) => {
                    if site_info
                        .site_data
                        .public
                        .registration_methods
                        .contains(&RegistrationMethod::OpenRegistration)
                    {
                        return do_registration(
                            &pool,
                            &acct_parts.user_name,
                            &acct_parts.host_name,
                            &user_creds.password,
                            &site_info,
                            &RegistrationMethod::OpenRegistration,
                        )
                        .await;
                    } else {
                        return err_bad_request!();
                    }
                }
                PostUser::SimpleCode {
                    user_creds,
                    simple_code_for_registration,
                } => {
                    if site_info
                        .site_data
                        .public
                        .registration_methods
                        .contains(&RegistrationMethod::SimpleCode)
                    {
                        if site_info.site_data.simple_code_for_registration
                            == Some(simple_code_for_registration)
                        {
                            return do_registration(
                                &pool,
                                &acct_parts.user_name,
                                &acct_parts.host_name,
                                &user_creds.password,
                                &site_info,
                                &RegistrationMethod::SimpleCode,
                            )
                            .await;
                        } else {
                            return err_unauthorized!();
                        }
                    } else {
                        return err_bad_request!();
                    }
                }
            }
        } else {
            return err_not_found!();
        }
    } else {
        err_bad_request!()
    }
}

async fn do_registration(
    pool: &Pool<Postgres>,
    user_name: &str,
    host_name: &str,
    password: &str,
    site_info: &SiteInfo,
    registration_method: &RegistrationMethod,
) -> Result<impl IntoResponse, ResponseError> {
    // make sure the user is not already registered
    let name_available = check_user_name_availability(&pool, &user_name, &host_name).await?;
    if !name_available {
        return err_unauthorized!();
    }

    create_user_with_default_actor(&pool, &user_name, &host_name, &password).await?;
    if site_info
        .site_data
        .auto_approved_registrations
        .contains(registration_method)
    {
        change_user_status(pool, &make_acct(user_name, host_name), &UserStatus::Active).await?;
        Ok(Json(PostUserResponse::AccountRegistered(None)))
    } else {
        Ok(Json(PostUserResponse::AccountNeedsApproval(None)))
    }
}