use axum::{
Json,
extract::{Path, Query},
http::StatusCode,
response::IntoResponse,
};
use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
use crate::service;
pub use manta_shared::types::api::queries::{DeleteGroupQuery, GroupQuery};
#[utoipa::path(get, path = "/groups/available", tag = "groups",
params(SiteHeader),
security(("bearerAuth" = [])),
responses(
(status = 200, description = "List of accessible group names", body = Vec<String>),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn get_available_groups(
ctx: RequestCtx,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let infra = ctx.infra();
let names = infra
.backend
.get_group_name_available(&ctx.token)
.await
.map_err(to_handler_error)?;
Ok(Json(names))
}
#[utoipa::path(get, path = "/groups", tag = "groups",
params(GroupQuery, SiteHeader),
security(("bearerAuth" = [])),
responses(
(status = 200, description = "List of groups", body = Vec<manta_backend_dispatcher::types::Group>),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn get_groups(
ctx: RequestCtx,
Query(q): Query<GroupQuery>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let infra = ctx.infra();
let params = service::group::GetGroupParams {
group_name: q.name,
settings_group_name: None,
};
let groups = service::group::get_groups(&infra, &ctx.token, ¶ms)
.await
.map_err(to_handler_error)?;
Ok(Json(groups))
}
#[utoipa::path(delete, path = "/groups/{label}", tag = "groups",
params(("label" = String, Path, description = "Group label"), DeleteGroupQuery, SiteHeader),
security(("bearerAuth" = [])),
responses(
(status = 204, description = "Group removed"),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 404, description = "Not found", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn delete_group(
ctx: RequestCtx,
Path(label): Path<String>,
Query(q): Query<DeleteGroupQuery>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!("delete_group label={} force={}", label, q.force);
let infra = ctx.infra();
service::authorization::validate_user_group_access(
&infra, &ctx.token, &label,
)
.await
.map_err(to_handler_error)?;
service::group::delete_group(&infra, &ctx.token, &label, q.force)
.await
.map_err(to_handler_error)?;
Ok(StatusCode::NO_CONTENT)
}
#[utoipa::path(post, path = "/groups", tag = "groups",
params(SiteHeader),
request_body = manta_backend_dispatcher::types::Group,
security(("bearerAuth" = [])),
responses(
(status = 201, description = "Group created", body = manta_shared::types::api::responses::CreatedResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 409, description = "Conflict", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn create_group(
ctx: RequestCtx,
Json(group): Json<::manta_backend_dispatcher::types::Group>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!("create_group");
let infra = ctx.infra();
if !crate::server::common::jwt_ops::is_user_admin(&ctx.token) {
return Err(to_handler_error(
manta_backend_dispatcher::error::Error::BadRequest(
"group creation requires admin privileges".to_string(),
),
));
}
service::group::create_group(&infra, &ctx.token, group)
.await
.map_err(to_handler_error)?;
Ok((
StatusCode::CREATED,
Json(serde_json::json!({ "created": true })),
))
}
pub use manta_shared::types::api::group::{
AddNodesToGroupRequest, AddNodesToGroupResponse, DeleteGroupMembersRequest,
};
#[utoipa::path(post, path = "/groups/{name}/members", tag = "groups",
params(("name" = String, Path, description = "Group name"), SiteHeader),
request_body = AddNodesToGroupRequest,
security(("bearerAuth" = [])),
responses(
(status = 200, description = "Members updated", body = AddNodesToGroupResponse),
(status = 400, description = "Bad request", body = ErrorResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn add_nodes_to_group(
ctx: RequestCtx,
Path(name): Path<String>,
Json(body): Json<AddNodesToGroupRequest>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!(
"add_nodes_to_group group={} hosts={}",
name,
body.hosts_expression
);
let infra = ctx.infra();
service::authorization::validate_user_group_access(&infra, &ctx.token, &name)
.await
.map_err(to_handler_error)?;
let (added, removed) = service::group::add_nodes_to_group(
&infra,
&ctx.token,
&name,
&body.hosts_expression,
)
.await
.map_err(to_handler_error)?;
Ok(Json(AddNodesToGroupResponse {
added,
final_members: removed.clone(),
removed,
}))
}
#[utoipa::path(delete, path = "/groups/{name}/members", tag = "groups",
params(("name" = String, Path, description = "Group name"), SiteHeader),
request_body = DeleteGroupMembersRequest,
security(("bearerAuth" = [])),
responses(
(status = 204, description = "Members removed"),
(status = 400, description = "Bad request", body = ErrorResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn delete_group_members(
ctx: RequestCtx,
Path(name): Path<String>,
Json(body): Json<DeleteGroupMembersRequest>,
) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
tracing::info!(
"delete_group_members group={} xnames_expression={} dry_run={}",
name,
body.xnames_expression,
body.dry_run
);
let infra = ctx.infra();
service::authorization::validate_user_group_access(&infra, &ctx.token, &name)
.await
.map_err(to_handler_error)?;
service::group::delete_group_members(
&infra,
&ctx.token,
&name,
&body.xnames_expression,
body.dry_run,
)
.await
.map_err(to_handler_error)?;
Ok(StatusCode::NO_CONTENT)
}