101 lines
3.4 KiB
Rust
101 lines
3.4 KiB
Rust
//! Who administers the app.
|
|
//!
|
|
//! Users themselves are not managed here: they come from the authentication
|
|
//! provider. The one decision that belongs to this app is `admin`, and this is
|
|
//! where it is taken.
|
|
|
|
use aide::{
|
|
axum::{
|
|
ApiRouter,
|
|
routing::{delete_with, get_with, post_with},
|
|
},
|
|
transform::TransformOperation,
|
|
};
|
|
use axum::{Json, extract::Path, http::StatusCode};
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
|
|
use crate::{
|
|
api::helpers::{IdPath, admin, admin_desc, desc, unexpected_error},
|
|
core::{
|
|
controller::{AppController, ControllerError, users::UsersControllerError},
|
|
models::user::Administrator,
|
|
},
|
|
};
|
|
|
|
pub fn routes() -> ApiRouter {
|
|
ApiRouter::new()
|
|
.api_route("/admins", get_with(get_admins, get_admins_docs))
|
|
.api_route("/admins", post_with(grant_admin, grant_admin_docs))
|
|
.api_route("/admins/{id}", delete_with(revoke_admin, revoke_admin_docs))
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, JsonSchema)]
|
|
struct GrantAdminForm {
|
|
/// The address of the person to promote, whether or not they have ever
|
|
/// logged in
|
|
email: String,
|
|
}
|
|
|
|
#[axum::debug_handler]
|
|
async fn get_admins(ac: AppController) -> Result<Json<Vec<Administrator>>, (StatusCode, String)> {
|
|
match admin(ac)?.get_admins().await {
|
|
Ok(admins) => Ok(Json(admins)),
|
|
Err(err) => unexpected_error("get_admins", err),
|
|
}
|
|
}
|
|
|
|
fn get_admins_docs(op: TransformOperation) -> TransformOperation {
|
|
op.tag("Users")
|
|
.summary("List the administrators")
|
|
.response_with::<403, (), _>(admin_desc)
|
|
}
|
|
|
|
#[axum::debug_handler]
|
|
async fn grant_admin(
|
|
ac: AppController,
|
|
Json(GrantAdminForm { email }): Json<GrantAdminForm>,
|
|
) -> Result<Json<Administrator>, (StatusCode, String)> {
|
|
match admin(ac)?.grant_admin(&email).await {
|
|
Ok(administrator) => Ok(Json(administrator)),
|
|
Err(ControllerError::User(err @ UsersControllerError::EmailInvalid)) => {
|
|
Err((StatusCode::BAD_REQUEST, err.to_string()))
|
|
}
|
|
Err(err) => unexpected_error("grant_admin", err),
|
|
}
|
|
}
|
|
|
|
fn grant_admin_docs(op: TransformOperation) -> TransformOperation {
|
|
op.tag("Users")
|
|
.summary("Make somebody an administrator, by email")
|
|
.description(
|
|
"The person need not have logged in yet: the row created is adopted \
|
|
at their first login. Granting to somebody who already is one \
|
|
changes nothing.",
|
|
)
|
|
.response_with::<403, (), _>(admin_desc)
|
|
.response_with::<400, (), _>(desc("Not an email address"))
|
|
}
|
|
|
|
#[axum::debug_handler]
|
|
async fn revoke_admin(
|
|
ac: AppController,
|
|
Path(IdPath { id }): Path<IdPath>,
|
|
) -> Result<(), (StatusCode, String)> {
|
|
match admin(ac)?.revoke_admin(id).await {
|
|
Ok(()) => Ok(()),
|
|
Err(ControllerError::User(err @ UsersControllerError::CannotDemoteSelf)) => {
|
|
Err((StatusCode::CONFLICT, err.to_string()))
|
|
}
|
|
Err(err) if err.is_not_found() => Err((StatusCode::NOT_FOUND, "No such user".to_owned())),
|
|
Err(err) => unexpected_error("revoke_admin", err),
|
|
}
|
|
}
|
|
|
|
fn revoke_admin_docs(op: TransformOperation) -> TransformOperation {
|
|
op.tag("Users")
|
|
.summary("Take the administrator rights away")
|
|
.response_with::<403, (), _>(admin_desc)
|
|
.response_with::<409, (), _>(desc("An administrator cannot demote themselves"))
|
|
.response::<404, ()>()
|
|
}
|