caps and delete
This commit is contained in:
@@ -14,12 +14,13 @@ use crate::auth::{
|
||||
regenerate_api_token as rotate_token, revoke_api_token as revoke_token,
|
||||
update_api_token_capabilities as update_capabilities,
|
||||
},
|
||||
capability_sets::load_capabilities_for_set,
|
||||
passkeys::PasskeySummary,
|
||||
TenantScopedConn,
|
||||
};
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::models::{ApiToken, ApiTokenCapability};
|
||||
use crate::state::AppState;
|
||||
use crate::models::{ApiCapability, ApiToken};
|
||||
use crate::state::{AppState, PgPooledConnection};
|
||||
use crate::utils::{db::no_content, time::to_iso};
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
@@ -28,7 +29,7 @@ pub struct ApiTokenResponse {
|
||||
pub tenant_id: Uuid,
|
||||
#[schema(nullable)]
|
||||
pub label: Option<String>,
|
||||
pub capabilities: Vec<ApiTokenCapability>,
|
||||
pub capabilities: Vec<ApiCapability>,
|
||||
pub created_at: String,
|
||||
#[schema(nullable)]
|
||||
pub last_used_at: Option<String>,
|
||||
@@ -52,12 +53,12 @@ pub struct CreateApiTokenRequest {
|
||||
pub expires_at: Option<String>,
|
||||
#[serde(default)]
|
||||
#[schema(nullable)]
|
||||
pub capabilities: Option<Vec<ApiTokenCapability>>,
|
||||
pub capabilities: Option<Vec<ApiCapability>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct UpdateApiTokenCapabilitiesRequest {
|
||||
pub capabilities: Vec<ApiTokenCapability>,
|
||||
pub capabilities: Vec<ApiCapability>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
@@ -103,7 +104,10 @@ pub async fn list_api_tokens(
|
||||
}: TenantScopedConn,
|
||||
) -> AppResult<Json<Vec<ApiTokenResponse>>> {
|
||||
let tokens = load_tokens(&mut conn, user_id, Some(tenant_id))?;
|
||||
let responses = tokens.into_iter().map(api_token_to_response).collect();
|
||||
let mut responses = Vec::with_capacity(tokens.len());
|
||||
for token in tokens {
|
||||
responses.push(api_token_to_response(&mut conn, token)?);
|
||||
}
|
||||
Ok(Json(responses))
|
||||
}
|
||||
|
||||
@@ -130,7 +134,11 @@ pub async fn create_api_token(
|
||||
|
||||
let capabilities = payload
|
||||
.capabilities
|
||||
.unwrap_or_else(|| vec![ApiTokenCapability::Webdav]);
|
||||
.ok_or_else(|| AppError::bad_request("at least one capability is required"))?;
|
||||
|
||||
if capabilities.is_empty() {
|
||||
return Err(AppError::bad_request("at least one capability is required"));
|
||||
}
|
||||
|
||||
let issued = issue_token(
|
||||
&mut conn,
|
||||
@@ -141,9 +149,11 @@ pub async fn create_api_token(
|
||||
capabilities,
|
||||
)?;
|
||||
|
||||
let token_info = api_token_to_response(&mut conn, issued.record)?;
|
||||
|
||||
let response = ApiTokenCreatedResponse {
|
||||
token: issued.token,
|
||||
token_info: api_token_to_response(issued.record),
|
||||
token_info,
|
||||
};
|
||||
|
||||
Ok((StatusCode::CREATED, Json(response)))
|
||||
@@ -166,9 +176,10 @@ pub async fn regenerate_api_token(
|
||||
Path(token_id): Path<Uuid>,
|
||||
) -> AppResult<Json<ApiTokenCreatedResponse>> {
|
||||
let issued = rotate_token(&mut conn, token_id, user_id, Some(tenant_id))?;
|
||||
let token_info = api_token_to_response(&mut conn, issued.record)?;
|
||||
let response = ApiTokenCreatedResponse {
|
||||
token: issued.token,
|
||||
token_info: api_token_to_response(issued.record),
|
||||
token_info,
|
||||
};
|
||||
|
||||
Ok(Json(response))
|
||||
@@ -192,6 +203,10 @@ pub async fn update_api_token(
|
||||
Path(token_id): Path<Uuid>,
|
||||
Json(payload): Json<UpdateApiTokenCapabilitiesRequest>,
|
||||
) -> AppResult<Json<ApiTokenResponse>> {
|
||||
if payload.capabilities.is_empty() {
|
||||
return Err(AppError::bad_request("at least one capability is required"));
|
||||
}
|
||||
|
||||
let updated = update_capabilities(
|
||||
&mut conn,
|
||||
token_id,
|
||||
@@ -200,7 +215,9 @@ pub async fn update_api_token(
|
||||
payload.capabilities,
|
||||
)?;
|
||||
|
||||
Ok(Json(api_token_to_response(updated)))
|
||||
let response = api_token_to_response(&mut conn, updated)?;
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -254,7 +271,10 @@ pub async fn delete_passkey(
|
||||
no_content()
|
||||
}
|
||||
|
||||
fn api_token_to_response(token: ApiToken) -> ApiTokenResponse {
|
||||
fn api_token_to_response(
|
||||
conn: &mut PgPooledConnection,
|
||||
token: ApiToken,
|
||||
) -> AppResult<ApiTokenResponse> {
|
||||
let ApiToken {
|
||||
id,
|
||||
tenant_id,
|
||||
@@ -263,11 +283,13 @@ fn api_token_to_response(token: ApiToken) -> ApiTokenResponse {
|
||||
last_used_at,
|
||||
expires_at,
|
||||
revoked_at,
|
||||
capabilities,
|
||||
capability_set_id,
|
||||
..
|
||||
} = token;
|
||||
|
||||
ApiTokenResponse {
|
||||
let capabilities = load_capabilities_for_set(conn, capability_set_id)?;
|
||||
|
||||
Ok(ApiTokenResponse {
|
||||
id,
|
||||
tenant_id,
|
||||
label,
|
||||
@@ -276,7 +298,7 @@ fn api_token_to_response(token: ApiToken) -> ApiTokenResponse {
|
||||
last_used_at: last_used_at.map(to_iso),
|
||||
expires_at: expires_at.map(to_iso),
|
||||
revoked_at: revoked_at.map(to_iso),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_timestamp(value: &str) -> AppResult<NaiveDateTime> {
|
||||
@@ -297,7 +319,7 @@ fn parse_timestamp(value: &str) -> AppResult<NaiveDateTime> {
|
||||
crate::routes::profile::delete_passkey
|
||||
),
|
||||
components(schemas(
|
||||
crate::models::ApiTokenCapability,
|
||||
crate::models::ApiCapability,
|
||||
crate::routes::profile::ApiTokenResponse,
|
||||
crate::routes::profile::ApiTokenCreatedResponse,
|
||||
crate::routes::profile::CreateApiTokenRequest,
|
||||
|
||||
Reference in New Issue
Block a user