fix: add /api/ciphers endpoint and fix ciphers model
This commit is contained in:
parent
b591e363b4
commit
1d45beebe2
3 changed files with 75 additions and 1 deletions
|
|
@ -176,3 +176,70 @@ pub async fn delete_cipher(
|
||||||
|
|
||||||
Ok(Json(()))
|
Ok(Json(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handler for POST /api/ciphers
|
||||||
|
/// Accepts flat JSON structure (camelCase) as sent by Bitwarden clients
|
||||||
|
/// when creating a cipher without collection assignments.
|
||||||
|
#[worker::send]
|
||||||
|
pub async fn create_cipher_simple(
|
||||||
|
claims: Claims,
|
||||||
|
State(env): State<Arc<Env>>,
|
||||||
|
Json(payload): Json<CipherRequestData>,
|
||||||
|
) -> Result<Json<Cipher>, AppError> {
|
||||||
|
let db = db::get_db(&env)?;
|
||||||
|
let now = Utc::now();
|
||||||
|
let now = now.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||||
|
|
||||||
|
let cipher_data = CipherData {
|
||||||
|
name: payload.name,
|
||||||
|
notes: payload.notes,
|
||||||
|
login: payload.login,
|
||||||
|
card: payload.card,
|
||||||
|
identity: payload.identity,
|
||||||
|
secure_note: payload.secure_note,
|
||||||
|
fields: payload.fields,
|
||||||
|
password_history: payload.password_history,
|
||||||
|
reprompt: payload.reprompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
let data_value = serde_json::to_value(&cipher_data).map_err(|_| AppError::Internal)?;
|
||||||
|
|
||||||
|
let cipher = Cipher {
|
||||||
|
id: Uuid::new_v4().to_string(),
|
||||||
|
user_id: Some(claims.sub.clone()),
|
||||||
|
organization_id: payload.organization_id.clone(),
|
||||||
|
r#type: payload.r#type,
|
||||||
|
data: data_value,
|
||||||
|
favorite: payload.favorite,
|
||||||
|
folder_id: payload.folder_id.clone(),
|
||||||
|
deleted_at: None,
|
||||||
|
created_at: now.clone(),
|
||||||
|
updated_at: now.clone(),
|
||||||
|
object: "cipher".to_string(),
|
||||||
|
organization_use_totp: false,
|
||||||
|
edit: true,
|
||||||
|
view_password: true,
|
||||||
|
collection_ids: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = serde_json::to_string(&cipher.data).map_err(|_| AppError::Internal)?;
|
||||||
|
|
||||||
|
query!(
|
||||||
|
&db,
|
||||||
|
"INSERT INTO ciphers (id, user_id, organization_id, type, data, favorite, folder_id, created_at, updated_at)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||||
|
cipher.id,
|
||||||
|
cipher.user_id,
|
||||||
|
cipher.organization_id,
|
||||||
|
cipher.r#type,
|
||||||
|
data,
|
||||||
|
cipher.favorite,
|
||||||
|
cipher.folder_id,
|
||||||
|
cipher.created_at,
|
||||||
|
cipher.updated_at,
|
||||||
|
).map_err(|_| AppError::Database)?
|
||||||
|
.run()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(cipher))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -277,13 +277,19 @@ pub struct CipherRequestData {
|
||||||
pub reprompt: Option<i32>,
|
pub reprompt: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub last_known_revision_date: Option<String>,
|
pub last_known_revision_date: Option<String>,
|
||||||
|
/// Cipher key field used for cipher key rotation scenarios
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents the full request payload for creating a cipher.
|
// Represents the full request payload for creating a cipher.
|
||||||
|
// Supports both camelCase and PascalCase for compatibility with different clients.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateCipherRequest {
|
pub struct CreateCipherRequest {
|
||||||
|
#[serde(alias = "Cipher")]
|
||||||
pub cipher: CipherRequestData,
|
pub cipher: CipherRequestData,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[serde(alias = "CollectionIds")]
|
||||||
pub collection_ids: Vec<String>,
|
pub collection_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub fn api_router(env: Env) -> Router {
|
||||||
// Main data sync route
|
// Main data sync route
|
||||||
.route("/api/sync", get(sync::get_sync_data))
|
.route("/api/sync", get(sync::get_sync_data))
|
||||||
// Ciphers CRUD
|
// Ciphers CRUD
|
||||||
|
.route("/api/ciphers", post(ciphers::create_cipher_simple))
|
||||||
.route("/api/ciphers/create", post(ciphers::create_cipher))
|
.route("/api/ciphers/create", post(ciphers::create_cipher))
|
||||||
.route("/api/ciphers/import", post(import::import_data))
|
.route("/api/ciphers/import", post(import::import_data))
|
||||||
.route("/api/ciphers/{id}", put(ciphers::update_cipher))
|
.route("/api/ciphers/{id}", put(ciphers::update_cipher))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue