torrust_index_backend/web/api/v1/contexts/user/
handlers.rs

1//! API handlers for the the [`user`](crate::web::api::v1::contexts::user) API
2//! context.
3use std::sync::Arc;
4
5use axum::extract::{self, Host, Path, State};
6use axum::response::{IntoResponse, Response};
7use axum::Json;
8use serde::Deserialize;
9
10use super::forms::{JsonWebToken, LoginForm, RegistrationForm};
11use super::responses::{self};
12use crate::common::AppData;
13use crate::web::api::v1::extractors::bearer_token::Extract;
14use crate::web::api::v1::responses::OkResponseData;
15
16// Registration
17
18/// It handles the registration of a new user.
19///
20/// # Errors
21///
22/// It returns an error if the user could not be registered.
23#[allow(clippy::unused_async)]
24pub async fn registration_handler(
25    State(app_data): State<Arc<AppData>>,
26    Host(host_from_header): Host,
27    extract::Json(registration_form): extract::Json<RegistrationForm>,
28) -> Response {
29    let api_base_url = app_data
30        .cfg
31        .get_api_base_url()
32        .await
33        .unwrap_or(api_base_url(&host_from_header));
34
35    match app_data
36        .registration_service
37        .register_user(&registration_form, &api_base_url)
38        .await
39    {
40        Ok(user_id) => responses::added_user(user_id).into_response(),
41        Err(error) => error.into_response(),
42    }
43}
44
45#[derive(Deserialize)]
46pub struct TokenParam(String);
47
48/// It handles the verification of the email verification token.
49#[allow(clippy::unused_async)]
50pub async fn email_verification_handler(State(app_data): State<Arc<AppData>>, Path(token): Path<TokenParam>) -> String {
51    match app_data.registration_service.verify_email(&token.0).await {
52        Ok(_) => String::from("Email verified, you can close this page."),
53        Err(error) => error.to_string(),
54    }
55}
56
57// Authentication
58
59/// It handles the user login.
60///
61/// # Errors
62///
63/// It returns an error if:
64///
65/// - Unable to verify the supplied payload as a valid JWT.
66/// - The JWT is not invalid or expired.
67#[allow(clippy::unused_async)]
68pub async fn login_handler(
69    State(app_data): State<Arc<AppData>>,
70    extract::Json(login_form): extract::Json<LoginForm>,
71) -> Response {
72    match app_data
73        .authentication_service
74        .login(&login_form.login, &login_form.password)
75        .await
76    {
77        Ok((token, user_compact)) => responses::logged_in_user(token, user_compact).into_response(),
78        Err(error) => error.into_response(),
79    }
80}
81
82/// It verifies a supplied JWT.
83///
84/// # Errors
85///
86/// It returns an error if:
87///
88/// - Unable to verify the supplied payload as a valid JWT.
89/// - The JWT is not invalid or expired.
90#[allow(clippy::unused_async)]
91pub async fn verify_token_handler(
92    State(app_data): State<Arc<AppData>>,
93    extract::Json(token): extract::Json<JsonWebToken>,
94) -> Response {
95    match app_data.json_web_token.verify(&token.token).await {
96        Ok(_) => axum::Json(OkResponseData {
97            data: "Token is valid.".to_string(),
98        })
99        .into_response(),
100        Err(error) => error.into_response(),
101    }
102}
103
104#[derive(Deserialize)]
105pub struct UsernameParam(pub String);
106
107/// It renews the JWT.
108///
109/// # Errors
110///
111/// It returns an error if:
112///
113/// - Unable to parse the supplied payload as a valid JWT.
114/// - The JWT is not invalid or expired.
115#[allow(clippy::unused_async)]
116pub async fn renew_token_handler(
117    State(app_data): State<Arc<AppData>>,
118    extract::Json(token): extract::Json<JsonWebToken>,
119) -> Response {
120    match app_data.authentication_service.renew_token(&token.token).await {
121        Ok((token, user_compact)) => responses::renewed_token(token, user_compact).into_response(),
122        Err(error) => error.into_response(),
123    }
124}
125
126/// It bans a user from the index.
127///
128/// # Errors
129///
130/// This function will return if:
131///
132/// - The JWT provided by the banning authority was not valid.
133/// - The user could not be banned: it does not exist, etcetera.
134#[allow(clippy::unused_async)]
135pub async fn ban_handler(
136    State(app_data): State<Arc<AppData>>,
137    Path(to_be_banned_username): Path<UsernameParam>,
138    Extract(maybe_bearer_token): Extract,
139) -> Response {
140    // todo: add reason and `date_expiry` parameters to request
141
142    let user_id = match app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await {
143        Ok(user_id) => user_id,
144        Err(error) => return error.into_response(),
145    };
146
147    match app_data.ban_service.ban_user(&to_be_banned_username.0, &user_id).await {
148        Ok(_) => Json(OkResponseData {
149            data: format!("Banned user: {}", to_be_banned_username.0),
150        })
151        .into_response(),
152        Err(error) => error.into_response(),
153    }
154}
155
156/// It returns the base API URL without the port. For example: `http://localhost`.
157fn api_base_url(host: &str) -> String {
158    // HTTPS is not supported yet.
159    // See https://github.com/torrust/torrust-index-backend/issues/131
160    format!("http://{host}")
161}