use super::apidocs::response_schema::{ApiResponseWithout200, DefaultApiResponse};
use super::errors::WebError;
use super::middleware::KOpId;
use super::v1::{
json_rest_event_get, json_rest_event_get_id, json_rest_event_get_id_attr, json_rest_event_post,
json_rest_event_put_attr,
};
use super::ServerState;
use crate::https::extractors::VerifiedClientInformation;
use axum::extract::{rejection::JsonRejection, DefaultBodyLimit, Path, Query, State};
use axum::response::{Html, IntoResponse, Response};
use axum::routing::{get, post};
use axum::{Extension, Json, Router};
use kanidm_proto::scim_v1::{
server::ScimEntryKanidm, ScimEntryGetQuery, ScimSyncRequest, ScimSyncState,
};
use kanidm_proto::v1::Entry as ProtoEntry;
use kanidmd_lib::prelude::*;
const DEFAULT_SCIM_SYNC_BYTES: usize = 1024 * 1024 * 32;
#[utoipa::path(
get,
path = "/v1/sync_account",
responses(
(status = 200,content_type="application/json", body=Vec<ProtoEntry>),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_get"
)]
pub async fn sync_account_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Vec<ProtoEntry>>, WebError> {
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
json_rest_event_get(state, None, filter, kopid, client_auth_info).await
}
#[utoipa::path(
post,
path = "/v1/sync_account",
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_post"
)]
pub async fn sync_account_post(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, WebError> {
let classes: Vec<String> = vec![EntryClass::SyncAccount.into(), EntryClass::Object.into()];
json_rest_event_post(state, classes, obj, kopid, client_auth_info).await
}
#[utoipa::path(
get,
path = "/v1/sync_account/{id}",
responses(
(status = 200,content_type="application/json", body=Option<ProtoEntry>),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
)]
pub async fn sync_account_id_get(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Option<ProtoEntry>>, WebError> {
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
json_rest_event_get_id(state, id, filter, None, kopid, client_auth_info).await
}
#[utoipa::path(
patch,
path = "/v1/sync_account/{id}",
request_body=ProtoEntry,
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_id_patch"
)]
pub async fn sync_account_id_patch(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, WebError> {
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str())));
state
.qe_w_ref
.handle_internalpatch(client_auth_info, filter, obj, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
get,
path = "/v1/sync_account/{id}/_finalise",
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_id_finalise_get"
)]
pub async fn sync_account_id_finalise_get(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, WebError> {
state
.qe_w_ref
.handle_sync_account_finalise(client_auth_info, id, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
get,
path = "/v1/sync_account/{id}/_terminate",
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_id_terminate_get"
)]
pub async fn sync_account_id_terminate_get(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, WebError> {
state
.qe_w_ref
.handle_sync_account_terminate(client_auth_info, id, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
post,
path = "/v1/sync_account/{id}/_sync_token",
responses(
(status = 200, body=String, content_type="application/json"),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_token_post"
)]
pub async fn sync_account_token_post(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(label): Json<String>,
) -> Result<Json<String>, WebError> {
state
.qe_w_ref
.handle_sync_account_token_generate(client_auth_info, id, label, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
delete,
path = "/v1/sync_account/{id}/_sync_token",
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_token_delete"
)]
pub async fn sync_account_token_delete(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, WebError> {
state
.qe_w_ref
.handle_sync_account_token_destroy(client_auth_info, id, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
get,
path = "/v1/sync_account/{id}/_attr/{attr}",
responses(
(status = 200, body=Option<Vec<String>>, content_type="application/json"),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_id_attr_get"
)]
pub async fn sync_account_id_attr_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((id, attr)): Path<(String, String)>,
) -> Result<Json<Option<Vec<String>>>, WebError> {
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
json_rest_event_get_id_attr(state, id, attr, filter, kopid, client_auth_info).await
}
#[utoipa::path(
post,
path = "/v1/sync_account/{id}/_attr/{attr}",
request_body=Vec<String>,
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "v1/sync_account",
operation_id = "sync_account_id_attr_put"
)]
pub async fn sync_account_id_attr_put(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((id, attr)): Path<(String, String)>,
Json(values): Json<Vec<String>>,
) -> Result<Json<()>, WebError> {
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await
}
async fn scim_sink_get() -> Html<&'static str> {
Html::from(include_str!("scim/sink.html"))
}
#[utoipa::path(
post,
path = "/scim/v1/Sync",
request_body = ScimSyncRequest,
responses(
DefaultApiResponse,
),
security(("token_jwt" = [])),
tag = "scim",
operation_id = "scim_sync_post"
)]
async fn scim_sync_post(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
payload: Result<Json<ScimSyncRequest>, JsonRejection>,
) -> Response {
match payload {
Ok(Json(changes)) => {
let res = state
.qe_w_ref
.handle_scim_sync_apply(client_auth_info, changes, kopid.eventid)
.await;
match res {
Ok(data) => Json::from(data).into_response(),
Err(err) => WebError::from(err).into_response(),
}
}
Err(rejection) => {
error!(?rejection, "Unable to process JSON");
rejection.into_response()
}
}
}
#[utoipa::path(
get,
path = "/scim/v1/Sync",
responses(
(status = 200, content_type="application/json", body=ScimSyncState), ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "scim",
operation_id = "scim_sync_get"
)]
async fn scim_sync_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<ScimSyncState>, WebError> {
state
.qe_r_ref
.handle_scim_sync_status(client_auth_info, kopid.eventid)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
get,
path = "/scim/v1/Entry/{id}",
responses(
(status = 200, content_type="application/json", body=ScimEntry),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "scim",
operation_id = "scim_entry_id_get"
)]
async fn scim_entry_id_get(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Query(scim_entry_get_query): Query<ScimEntryGetQuery>,
) -> Result<Json<ScimEntryKanidm>, WebError> {
state
.qe_r_ref
.scim_entry_id_get(
client_auth_info,
kopid.eventid,
id,
EntryClass::Object,
scim_entry_get_query,
)
.await
.map(Json::from)
.map_err(WebError::from)
}
#[utoipa::path(
get,
path = "/scim/v1/Person/{id}",
responses(
(status = 200, content_type="application/json", body=ScimEntry),
ApiResponseWithout200,
),
security(("token_jwt" = [])),
tag = "scim",
operation_id = "scim_person_id_get"
)]
async fn scim_person_id_get(
State(state): State<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Query(scim_entry_get_query): Query<ScimEntryGetQuery>,
) -> Result<Json<ScimEntryKanidm>, WebError> {
state
.qe_r_ref
.scim_entry_id_get(
client_auth_info,
kopid.eventid,
id,
EntryClass::Person,
scim_entry_get_query,
)
.await
.map(Json::from)
.map_err(WebError::from)
}
pub fn route_setup() -> Router<ServerState> {
Router::new()
.route(
"/v1/sync_account",
get(sync_account_get).post(sync_account_post),
)
.route(
"/v1/sync_account/:id",
get(sync_account_id_get).patch(sync_account_id_patch),
)
.route(
"/v1/sync_account/:id/_attr/:attr",
get(sync_account_id_attr_get).put(sync_account_id_attr_put),
)
.route(
"/v1/sync_account/:id/_finalise",
get(sync_account_id_finalise_get),
)
.route(
"/v1/sync_account/:id/_terminate",
get(sync_account_id_terminate_get),
)
.route(
"/v1/sync_account/:id/_sync_token",
post(sync_account_token_post).delete(sync_account_token_delete),
)
.route("/scim/v1/Entry/:id", get(scim_entry_id_get))
.route("/scim/v1/Person/:id", get(scim_person_id_get))
.route(
"/scim/v1/Sync",
post(scim_sync_post)
.layer(DefaultBodyLimit::max(DEFAULT_SCIM_SYNC_BYTES))
.get(scim_sync_get),
)
.route("/scim/v1/Sink", get(scim_sink_get)) }