use axum::{
extract::{Path, State},
Json,
};
use freshblu_core::{
error::FreshBluError, permissions::PermissionChecker, token::GenerateTokenOptions,
};
use std::collections::HashMap;
use uuid::Uuid;
use super::AuthenticatedDevice;
use crate::{ApiError, AppState};
type ApiResult<T> = Result<Json<T>, ApiError>;
pub async fn generate_token(
State(state): State<AppState>,
AuthenticatedDevice(actor, _): AuthenticatedDevice,
Path(uuid): Path<Uuid>,
Json(opts): Json<Option<GenerateTokenOptions>>,
) -> ApiResult<serde_json::Value> {
let device = state
.store
.get_device(&uuid)
.await?
.ok_or(FreshBluError::NotFound)
.map_err(ApiError::from)?;
let checker = PermissionChecker::new(&device.meshblu.whitelists, &actor.uuid, &uuid);
if !checker.can_configure_update() {
return Err(FreshBluError::Forbidden.into());
}
let (record, plaintext) = state
.store
.generate_token(&uuid, opts.unwrap_or_default())
.await?;
Ok(Json(serde_json::json!({
"uuid": uuid,
"token": plaintext,
"createdAt": record.created_at,
})))
}
pub async fn revoke_token(
State(state): State<AppState>,
AuthenticatedDevice(actor, _): AuthenticatedDevice,
Path((uuid, token)): Path<(Uuid, String)>,
) -> ApiResult<serde_json::Value> {
let device = state
.store
.get_device(&uuid)
.await?
.ok_or(FreshBluError::NotFound)
.map_err(ApiError::from)?;
let checker = PermissionChecker::new(&device.meshblu.whitelists, &actor.uuid, &uuid);
if !checker.can_configure_update() {
return Err(FreshBluError::Forbidden.into());
}
state.store.revoke_token(&uuid, &token).await?;
Ok(Json(serde_json::json!({ "revoked": true })))
}
pub async fn reset_token(
State(state): State<AppState>,
AuthenticatedDevice(actor, _): AuthenticatedDevice,
Path(uuid): Path<Uuid>,
) -> ApiResult<serde_json::Value> {
let device = state
.store
.get_device(&uuid)
.await?
.ok_or(FreshBluError::NotFound)
.map_err(ApiError::from)?;
let checker = PermissionChecker::new(&device.meshblu.whitelists, &actor.uuid, &uuid);
if !checker.can_configure_update() {
return Err(FreshBluError::Forbidden.into());
}
let new_token = state.store.reset_token(&uuid).await?;
Ok(Json(serde_json::json!({
"uuid": uuid,
"token": new_token,
})))
}
pub async fn search_tokens(
State(state): State<AppState>,
AuthenticatedDevice(actor, _): AuthenticatedDevice,
Json(mut query): Json<HashMap<String, serde_json::Value>>,
) -> ApiResult<Vec<serde_json::Value>> {
query.insert(
"uuid".to_string(),
serde_json::Value::String(actor.uuid.to_string()),
);
let records = state.store.search_tokens(&query).await?;
let results: Vec<serde_json::Value> = records
.into_iter()
.map(|r| {
serde_json::json!({
"uuid": r.uuid,
"createdAt": r.created_at,
"expiresOn": r.expires_on,
"tag": r.tag,
})
})
.collect();
Ok(Json(results))
}