2025-12-29 05:04:53 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
2025-12-22 21:34:01 +00:00
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
use axum::{
|
|
|
|
|
extract::{Path, State},
|
|
|
|
|
http::StatusCode,
|
|
|
|
|
response::IntoResponse,
|
|
|
|
|
Json,
|
|
|
|
|
};
|
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
|
|
|
|
|
use super::ApiResponse;
|
|
|
|
|
use crate::core::VaultCore;
|
|
|
|
|
|
|
|
|
|
/// GET /v1/* - Read a secret from any mounted engine
|
|
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
pub async fn read_secret(
|
|
|
|
|
State(vault): State<Arc<VaultCore>>,
|
|
|
|
|
Path(path): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let full_path = path;
|
|
|
|
|
|
|
|
|
|
match vault.split_path(&full_path) {
|
|
|
|
|
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
|
|
|
|
|
Some(engine) => match engine.read(&relative_path).await {
|
|
|
|
|
Ok(Some(data)) => {
|
|
|
|
|
let response = ApiResponse::success(data);
|
|
|
|
|
(StatusCode::OK, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Ok(None) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Secret not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error(format!("Failed to read: {}", e));
|
|
|
|
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("No engine mounted at this path");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Path not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /v1/* - Write a secret to any mounted engine
|
|
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
pub async fn write_secret(
|
|
|
|
|
State(vault): State<Arc<VaultCore>>,
|
|
|
|
|
Path(path): Path<String>,
|
|
|
|
|
Json(payload): Json<Value>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let full_path = path;
|
|
|
|
|
|
|
|
|
|
match vault.split_path(&full_path) {
|
|
|
|
|
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
|
|
|
|
|
Some(engine) => match engine.write(&relative_path, &payload).await {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
let response = ApiResponse::success(json!({"path": full_path}));
|
|
|
|
|
(StatusCode::OK, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error(format!("Failed to write: {}", e));
|
|
|
|
|
(StatusCode::BAD_REQUEST, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("No engine mounted at this path");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Path not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// PUT /v1/* - Update a secret in any mounted engine
|
|
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
pub async fn update_secret(
|
|
|
|
|
State(vault): State<Arc<VaultCore>>,
|
|
|
|
|
Path(path): Path<String>,
|
|
|
|
|
Json(payload): Json<Value>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let full_path = path;
|
|
|
|
|
|
|
|
|
|
match vault.split_path(&full_path) {
|
|
|
|
|
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
|
|
|
|
|
Some(engine) => match engine.write(&relative_path, &payload).await {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
let response = ApiResponse::success(json!({"path": full_path}));
|
|
|
|
|
(StatusCode::OK, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error(format!("Failed to update: {}", e));
|
|
|
|
|
(StatusCode::BAD_REQUEST, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("No engine mounted at this path");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Path not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// DELETE /v1/* - Delete a secret from any mounted engine
|
|
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
pub async fn delete_secret(
|
|
|
|
|
State(vault): State<Arc<VaultCore>>,
|
|
|
|
|
Path(path): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let full_path = path;
|
|
|
|
|
|
|
|
|
|
match vault.split_path(&full_path) {
|
|
|
|
|
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
|
|
|
|
|
Some(engine) => match engine.delete(&relative_path).await {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
let response: ApiResponse<Value> = ApiResponse::success(json!({}));
|
|
|
|
|
(StatusCode::NO_CONTENT, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error(format!("Failed to delete: {}", e));
|
|
|
|
|
(StatusCode::BAD_REQUEST, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("No engine mounted at this path");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Path not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// LIST /v1/* - List secrets at a path prefix
|
|
|
|
|
#[cfg(feature = "server")]
|
|
|
|
|
pub async fn list_secrets(
|
|
|
|
|
State(vault): State<Arc<VaultCore>>,
|
|
|
|
|
Path(path): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let full_path = path;
|
|
|
|
|
|
|
|
|
|
match vault.split_path(&full_path) {
|
|
|
|
|
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
|
|
|
|
|
Some(engine) => match engine.list(&relative_path).await {
|
|
|
|
|
Ok(items) => {
|
|
|
|
|
let response = ApiResponse::success(json!({"keys": items}));
|
|
|
|
|
(StatusCode::OK, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let response = ApiResponse::<Value>::error(format!("Failed to list: {}", e));
|
|
|
|
|
(StatusCode::BAD_REQUEST, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("No engine mounted at this path");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let response = ApiResponse::<Value>::error("Path not found");
|
|
|
|
|
(StatusCode::NOT_FOUND, Json(response)).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|