1
0
Fork 1
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:
Stefan Melmuk 2024-12-21 15:03:30 +01:00
Ursprung 189fd77806
Commit 8b8507f8cc
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 817020C608FE9C09
15 geänderte Dateien mit 240 neuen und 150 gelöschten Zeilen

Datei anzeigen

@ -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")
}

Datei anzeigen

@ -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(&current_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(&current_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);

Datei anzeigen

@ -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

Datei anzeigen

@ -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")
};

Datei anzeigen

@ -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),

Datei anzeigen

@ -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.

Datei anzeigen

@ -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(),

Datei anzeigen

@ -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))

Datei anzeigen

@ -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(())
}
}
}

Datei anzeigen

@ -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)

Datei anzeigen

@ -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,

Datei anzeigen

@ -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()
}}
}

Datei anzeigen

@ -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()
}}
}

Datei anzeigen

@ -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};

Datei anzeigen

@ -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()))))