backend: webdav tokens

This commit is contained in:
2025-10-29 00:12:58 +01:00
parent b4282c2b4b
commit d9b2297eca
12 changed files with 707 additions and 119 deletions
+112
View File
@@ -0,0 +1,112 @@
use axum::{extract::Path, http::StatusCode, Json};
use chrono::{DateTime, NaiveDateTime};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::auth::{
webdav_tokens::{
create_webdav_token as issue_token, list_webdav_tokens as load_tokens, parse_scopes,
revoke_webdav_token as revoke_token,
},
TenantScopedConn,
};
use crate::error::{AppError, AppResult};
use crate::models::WebdavToken;
use crate::utils::{db::no_content, time::to_iso};
#[derive(Debug, Serialize)]
pub struct WebdavTokenResponse {
pub id: Uuid,
pub tenant_id: Uuid,
pub label: Option<String>,
pub scopes: Vec<String>,
pub created_at: String,
pub last_used_at: Option<String>,
pub expires_at: Option<String>,
pub revoked_at: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct WebdavTokenCreatedResponse {
pub token: String,
pub token_info: WebdavTokenResponse,
}
#[derive(Debug, Deserialize)]
pub struct CreateWebdavTokenRequest {
pub label: Option<String>,
pub expires_at: Option<String>,
}
pub async fn list_webdav_tokens(
TenantScopedConn {
mut conn,
tenant_id,
user_id,
..
}: TenantScopedConn,
) -> AppResult<Json<Vec<WebdavTokenResponse>>> {
let tokens = load_tokens(&mut conn, user_id, Some(tenant_id))?;
let responses = tokens.into_iter().map(webdav_token_to_response).collect();
Ok(Json(responses))
}
pub async fn create_webdav_token(
TenantScopedConn {
mut conn,
tenant_id,
user_id,
..
}: TenantScopedConn,
Json(payload): Json<CreateWebdavTokenRequest>,
) -> AppResult<(StatusCode, Json<WebdavTokenCreatedResponse>)> {
let expires_at = match payload.expires_at {
Some(ref value) => Some(parse_timestamp(value)?),
None => None,
};
let issued = issue_token(
&mut conn,
user_id,
tenant_id,
payload.label.clone(),
None,
expires_at,
)?;
let response = WebdavTokenCreatedResponse {
token: issued.token,
token_info: webdav_token_to_response(issued.record),
};
Ok((StatusCode::CREATED, Json(response)))
}
pub async fn delete_webdav_token(
TenantScopedConn {
mut conn, user_id, ..
}: TenantScopedConn,
Path(token_id): Path<Uuid>,
) -> AppResult<StatusCode> {
revoke_token(&mut conn, token_id, user_id)?;
no_content()
}
fn webdav_token_to_response(token: WebdavToken) -> WebdavTokenResponse {
WebdavTokenResponse {
id: token.id,
tenant_id: token.tenant_id,
label: token.label,
scopes: parse_scopes(&token.scopes),
created_at: to_iso(token.created_at),
last_used_at: token.last_used_at.map(to_iso),
expires_at: token.expires_at.map(to_iso),
revoked_at: token.revoked_at.map(to_iso),
}
}
fn parse_timestamp(value: &str) -> AppResult<NaiveDateTime> {
let dt = DateTime::parse_from_rfc3339(value)
.map_err(|_| AppError::bad_request("invalid expires_at timestamp"))?;
Ok(dt.naive_utc())
}