fix(secrets-mcp 0.5.20): code review plan — export secret types, env map, rollback, API key, MCP tools, web session & validation
- Export/import: optional secret_types map; AddResult includes entry_id - env_map: dot→__ segment encoding; collision errors - rollback: FOR UPDATE + txn-consistent snapshot; restore name from history - regenerate_api_key: rows_affected guard - MCP: find count propagates errors; add uses entry_id for relations; rollback no encryption key - Web: load_session_user_strict + JSON handlers key_version; PATCH length limits - Tests: ExportEntry serde, env segment
This commit is contained in:
@@ -11,7 +11,7 @@ use secrets_core::service::{
|
||||
|
||||
use crate::AppState;
|
||||
|
||||
use super::{SESSION_KEY_VERSION, current_user_id, render_template, require_valid_user};
|
||||
use super::{SESSION_KEY_VERSION, load_session_user_strict, render_template, require_valid_user};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "dashboard.html")]
|
||||
@@ -92,17 +92,11 @@ pub(super) async fn api_key_salt(
|
||||
State(state): State<AppState>,
|
||||
session: Session,
|
||||
) -> Result<Json<KeySaltResponse>, StatusCode> {
|
||||
let user_id = current_user_id(&session)
|
||||
.await
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let user = get_user_by_id(&state.pool, user_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, %user_id, "failed to load user for key-salt API");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
let user = match load_session_user_strict(&state.pool, &session).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return Err(StatusCode::UNAUTHORIZED),
|
||||
Err(()) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
if user.key_salt.is_none() {
|
||||
return Ok(Json(KeySaltResponse {
|
||||
@@ -126,19 +120,14 @@ pub(super) async fn api_key_setup(
|
||||
session: Session,
|
||||
Json(body): Json<KeySetupRequest>,
|
||||
) -> Result<Json<KeySetupResponse>, StatusCode> {
|
||||
let user_id = current_user_id(&session)
|
||||
.await
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
let user = match load_session_user_strict(&state.pool, &session).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return Err(StatusCode::UNAUTHORIZED),
|
||||
Err(()) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
let user_id = user.id;
|
||||
|
||||
// Guard: if a passphrase is already configured, reject and direct to /api/key-change
|
||||
let user = get_user_by_id(&state.pool, user_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, %user_id, "failed to load user for key-setup guard");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
if user.key_salt.is_some() {
|
||||
tracing::warn!(%user_id, "key-setup called but passphrase already configured; use /api/key-change");
|
||||
return Err(StatusCode::CONFLICT);
|
||||
@@ -175,17 +164,12 @@ pub(super) async fn api_key_change(
|
||||
session: Session,
|
||||
Json(body): Json<KeyChangeRequest>,
|
||||
) -> Result<Json<KeySetupResponse>, StatusCode> {
|
||||
let user_id = current_user_id(&session)
|
||||
.await
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let user = get_user_by_id(&state.pool, user_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, %user_id, "failed to load user for key-change");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
let user = match load_session_user_strict(&state.pool, &session).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return Err(StatusCode::UNAUTHORIZED),
|
||||
Err(()) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
let user_id = user.id;
|
||||
|
||||
// Must have an existing passphrase to change
|
||||
let existing_key_check = user.key_check.ok_or_else(|| {
|
||||
@@ -276,9 +260,12 @@ pub(super) async fn api_apikey_get(
|
||||
State(state): State<AppState>,
|
||||
session: Session,
|
||||
) -> Result<Json<ApiKeyResponse>, StatusCode> {
|
||||
let user_id = current_user_id(&session)
|
||||
.await
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
let user = match load_session_user_strict(&state.pool, &session).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return Err(StatusCode::UNAUTHORIZED),
|
||||
Err(()) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
let user_id = user.id;
|
||||
|
||||
let api_key = ensure_api_key(&state.pool, user_id).await.map_err(|e| {
|
||||
tracing::error!(error = %e, %user_id, "ensure_api_key failed");
|
||||
@@ -292,9 +279,12 @@ pub(super) async fn api_apikey_regenerate(
|
||||
State(state): State<AppState>,
|
||||
session: Session,
|
||||
) -> Result<Json<ApiKeyResponse>, StatusCode> {
|
||||
let user_id = current_user_id(&session)
|
||||
.await
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
let user = match load_session_user_strict(&state.pool, &session).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return Err(StatusCode::UNAUTHORIZED),
|
||||
Err(()) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
let user_id = user.id;
|
||||
|
||||
let api_key = regenerate_api_key(&state.pool, user_id)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user