Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2025-01-07 11:45:40 +01:00
introduce cipher_id newtype
Dieser Commit ist enthalten in:
Ursprung
189fd77806
Commit
8b8507f8cc
15 geänderte Dateien mit 240 neuen und 150 gelöschten Zeilen
|
@ -488,13 +488,13 @@ fn validate_keydata(
|
|||
existing_sends: &[Send],
|
||||
) -> EmptyResult {
|
||||
// Check that we're correctly rotating all the user's ciphers
|
||||
let existing_cipher_ids = existing_ciphers.iter().map(|c| c.uuid.as_str()).collect::<HashSet<_>>();
|
||||
let existing_cipher_ids = existing_ciphers.iter().map(|c| &c.uuid).collect::<HashSet<&CipherId>>();
|
||||
let provided_cipher_ids = data
|
||||
.ciphers
|
||||
.iter()
|
||||
.filter(|c| c.organization_id.is_none())
|
||||
.filter_map(|c| c.id.as_deref())
|
||||
.collect::<HashSet<_>>();
|
||||
.filter_map(|c| c.id.as_ref())
|
||||
.collect::<HashSet<&CipherId>>();
|
||||
if !provided_cipher_ids.is_superset(&existing_cipher_ids) {
|
||||
err!("All existing ciphers must be included in the rotation")
|
||||
}
|
||||
|
|
|
@ -192,8 +192,8 @@ async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json<Value> {
|
|||
}
|
||||
|
||||
#[get("/ciphers/<uuid>")]
|
||||
async fn get_cipher(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
async fn get_cipher(uuid: CipherId, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -205,13 +205,13 @@ async fn get_cipher(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResul
|
|||
}
|
||||
|
||||
#[get("/ciphers/<uuid>/admin")]
|
||||
async fn get_cipher_admin(uuid: &str, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn get_cipher_admin(uuid: CipherId, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
// TODO: Implement this correctly
|
||||
get_cipher(uuid, headers, conn).await
|
||||
}
|
||||
|
||||
#[get("/ciphers/<uuid>/details")]
|
||||
async fn get_cipher_details(uuid: &str, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn get_cipher_details(uuid: CipherId, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
get_cipher(uuid, headers, conn).await
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ async fn get_cipher_details(uuid: &str, headers: Headers, conn: DbConn) -> JsonR
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CipherData {
|
||||
// Id is optional as it is included only in bulk share
|
||||
pub id: Option<String>,
|
||||
pub id: Option<CipherId>,
|
||||
// Folder id is not included in import
|
||||
pub folder_id: Option<String>,
|
||||
// TODO: Some of these might appear all the time, no need for Option
|
||||
|
@ -256,7 +256,7 @@ pub struct CipherData {
|
|||
// 'Attachments' is unused, contains map of {id: filename}
|
||||
#[allow(dead_code)]
|
||||
attachments: Option<Value>,
|
||||
attachments2: Option<HashMap<String, Attachments2Data>>,
|
||||
attachments2: Option<HashMap<CipherId, Attachments2Data>>,
|
||||
|
||||
// The revision datetime (in ISO 8601 format) of the client's local copy
|
||||
// of the cipher. This is used to prevent a client from updating a cipher
|
||||
|
@ -620,7 +620,7 @@ async fn post_ciphers_import(
|
|||
/// Called when an org admin modifies an existing org cipher.
|
||||
#[put("/ciphers/<uuid>/admin", data = "<data>")]
|
||||
async fn put_cipher_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -631,7 +631,7 @@ async fn put_cipher_admin(
|
|||
|
||||
#[post("/ciphers/<uuid>/admin", data = "<data>")]
|
||||
async fn post_cipher_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -641,13 +641,19 @@ async fn post_cipher_admin(
|
|||
}
|
||||
|
||||
#[post("/ciphers/<uuid>", data = "<data>")]
|
||||
async fn post_cipher(uuid: &str, data: Json<CipherData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
|
||||
async fn post_cipher(
|
||||
uuid: CipherId,
|
||||
data: Json<CipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> JsonResult {
|
||||
put_cipher(uuid, data, headers, conn, nt).await
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>", data = "<data>")]
|
||||
async fn put_cipher(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CipherData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -655,7 +661,7 @@ async fn put_cipher(
|
|||
) -> JsonResult {
|
||||
let data: CipherData = data.into_inner();
|
||||
|
||||
let Some(mut cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
let Some(mut cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -674,21 +680,26 @@ async fn put_cipher(
|
|||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/partial", data = "<data>")]
|
||||
async fn post_cipher_partial(uuid: &str, data: Json<PartialCipherData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn post_cipher_partial(
|
||||
uuid: CipherId,
|
||||
data: Json<PartialCipherData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
put_cipher_partial(uuid, data, headers, conn).await
|
||||
}
|
||||
|
||||
// Only update the folder and favorite for the user, since this cipher is read-only
|
||||
#[put("/ciphers/<uuid>/partial", data = "<data>")]
|
||||
async fn put_cipher_partial(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<PartialCipherData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: PartialCipherData = data.into_inner();
|
||||
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -715,7 +726,7 @@ struct CollectionsAdminData {
|
|||
|
||||
#[put("/ciphers/<uuid>/collections_v2", data = "<data>")]
|
||||
async fn put_collections2_update(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -726,7 +737,7 @@ async fn put_collections2_update(
|
|||
|
||||
#[post("/ciphers/<uuid>/collections_v2", data = "<data>")]
|
||||
async fn post_collections2_update(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -742,7 +753,7 @@ async fn post_collections2_update(
|
|||
|
||||
#[put("/ciphers/<uuid>/collections", data = "<data>")]
|
||||
async fn put_collections_update(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -753,7 +764,7 @@ async fn put_collections_update(
|
|||
|
||||
#[post("/ciphers/<uuid>/collections", data = "<data>")]
|
||||
async fn post_collections_update(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -761,7 +772,7 @@ async fn post_collections_update(
|
|||
) -> JsonResult {
|
||||
let data: CollectionsAdminData = data.into_inner();
|
||||
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -771,7 +782,7 @@ async fn post_collections_update(
|
|||
|
||||
let posted_collections = HashSet::<CollectionId>::from_iter(data.collection_ids);
|
||||
let current_collections =
|
||||
HashSet::<CollectionId>::from_iter(cipher.get_collections(headers.user.uuid.to_string(), &mut conn).await);
|
||||
HashSet::<CollectionId>::from_iter(cipher.get_collections(headers.user.uuid.clone(), &mut conn).await);
|
||||
|
||||
for collection in posted_collections.symmetric_difference(¤t_collections) {
|
||||
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
|
||||
|
@ -819,7 +830,7 @@ async fn post_collections_update(
|
|||
|
||||
#[put("/ciphers/<uuid>/collections-admin", data = "<data>")]
|
||||
async fn put_collections_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -830,7 +841,7 @@ async fn put_collections_admin(
|
|||
|
||||
#[post("/ciphers/<uuid>/collections-admin", data = "<data>")]
|
||||
async fn post_collections_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<CollectionsAdminData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -838,7 +849,7 @@ async fn post_collections_admin(
|
|||
) -> EmptyResult {
|
||||
let data: CollectionsAdminData = data.into_inner();
|
||||
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -847,9 +858,8 @@ async fn post_collections_admin(
|
|||
}
|
||||
|
||||
let posted_collections = HashSet::<CollectionId>::from_iter(data.collection_ids);
|
||||
let current_collections = HashSet::<CollectionId>::from_iter(
|
||||
cipher.get_admin_collections(headers.user.uuid.to_string(), &mut conn).await,
|
||||
);
|
||||
let current_collections =
|
||||
HashSet::<CollectionId>::from_iter(cipher.get_admin_collections(headers.user.uuid.clone(), &mut conn).await);
|
||||
|
||||
for collection in posted_collections.symmetric_difference(¤t_collections) {
|
||||
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
|
||||
|
@ -906,7 +916,7 @@ struct ShareCipherData {
|
|||
|
||||
#[post("/ciphers/<uuid>/share", data = "<data>")]
|
||||
async fn post_cipher_share(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<ShareCipherData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -914,12 +924,12 @@ async fn post_cipher_share(
|
|||
) -> JsonResult {
|
||||
let data: ShareCipherData = data.into_inner();
|
||||
|
||||
share_cipher_by_uuid(uuid, data, &headers, &mut conn, &nt).await
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/share", data = "<data>")]
|
||||
async fn put_cipher_share(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<ShareCipherData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -927,7 +937,7 @@ async fn put_cipher_share(
|
|||
) -> JsonResult {
|
||||
let data: ShareCipherData = data.into_inner();
|
||||
|
||||
share_cipher_by_uuid(uuid, data, &headers, &mut conn, &nt).await
|
||||
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -976,7 +986,7 @@ async fn put_cipher_share_selected(
|
|||
}
|
||||
|
||||
async fn share_cipher_by_uuid(
|
||||
uuid: &str,
|
||||
uuid: &CipherId,
|
||||
data: ShareCipherData,
|
||||
headers: &Headers,
|
||||
conn: &mut DbConn,
|
||||
|
@ -1030,8 +1040,8 @@ async fn share_cipher_by_uuid(
|
|||
/// their object storage service. For self-hosted instances, it basically just
|
||||
/// redirects to the same location as before the v2 API.
|
||||
#[get("/ciphers/<uuid>/attachment/<attachment_id>")]
|
||||
async fn get_attachment(uuid: &str, attachment_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
async fn get_attachment(uuid: CipherId, attachment_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -1066,12 +1076,12 @@ enum FileUploadType {
|
|||
/// For self-hosted instances, it's another API on the local instance.
|
||||
#[post("/ciphers/<uuid>/attachment/v2", data = "<data>")]
|
||||
async fn post_attachment_v2(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Json<AttachmentRequestData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -1121,7 +1131,7 @@ struct UploadData<'f> {
|
|||
/// database record, which is passed in as `attachment`.
|
||||
async fn save_attachment(
|
||||
mut attachment: Option<Attachment>,
|
||||
cipher_uuid: &str,
|
||||
cipher_uuid: CipherId,
|
||||
data: Form<UploadData<'_>>,
|
||||
headers: &Headers,
|
||||
mut conn: DbConn,
|
||||
|
@ -1136,7 +1146,7 @@ async fn save_attachment(
|
|||
err!("Attachment size can't be negative")
|
||||
}
|
||||
|
||||
let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &mut conn).await else {
|
||||
let Some(cipher) = Cipher::find_by_uuid(&cipher_uuid, &mut conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
||||
|
@ -1250,11 +1260,11 @@ async fn save_attachment(
|
|||
err!("No attachment key provided")
|
||||
}
|
||||
let attachment =
|
||||
Attachment::new(file_id.clone(), String::from(cipher_uuid), encrypted_filename.unwrap(), size, data.key);
|
||||
Attachment::new(file_id.clone(), cipher_uuid.clone(), encrypted_filename.unwrap(), size, data.key);
|
||||
attachment.save(&mut conn).await.expect("Error saving attachment");
|
||||
}
|
||||
|
||||
let folder_path = tokio::fs::canonicalize(&CONFIG.attachments_folder()).await?.join(cipher_uuid);
|
||||
let folder_path = tokio::fs::canonicalize(&CONFIG.attachments_folder()).await?.join(cipher_uuid.as_ref());
|
||||
let file_path = folder_path.join(&file_id);
|
||||
tokio::fs::create_dir_all(&folder_path).await?;
|
||||
|
||||
|
@ -1294,7 +1304,7 @@ async fn save_attachment(
|
|||
/// with this one.
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>", format = "multipart/form-data", data = "<data>", rank = 1)]
|
||||
async fn post_attachment_v2_data(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
data: Form<UploadData<'_>>,
|
||||
headers: Headers,
|
||||
|
@ -1315,7 +1325,7 @@ async fn post_attachment_v2_data(
|
|||
/// Legacy API for creating an attachment associated with a cipher.
|
||||
#[post("/ciphers/<uuid>/attachment", format = "multipart/form-data", data = "<data>")]
|
||||
async fn post_attachment(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Form<UploadData<'_>>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -1332,7 +1342,7 @@ async fn post_attachment(
|
|||
|
||||
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
|
||||
async fn post_attachment_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
data: Form<UploadData<'_>>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -1343,20 +1353,20 @@ async fn post_attachment_admin(
|
|||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
|
||||
async fn post_attachment_share(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
data: Form<UploadData<'_>>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> JsonResult {
|
||||
_delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await?;
|
||||
_delete_cipher_attachment_by_id(&uuid, attachment_id, &headers, &mut conn, &nt).await?;
|
||||
post_attachment(uuid, data, headers, conn, nt).await
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
|
||||
async fn delete_attachment_post_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -1367,7 +1377,7 @@ async fn delete_attachment_post_admin(
|
|||
|
||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
|
||||
async fn delete_attachment_post(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
|
@ -1378,58 +1388,58 @@ async fn delete_attachment_post(
|
|||
|
||||
#[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
|
||||
async fn delete_attachment(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await
|
||||
_delete_cipher_attachment_by_id(&uuid, attachment_id, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")]
|
||||
async fn delete_attachment_admin(
|
||||
uuid: &str,
|
||||
uuid: CipherId,
|
||||
attachment_id: &str,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> EmptyResult {
|
||||
_delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await
|
||||
_delete_cipher_attachment_by_id(&uuid, attachment_id, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/delete")]
|
||||
async fn delete_cipher_post(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await
|
||||
async fn delete_cipher_post(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
|
||||
// permanent delete
|
||||
}
|
||||
|
||||
#[post("/ciphers/<uuid>/delete-admin")]
|
||||
async fn delete_cipher_post_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await
|
||||
async fn delete_cipher_post_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
|
||||
// permanent delete
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/delete")]
|
||||
async fn delete_cipher_put(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, true, &nt).await
|
||||
async fn delete_cipher_put(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await
|
||||
// soft delete
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/delete-admin")]
|
||||
async fn delete_cipher_put_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, true, &nt).await
|
||||
async fn delete_cipher_put_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>")]
|
||||
async fn delete_cipher(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await
|
||||
async fn delete_cipher(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
|
||||
// permanent delete
|
||||
}
|
||||
|
||||
#[delete("/ciphers/<uuid>/admin")]
|
||||
async fn delete_cipher_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await
|
||||
async fn delete_cipher_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
|
||||
// permanent delete
|
||||
}
|
||||
|
||||
|
@ -1494,13 +1504,13 @@ async fn delete_cipher_selected_put_admin(
|
|||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/restore")]
|
||||
async fn restore_cipher_put(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
|
||||
_restore_cipher_by_uuid(uuid, &headers, &mut conn, &nt).await
|
||||
async fn restore_cipher_put(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
|
||||
_restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[put("/ciphers/<uuid>/restore-admin")]
|
||||
async fn restore_cipher_put_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
|
||||
_restore_cipher_by_uuid(uuid, &headers, &mut conn, &nt).await
|
||||
async fn restore_cipher_put_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
|
||||
_restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await
|
||||
}
|
||||
|
||||
#[put("/ciphers/restore", data = "<data>")]
|
||||
|
@ -1517,7 +1527,7 @@ async fn restore_cipher_selected(
|
|||
#[serde(rename_all = "camelCase")]
|
||||
struct MoveCipherData {
|
||||
folder_id: Option<String>,
|
||||
ids: Vec<String>,
|
||||
ids: Vec<CipherId>,
|
||||
}
|
||||
|
||||
#[post("/ciphers/move", data = "<data>")]
|
||||
|
@ -1640,7 +1650,7 @@ async fn delete_all(
|
|||
}
|
||||
|
||||
async fn _delete_cipher_by_uuid(
|
||||
uuid: &str,
|
||||
uuid: &CipherId,
|
||||
headers: &Headers,
|
||||
conn: &mut DbConn,
|
||||
soft_delete: bool,
|
||||
|
@ -1695,7 +1705,7 @@ async fn _delete_cipher_by_uuid(
|
|||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CipherIdsData {
|
||||
ids: Vec<String>,
|
||||
ids: Vec<CipherId>,
|
||||
}
|
||||
|
||||
async fn _delete_multiple_ciphers(
|
||||
|
@ -1716,7 +1726,7 @@ async fn _delete_multiple_ciphers(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult {
|
||||
async fn _restore_cipher_by_uuid(uuid: &CipherId, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult {
|
||||
let Some(mut cipher) = Cipher::find_by_uuid(uuid, conn).await else {
|
||||
err!("Cipher doesn't exist")
|
||||
};
|
||||
|
@ -1778,7 +1788,7 @@ async fn _restore_multiple_ciphers(
|
|||
}
|
||||
|
||||
async fn _delete_cipher_attachment_by_id(
|
||||
uuid: &str,
|
||||
uuid: &CipherId,
|
||||
attachment_id: &str,
|
||||
headers: &Headers,
|
||||
conn: &mut DbConn,
|
||||
|
@ -1788,7 +1798,7 @@ async fn _delete_cipher_attachment_by_id(
|
|||
err!("Attachment doesn't exist")
|
||||
};
|
||||
|
||||
if attachment.cipher_uuid != uuid {
|
||||
if &attachment.cipher_uuid != uuid {
|
||||
err!("Attachment from other cipher")
|
||||
}
|
||||
|
||||
|
@ -1832,10 +1842,10 @@ async fn _delete_cipher_attachment_by_id(
|
|||
/// It will prevent the so called N+1 SQL issue by running just a few queries which will hold all the data needed.
|
||||
/// This will not improve the speed of a single cipher.to_json() call that much, so better not to use it for those calls.
|
||||
pub struct CipherSyncData {
|
||||
pub cipher_attachments: HashMap<String, Vec<Attachment>>,
|
||||
pub cipher_folders: HashMap<String, String>,
|
||||
pub cipher_favorites: HashSet<String>,
|
||||
pub cipher_collections: HashMap<String, Vec<CollectionId>>,
|
||||
pub cipher_attachments: HashMap<CipherId, Vec<Attachment>>,
|
||||
pub cipher_folders: HashMap<CipherId, String>,
|
||||
pub cipher_favorites: HashSet<CipherId>,
|
||||
pub cipher_collections: HashMap<CipherId, Vec<CollectionId>>,
|
||||
pub members: HashMap<OrganizationId, Membership>,
|
||||
pub user_collections: HashMap<CollectionId, CollectionUser>,
|
||||
pub user_collections_groups: HashMap<CollectionId, CollectionGroup>,
|
||||
|
@ -1850,8 +1860,8 @@ pub enum CipherSyncType {
|
|||
|
||||
impl CipherSyncData {
|
||||
pub async fn new(user_uuid: &UserId, sync_type: CipherSyncType, conn: &mut DbConn) -> Self {
|
||||
let cipher_folders: HashMap<String, String>;
|
||||
let cipher_favorites: HashSet<String>;
|
||||
let cipher_folders: HashMap<CipherId, String>;
|
||||
let cipher_favorites: HashSet<CipherId>;
|
||||
match sync_type {
|
||||
// User Sync supports Folders and Favorites
|
||||
CipherSyncType::User => {
|
||||
|
@ -1872,14 +1882,14 @@ impl CipherSyncData {
|
|||
// Generate a list of Cipher UUID's containing a Vec with one or more Attachment records
|
||||
let orgs = Membership::get_orgs_by_user(user_uuid, conn).await;
|
||||
let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &orgs, conn).await;
|
||||
let mut cipher_attachments: HashMap<String, Vec<Attachment>> = HashMap::with_capacity(attachments.len());
|
||||
let mut cipher_attachments: HashMap<CipherId, Vec<Attachment>> = HashMap::with_capacity(attachments.len());
|
||||
for attachment in attachments {
|
||||
cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment);
|
||||
}
|
||||
|
||||
// Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's
|
||||
let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await;
|
||||
let mut cipher_collections: HashMap<String, Vec<CollectionId>> =
|
||||
let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.clone(), conn).await;
|
||||
let mut cipher_collections: HashMap<CipherId, Vec<CollectionId>> =
|
||||
HashMap::with_capacity(user_cipher_collections.len());
|
||||
for (cipher, collection) in user_cipher_collections {
|
||||
cipher_collections.entry(cipher).or_default().push(collection);
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
api::{EmptyResult, JsonResult},
|
||||
auth::{AdminHeaders, Headers},
|
||||
db::{
|
||||
models::{Cipher, Event, Membership, MembershipId, OrganizationId, UserId},
|
||||
models::{Cipher, CipherId, Event, Membership, MembershipId, OrganizationId, UserId},
|
||||
DbConn, DbPool,
|
||||
},
|
||||
util::parse_date,
|
||||
|
@ -59,14 +59,14 @@ async fn get_org_events(org_id: &str, data: EventRange, _headers: AdminHeaders,
|
|||
}
|
||||
|
||||
#[get("/ciphers/<cipher_id>/events?<data..>")]
|
||||
async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_cipher_events(cipher_id: CipherId, data: EventRange, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
// Return an empty vec when we org events are disabled.
|
||||
// This prevents client errors
|
||||
let events_json: Vec<Value> = if !CONFIG.org_events_enabled() {
|
||||
Vec::with_capacity(0)
|
||||
} else {
|
||||
let mut events_json = Vec::with_capacity(0);
|
||||
if Membership::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await {
|
||||
if Membership::user_has_ge_admin_access_to_cipher(&headers.user.uuid, &cipher_id, &mut conn).await {
|
||||
let start_date = parse_date(&data.start);
|
||||
let end_date = if let Some(before_date) = &data.continuation_token {
|
||||
parse_date(before_date)
|
||||
|
@ -74,7 +74,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
|
|||
parse_date(&data.end)
|
||||
};
|
||||
|
||||
events_json = Event::find_by_cipher_uuid(cipher_id, &start_date, &end_date, &mut conn)
|
||||
events_json = Event::find_by_cipher_uuid(&cipher_id, &start_date, &end_date, &mut conn)
|
||||
.await
|
||||
.iter()
|
||||
.map(|e| e.to_json())
|
||||
|
@ -152,7 +152,7 @@ struct EventCollection {
|
|||
date: String,
|
||||
|
||||
// Optional
|
||||
cipher_id: Option<String>,
|
||||
cipher_id: Option<CipherId>,
|
||||
organization_id: Option<OrganizationId>,
|
||||
}
|
||||
|
||||
|
@ -290,19 +290,19 @@ async fn _log_event(
|
|||
// 1000..=1099 Are user events, they need to be logged via log_user_event()
|
||||
// Cipher Events
|
||||
1100..=1199 => {
|
||||
event.cipher_uuid = Some(String::from(source_uuid));
|
||||
event.cipher_uuid = Some(source_uuid.to_string().into());
|
||||
}
|
||||
// Collection Events
|
||||
1300..=1399 => {
|
||||
event.collection_uuid = Some(String::from(source_uuid));
|
||||
event.collection_uuid = Some(source_uuid.to_string().into());
|
||||
}
|
||||
// Group Events
|
||||
1400..=1499 => {
|
||||
event.group_uuid = Some(String::from(source_uuid));
|
||||
event.group_uuid = Some(source_uuid.to_string().into());
|
||||
}
|
||||
// Org User Events
|
||||
1500..=1599 => {
|
||||
event.org_user_uuid = Some(String::from(source_uuid));
|
||||
event.org_user_uuid = Some(source_uuid.to_string().into());
|
||||
}
|
||||
// 1600..=1699 Are organizational events, and they do not need the source_uuid
|
||||
// Policy Events
|
||||
|
|
|
@ -1645,7 +1645,7 @@ async fn post_org_import(
|
|||
|
||||
let headers: Headers = headers.into();
|
||||
|
||||
let mut ciphers: Vec<String> = Vec::with_capacity(data.ciphers.len());
|
||||
let mut ciphers: Vec<CipherId> = Vec::with_capacity(data.ciphers.len());
|
||||
for mut cipher_data in data.ciphers {
|
||||
// Always clear folder_id's via an organization import
|
||||
cipher_data.folder_id = None;
|
||||
|
@ -1670,7 +1670,7 @@ async fn post_org_import(
|
|||
#[allow(dead_code)]
|
||||
struct BulkCollectionsData {
|
||||
organization_id: OrganizationId,
|
||||
cipher_ids: Vec<String>,
|
||||
cipher_ids: Vec<CipherId>,
|
||||
collection_ids: HashSet<CollectionId>,
|
||||
remove_collections: bool,
|
||||
}
|
||||
|
@ -2659,7 +2659,7 @@ async fn get_group_users(
|
|||
err!("Group support is disabled");
|
||||
}
|
||||
|
||||
if Group::find_by_uuid_and_org(&&group_id, &org_id, &mut conn).await.is_none() {
|
||||
if Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await.is_none() {
|
||||
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
|
||||
};
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ impl WebSocketUsers {
|
|||
|
||||
let data = create_update(
|
||||
vec![
|
||||
("Id".into(), cipher.uuid.clone().into()),
|
||||
("Id".into(), cipher.uuid.to_string().into()),
|
||||
("UserId".into(), user_uuid),
|
||||
("OrganizationId".into(), org_uuid),
|
||||
("CollectionIds".into(), collection_uuids),
|
||||
|
|
|
@ -13,6 +13,7 @@ use serde_json::Value;
|
|||
use crate::{
|
||||
api::{core::now, ApiResult, EmptyResult},
|
||||
auth::decode_file_download,
|
||||
db::models::CipherId,
|
||||
error::Error,
|
||||
util::{get_web_vault_version, Cached, SafeString},
|
||||
CONFIG,
|
||||
|
@ -196,15 +197,15 @@ async fn web_files(p: PathBuf) -> Cached<Option<NamedFile>> {
|
|||
}
|
||||
|
||||
#[get("/attachments/<uuid>/<file_id>?<token>")]
|
||||
async fn attachments(uuid: SafeString, file_id: SafeString, token: String) -> Option<NamedFile> {
|
||||
async fn attachments(uuid: CipherId, file_id: SafeString, token: String) -> Option<NamedFile> {
|
||||
let Ok(claims) = decode_file_download(&token) else {
|
||||
return None;
|
||||
};
|
||||
if claims.sub != *uuid || claims.file_id != *file_id {
|
||||
if claims.sub != uuid || claims.file_id != *file_id {
|
||||
return None;
|
||||
}
|
||||
|
||||
NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid).join(file_id)).await.ok()
|
||||
NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid.as_ref()).join(file_id)).await.ok()
|
||||
}
|
||||
|
||||
// We use DbConn here to let the alive healthcheck also verify the database connection.
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::{
|
|||
net::IpAddr,
|
||||
};
|
||||
|
||||
use crate::db::models::{CollectionId, MembershipId, OrganizationId, UserId};
|
||||
use crate::db::models::{CipherId, CollectionId, MembershipId, OrganizationId, UserId};
|
||||
use crate::{error::Error, CONFIG};
|
||||
|
||||
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||
|
@ -293,12 +293,12 @@ pub struct FileDownloadClaims {
|
|||
// Issuer
|
||||
pub iss: String,
|
||||
// Subject
|
||||
pub sub: String,
|
||||
pub sub: CipherId,
|
||||
|
||||
pub file_id: String,
|
||||
}
|
||||
|
||||
pub fn generate_file_download_claims(uuid: String, file_id: String) -> FileDownloadClaims {
|
||||
pub fn generate_file_download_claims(uuid: CipherId, file_id: String) -> FileDownloadClaims {
|
||||
let time_now = Utc::now();
|
||||
FileDownloadClaims {
|
||||
nbf: time_now.timestamp(),
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::io::ErrorKind;
|
|||
use bigdecimal::{BigDecimal, ToPrimitive};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{OrganizationId, UserId};
|
||||
use super::{CipherId, OrganizationId, UserId};
|
||||
use crate::CONFIG;
|
||||
|
||||
db_object! {
|
||||
|
@ -13,7 +13,7 @@ db_object! {
|
|||
#[diesel(primary_key(id))]
|
||||
pub struct Attachment {
|
||||
pub id: String,
|
||||
pub cipher_uuid: String,
|
||||
pub cipher_uuid: CipherId,
|
||||
pub file_name: String, // encrypted
|
||||
pub file_size: i64,
|
||||
pub akey: Option<String>,
|
||||
|
@ -22,7 +22,13 @@ db_object! {
|
|||
|
||||
/// Local methods
|
||||
impl Attachment {
|
||||
pub const fn new(id: String, cipher_uuid: String, file_name: String, file_size: i64, akey: Option<String>) -> Self {
|
||||
pub const fn new(
|
||||
id: String,
|
||||
cipher_uuid: CipherId,
|
||||
file_name: String,
|
||||
file_size: i64,
|
||||
akey: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
cipher_uuid,
|
||||
|
@ -118,7 +124,7 @@ impl Attachment {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &mut DbConn) -> EmptyResult {
|
||||
for attachment in Attachment::find_by_cipher(cipher_uuid, conn).await {
|
||||
attachment.delete(conn).await?;
|
||||
}
|
||||
|
@ -135,7 +141,7 @@ impl Attachment {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
|
||||
pub async fn find_by_cipher(cipher_uuid: &CipherId, conn: &mut DbConn) -> Vec<Self> {
|
||||
db_run! { conn: {
|
||||
attachments::table
|
||||
.filter(attachments::cipher_uuid.eq(cipher_uuid))
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use crate::util::LowerCase;
|
||||
use crate::CONFIG;
|
||||
use chrono::{NaiveDateTime, TimeDelta, Utc};
|
||||
use rocket::request::FromParam;
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt::{Display, Formatter},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use super::{
|
||||
Attachment, CollectionCipher, CollectionId, Favorite, FolderCipher, Group, Membership, MembershipStatus,
|
||||
|
@ -18,7 +24,7 @@ db_object! {
|
|||
#[diesel(treat_none_as_null = true)]
|
||||
#[diesel(primary_key(uuid))]
|
||||
pub struct Cipher {
|
||||
pub uuid: String,
|
||||
pub uuid: CipherId,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
|
||||
|
@ -58,7 +64,7 @@ impl Cipher {
|
|||
let now = Utc::now().naive_utc();
|
||||
|
||||
Self {
|
||||
uuid: crate::util::get_uuid(),
|
||||
uuid: CipherId(crate::util::get_uuid()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
|
||||
|
@ -279,7 +285,7 @@ impl Cipher {
|
|||
Cow::from(Vec::with_capacity(0))
|
||||
}
|
||||
} else {
|
||||
Cow::from(self.get_admin_collections(user_uuid.to_string(), conn).await)
|
||||
Cow::from(self.get_admin_collections(user_uuid.clone(), conn).await)
|
||||
};
|
||||
|
||||
// There are three types of cipher response models in upstream
|
||||
|
@ -683,7 +689,7 @@ impl Cipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
||||
pub async fn find_by_uuid(uuid: &CipherId, conn: &mut DbConn) -> Option<Self> {
|
||||
db_run! {conn: {
|
||||
ciphers::table
|
||||
.filter(ciphers::uuid.eq(uuid))
|
||||
|
@ -693,7 +699,7 @@ impl Cipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_uuid_and_org(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
||||
pub async fn find_by_uuid_and_org(cipher_uuid: &CipherId, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
||||
db_run! {conn: {
|
||||
ciphers::table
|
||||
.filter(ciphers::uuid.eq(cipher_uuid))
|
||||
|
@ -862,7 +868,7 @@ impl Cipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<CollectionId> {
|
||||
pub async fn get_collections(&self, user_id: UserId, conn: &mut DbConn) -> Vec<CollectionId> {
|
||||
if CONFIG.org_groups_enabled() {
|
||||
db_run! {conn: {
|
||||
ciphers_collections::table
|
||||
|
@ -921,7 +927,7 @@ impl Cipher {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_admin_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<CollectionId> {
|
||||
pub async fn get_admin_collections(&self, user_id: UserId, conn: &mut DbConn) -> Vec<CollectionId> {
|
||||
if CONFIG.org_groups_enabled() {
|
||||
db_run! {conn: {
|
||||
ciphers_collections::table
|
||||
|
@ -985,9 +991,9 @@ impl Cipher {
|
|||
/// Return a Vec with (cipher_uuid, collection_uuid)
|
||||
/// This is used during a full sync so we only need one query for all collections accessible.
|
||||
pub async fn get_collections_with_cipher_by_user(
|
||||
user_id: String,
|
||||
user_id: UserId,
|
||||
conn: &mut DbConn,
|
||||
) -> Vec<(String, CollectionId)> {
|
||||
) -> Vec<(CipherId, CollectionId)> {
|
||||
db_run! {conn: {
|
||||
ciphers_collections::table
|
||||
.inner_join(collections::table.on(
|
||||
|
@ -1021,7 +1027,55 @@ impl Cipher {
|
|||
.or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group
|
||||
.select(ciphers_collections::all_columns)
|
||||
.distinct()
|
||||
.load::<(String, CollectionId)>(conn).unwrap_or_default()
|
||||
.load::<(CipherId, CollectionId)>(conn).unwrap_or_default()
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CipherId(String);
|
||||
|
||||
impl AsRef<str> for CipherId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CipherId {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for CipherId {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CipherId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CipherId {
|
||||
fn from(raw: String) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> FromParam<'r> for CipherId {
|
||||
type Error = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
|
||||
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
|
||||
Ok(Self(param.to_string()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ use std::{
|
|||
ops::Deref,
|
||||
};
|
||||
|
||||
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId};
|
||||
use super::{
|
||||
CipherId, CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId,
|
||||
};
|
||||
use crate::CONFIG;
|
||||
|
||||
db_object! {
|
||||
|
@ -34,7 +36,7 @@ db_object! {
|
|||
#[diesel(table_name = ciphers_collections)]
|
||||
#[diesel(primary_key(cipher_uuid, collection_uuid))]
|
||||
pub struct CollectionCipher {
|
||||
pub cipher_uuid: String,
|
||||
pub cipher_uuid: CipherId,
|
||||
pub collection_uuid: CollectionId,
|
||||
}
|
||||
}
|
||||
|
@ -710,7 +712,7 @@ impl CollectionUser {
|
|||
|
||||
/// Database methods
|
||||
impl CollectionCipher {
|
||||
pub async fn save(cipher_uuid: &str, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn save(cipher_uuid: &CipherId, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
|
||||
Self::update_users_revision(collection_uuid, conn).await;
|
||||
|
||||
db_run! { conn:
|
||||
|
@ -740,7 +742,7 @@ impl CollectionCipher {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn delete(cipher_uuid: &str, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn delete(cipher_uuid: &CipherId, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
|
||||
Self::update_users_revision(collection_uuid, conn).await;
|
||||
|
||||
db_run! { conn: {
|
||||
|
@ -754,7 +756,7 @@ impl CollectionCipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &mut DbConn) -> EmptyResult {
|
||||
db_run! { conn: {
|
||||
diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)))
|
||||
.execute(conn)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::db::DbConn;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{OrganizationId, UserId};
|
||||
use super::{CipherId, CollectionId, GroupId, MembershipId, OrganizationId, UserId};
|
||||
use crate::{api::EmptyResult, error::MapResult, CONFIG};
|
||||
|
||||
use chrono::{NaiveDateTime, TimeDelta, Utc};
|
||||
|
@ -20,10 +20,10 @@ db_object! {
|
|||
pub event_type: i32, // EventType
|
||||
pub user_uuid: Option<UserId>,
|
||||
pub org_uuid: Option<OrganizationId>,
|
||||
pub cipher_uuid: Option<String>,
|
||||
pub collection_uuid: Option<String>,
|
||||
pub group_uuid: Option<String>,
|
||||
pub org_user_uuid: Option<String>,
|
||||
pub cipher_uuid: Option<CipherId>,
|
||||
pub collection_uuid: Option<CollectionId>,
|
||||
pub group_uuid: Option<GroupId>,
|
||||
pub org_user_uuid: Option<MembershipId>,
|
||||
pub act_user_uuid: Option<String>,
|
||||
// Upstream enum: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Enums/DeviceType.cs
|
||||
pub device_type: Option<i32>,
|
||||
|
@ -298,7 +298,7 @@ impl Event {
|
|||
}
|
||||
|
||||
pub async fn find_by_cipher_uuid(
|
||||
cipher_uuid: &str,
|
||||
cipher_uuid: &CipherId,
|
||||
start: &NaiveDateTime,
|
||||
end: &NaiveDateTime,
|
||||
conn: &mut DbConn,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{User, UserId};
|
||||
use super::{CipherId, User, UserId};
|
||||
|
||||
db_object! {
|
||||
#[derive(Identifiable, Queryable, Insertable)]
|
||||
|
@ -6,7 +6,7 @@ db_object! {
|
|||
#[diesel(primary_key(user_uuid, cipher_uuid))]
|
||||
pub struct Favorite {
|
||||
pub user_uuid: UserId,
|
||||
pub cipher_uuid: String,
|
||||
pub cipher_uuid: CipherId,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ use crate::error::MapResult;
|
|||
|
||||
impl Favorite {
|
||||
// Returns whether the specified cipher is a favorite of the specified user.
|
||||
pub async fn is_favorite(cipher_uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> bool {
|
||||
pub async fn is_favorite(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &mut DbConn) -> bool {
|
||||
db_run! { conn: {
|
||||
let query = favorites::table
|
||||
.filter(favorites::cipher_uuid.eq(cipher_uuid))
|
||||
|
@ -29,7 +29,12 @@ impl Favorite {
|
|||
}
|
||||
|
||||
// Sets whether the specified cipher is a favorite of the specified user.
|
||||
pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn set_favorite(
|
||||
favorite: bool,
|
||||
cipher_uuid: &CipherId,
|
||||
user_uuid: &UserId,
|
||||
conn: &mut DbConn,
|
||||
) -> EmptyResult {
|
||||
let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite);
|
||||
match (old, new) {
|
||||
(false, true) => {
|
||||
|
@ -62,7 +67,7 @@ impl Favorite {
|
|||
}
|
||||
|
||||
// Delete all favorite entries associated with the specified cipher.
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &mut DbConn) -> EmptyResult {
|
||||
db_run! { conn: {
|
||||
diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid)))
|
||||
.execute(conn)
|
||||
|
@ -81,12 +86,12 @@ impl Favorite {
|
|||
|
||||
/// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers
|
||||
/// This is used during a full sync so we only need one query for all favorite cipher matches.
|
||||
pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<String> {
|
||||
pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<CipherId> {
|
||||
db_run! { conn: {
|
||||
favorites::table
|
||||
.filter(favorites::user_uuid.eq(user_uuid))
|
||||
.select(favorites::cipher_uuid)
|
||||
.load::<String>(conn)
|
||||
.load::<CipherId>(conn)
|
||||
.unwrap_or_default()
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use chrono::{NaiveDateTime, Utc};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{User, UserId};
|
||||
use super::{CipherId, User, UserId};
|
||||
|
||||
db_object! {
|
||||
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
|
||||
|
@ -19,7 +19,7 @@ db_object! {
|
|||
#[diesel(table_name = folders_ciphers)]
|
||||
#[diesel(primary_key(cipher_uuid, folder_uuid))]
|
||||
pub struct FolderCipher {
|
||||
pub cipher_uuid: String,
|
||||
pub cipher_uuid: CipherId,
|
||||
pub folder_uuid: String,
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,10 @@ impl Folder {
|
|||
}
|
||||
|
||||
impl FolderCipher {
|
||||
pub fn new(folder_uuid: &str, cipher_uuid: &str) -> Self {
|
||||
pub fn new(folder_uuid: &str, cipher_uuid: &CipherId) -> Self {
|
||||
Self {
|
||||
folder_uuid: folder_uuid.to_string(),
|
||||
cipher_uuid: cipher_uuid.to_string(),
|
||||
cipher_uuid: cipher_uuid.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl FolderCipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
||||
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &mut DbConn) -> EmptyResult {
|
||||
db_run! { conn: {
|
||||
diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)))
|
||||
.execute(conn)
|
||||
|
@ -193,7 +193,11 @@ impl FolderCipher {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
||||
pub async fn find_by_folder_and_cipher(
|
||||
folder_uuid: &str,
|
||||
cipher_uuid: &CipherId,
|
||||
conn: &mut DbConn,
|
||||
) -> Option<Self> {
|
||||
db_run! { conn: {
|
||||
folders_ciphers::table
|
||||
.filter(folders_ciphers::folder_uuid.eq(folder_uuid))
|
||||
|
@ -216,13 +220,13 @@ impl FolderCipher {
|
|||
|
||||
/// Return a vec with (cipher_uuid, folder_uuid)
|
||||
/// This is used during a full sync so we only need one query for all folder matches.
|
||||
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<(String, String)> {
|
||||
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<(CipherId, String)> {
|
||||
db_run! { conn: {
|
||||
folders_ciphers::table
|
||||
.inner_join(folders::table)
|
||||
.filter(folders::user_uuid.eq(user_uuid))
|
||||
.select(folders_ciphers::all_columns)
|
||||
.load::<(String, String)>(conn)
|
||||
.load::<(CipherId, String)>(conn)
|
||||
.unwrap_or_default()
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ mod user;
|
|||
|
||||
pub use self::attachment::Attachment;
|
||||
pub use self::auth_request::AuthRequest;
|
||||
pub use self::cipher::{Cipher, RepromptType};
|
||||
pub use self::cipher::{Cipher, CipherId, RepromptType};
|
||||
pub use self::collection::{Collection, CollectionCipher, CollectionId, CollectionUser};
|
||||
pub use self::device::{Device, DeviceType};
|
||||
pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType};
|
||||
|
|
|
@ -11,8 +11,8 @@ use std::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
Collection, CollectionGroup, CollectionId, CollectionUser, Group, GroupId, GroupUser, OrgPolicy, OrgPolicyType,
|
||||
TwoFactor, User, UserId,
|
||||
CipherId, Collection, CollectionGroup, CollectionId, CollectionUser, Group, GroupId, GroupUser, OrgPolicy,
|
||||
OrgPolicyType, TwoFactor, User, UserId,
|
||||
};
|
||||
use crate::CONFIG;
|
||||
|
||||
|
@ -897,7 +897,11 @@ impl Membership {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
|
||||
pub async fn find_by_cipher_and_org(
|
||||
cipher_uuid: &CipherId,
|
||||
org_uuid: &OrganizationId,
|
||||
conn: &mut DbConn,
|
||||
) -> Vec<Self> {
|
||||
db_run! { conn: {
|
||||
users_organizations::table
|
||||
.filter(users_organizations::org_uuid.eq(org_uuid))
|
||||
|
@ -921,7 +925,7 @@ impl Membership {
|
|||
}
|
||||
|
||||
pub async fn find_by_cipher_and_org_with_group(
|
||||
cipher_uuid: &str,
|
||||
cipher_uuid: &CipherId,
|
||||
org_uuid: &OrganizationId,
|
||||
conn: &mut DbConn,
|
||||
) -> Vec<Self> {
|
||||
|
@ -950,7 +954,11 @@ impl Membership {
|
|||
}}
|
||||
}
|
||||
|
||||
pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &UserId, cipher_uuid: &str, conn: &mut DbConn) -> bool {
|
||||
pub async fn user_has_ge_admin_access_to_cipher(
|
||||
user_uuid: &UserId,
|
||||
cipher_uuid: &CipherId,
|
||||
conn: &mut DbConn,
|
||||
) -> bool {
|
||||
db_run! { conn: {
|
||||
users_organizations::table
|
||||
.inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()))))
|
||||
|
|
Laden …
In neuem Issue referenzieren