1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2025-01-08 11:55:42 +01:00

use id names more consistently

Dieser Commit ist enthalten in:
Stefan Melmuk 2024-12-24 00:55:32 +01:00
Ursprung 1397392699
Commit 6355985539
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 817020C608FE9C09
21 geänderte Dateien mit 442 neuen und 424 gelöschten Zeilen

Datei anzeigen

@ -280,8 +280,8 @@ struct InviteData {
email: String, email: String,
} }
async fn get_user_or_404(uuid: &UserId, conn: &mut DbConn) -> ApiResult<User> { async fn get_user_or_404(user_id: &UserId, conn: &mut DbConn) -> ApiResult<User> {
if let Some(user) = User::find_by_uuid(uuid, conn).await { if let Some(user) = User::find_by_uuid(user_id, conn).await {
Ok(user) Ok(user)
} else { } else {
err_code!("User doesn't exist", Status::NotFound.code); err_code!("User doesn't exist", Status::NotFound.code);
@ -381,21 +381,21 @@ async fn get_user_by_mail_json(mail: &str, _token: AdminToken, mut conn: DbConn)
} }
} }
#[get("/users/<uuid>")] #[get("/users/<user_id>")]
async fn get_user_json(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> JsonResult { async fn get_user_json(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> JsonResult {
let u = get_user_or_404(&uuid, &mut conn).await?; let u = get_user_or_404(&user_id, &mut conn).await?;
let mut usr = u.to_json(&mut conn).await; let mut usr = u.to_json(&mut conn).await;
usr["userEnabled"] = json!(u.enabled); usr["userEnabled"] = json!(u.enabled);
usr["createdAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); usr["createdAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT));
Ok(Json(usr)) Ok(Json(usr))
} }
#[post("/users/<uuid>/delete")] #[post("/users/<user_id>/delete")]
async fn delete_user(uuid: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn delete_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(&uuid, &mut conn).await?; let user = get_user_or_404(&user_id, &mut conn).await?;
// Get the membership records before deleting the actual user // Get the membership records before deleting the actual user
let memberships = Membership::find_any_state_by_user(&uuid, &mut conn).await; let memberships = Membership::find_any_state_by_user(&user_id, &mut conn).await;
let res = user.delete(&mut conn).await; let res = user.delete(&mut conn).await;
for membership in memberships { for membership in memberships {
@ -414,9 +414,9 @@ async fn delete_user(uuid: UserId, token: AdminToken, mut conn: DbConn) -> Empty
res res
} }
#[post("/users/<uuid>/deauth")] #[post("/users/<user_id>/deauth")]
async fn deauth_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn deauth_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?; let mut user = get_user_or_404(&user_id, &mut conn).await?;
nt.send_logout(&user, None).await; nt.send_logout(&user, None).await;
@ -435,9 +435,9 @@ async fn deauth_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Not
user.save(&mut conn).await user.save(&mut conn).await
} }
#[post("/users/<uuid>/disable")] #[post("/users/<user_id>/disable")]
async fn disable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn disable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?; let mut user = get_user_or_404(&user_id, &mut conn).await?;
Device::delete_all_by_user(&user.uuid, &mut conn).await?; Device::delete_all_by_user(&user.uuid, &mut conn).await?;
user.reset_security_stamp(); user.reset_security_stamp();
user.enabled = false; user.enabled = false;
@ -449,26 +449,26 @@ async fn disable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: No
save_result save_result
} }
#[post("/users/<uuid>/enable")] #[post("/users/<user_id>/enable")]
async fn enable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn enable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?; let mut user = get_user_or_404(&user_id, &mut conn).await?;
user.enabled = true; user.enabled = true;
user.save(&mut conn).await user.save(&mut conn).await
} }
#[post("/users/<uuid>/remove-2fa")] #[post("/users/<user_id>/remove-2fa")]
async fn remove_2fa(uuid: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn remove_2fa(user_id: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?; let mut user = get_user_or_404(&user_id, &mut conn).await?;
TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
two_factor::enforce_2fa_policy(&user, ACTING_ADMIN_USER, 14, &token.ip.ip, &mut conn).await?; two_factor::enforce_2fa_policy(&user, ACTING_ADMIN_USER, 14, &token.ip.ip, &mut conn).await?;
user.totp_recover = None; user.totp_recover = None;
user.save(&mut conn).await user.save(&mut conn).await
} }
#[post("/users/<uuid>/invite/resend")] #[post("/users/<user_id>/invite/resend")]
async fn resend_user_invite(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn resend_user_invite(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
if let Some(user) = User::find_by_uuid(&uuid, &mut conn).await { if let Some(user) = User::find_by_uuid(&user_id, &mut conn).await {
//TODO: replace this with user.status check when it will be available (PR#3397) //TODO: replace this with user.status check when it will be available (PR#3397)
if !user.password_hash.is_empty() { if !user.password_hash.is_empty() {
err_code!("User already accepted invitation", Status::BadRequest.code); err_code!("User already accepted invitation", Status::BadRequest.code);
@ -570,9 +570,9 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu
Ok(Html(text)) Ok(Html(text))
} }
#[post("/organizations/<org_uuid>/delete")] #[post("/organizations/<org_id>/delete")]
async fn delete_organization(org_uuid: OrganizationId, _token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn delete_organization(org_id: OrganizationId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let org = Organization::find_by_uuid(&org_uuid, &mut conn).await.map_res("Organization doesn't exist")?; let org = Organization::find_by_uuid(&org_id, &mut conn).await.map_res("Organization doesn't exist")?;
org.delete(&mut conn).await org.delete(&mut conn).await
} }

Datei anzeigen

@ -305,9 +305,9 @@ async fn put_avatar(data: Json<AvatarData>, headers: Headers, mut conn: DbConn)
Ok(Json(user.to_json(&mut conn).await)) Ok(Json(user.to_json(&mut conn).await))
} }
#[get("/users/<uuid>/public-key")] #[get("/users/<user_id>/public-key")]
async fn get_public_keys(uuid: UserId, _headers: Headers, mut conn: DbConn) -> JsonResult { async fn get_public_keys(user_id: UserId, _headers: Headers, mut conn: DbConn) -> JsonResult {
let user = match User::find_by_uuid(&uuid, &mut conn).await { let user = match User::find_by_uuid(&user_id, &mut conn).await {
Some(user) if user.public_key.is_some() => user, Some(user) if user.public_key.is_some() => user,
Some(_) => err_code!("User has no public_key", Status::NotFound.code), Some(_) => err_code!("User has no public_key", Status::NotFound.code),
None => err_code!("User doesn't exist", Status::NotFound.code), None => err_code!("User doesn't exist", Status::NotFound.code),
@ -549,17 +549,17 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
// TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks. // TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks.
Cipher::validate_cipher_data(&data.ciphers)?; Cipher::validate_cipher_data(&data.ciphers)?;
let user_uuid = &headers.user.uuid; let user_id = &headers.user.uuid;
// TODO: Ideally we'd do everything after this point in a single transaction. // TODO: Ideally we'd do everything after this point in a single transaction.
let mut existing_ciphers = Cipher::find_owned_by_user(user_uuid, &mut conn).await; let mut existing_ciphers = Cipher::find_owned_by_user(user_id, &mut conn).await;
let mut existing_folders = Folder::find_by_user(user_uuid, &mut conn).await; let mut existing_folders = Folder::find_by_user(user_id, &mut conn).await;
let mut existing_emergency_access = EmergencyAccess::find_all_by_grantor_uuid(user_uuid, &mut conn).await; let mut existing_emergency_access = EmergencyAccess::find_all_by_grantor_uuid(user_id, &mut conn).await;
let mut existing_memberships = Membership::find_by_user(user_uuid, &mut conn).await; let mut existing_memberships = Membership::find_by_user(user_id, &mut conn).await;
// We only rotate the reset password key if it is set. // We only rotate the reset password key if it is set.
existing_memberships.retain(|m| m.reset_password_key.is_some()); existing_memberships.retain(|m| m.reset_password_key.is_some());
let mut existing_sends = Send::find_by_user(user_uuid, &mut conn).await; let mut existing_sends = Send::find_by_user(user_id, &mut conn).await;
validate_keydata( validate_keydata(
&data, &data,

Datei anzeigen

@ -191,9 +191,9 @@ async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json<Value> {
})) }))
} }
#[get("/ciphers/<uuid>")] #[get("/ciphers/<cipher_id>")]
async fn get_cipher(uuid: CipherId, headers: Headers, mut conn: DbConn) -> JsonResult { async fn get_cipher(cipher_id: CipherId, 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(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -204,15 +204,15 @@ async fn get_cipher(uuid: CipherId, headers: Headers, mut conn: DbConn) -> JsonR
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
} }
#[get("/ciphers/<uuid>/admin")] #[get("/ciphers/<cipher_id>/admin")]
async fn get_cipher_admin(uuid: CipherId, headers: Headers, conn: DbConn) -> JsonResult { async fn get_cipher_admin(cipher_id: CipherId, headers: Headers, conn: DbConn) -> JsonResult {
// TODO: Implement this correctly // TODO: Implement this correctly
get_cipher(uuid, headers, conn).await get_cipher(cipher_id, headers, conn).await
} }
#[get("/ciphers/<uuid>/details")] #[get("/ciphers/<cipher_id>/details")]
async fn get_cipher_details(uuid: CipherId, headers: Headers, conn: DbConn) -> JsonResult { async fn get_cipher_details(cipher_id: CipherId, headers: Headers, conn: DbConn) -> JsonResult {
get_cipher(uuid, headers, conn).await get_cipher(cipher_id, headers, conn).await
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -355,9 +355,9 @@ async fn enforce_personal_ownership_policy(
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
if data.is_none() || data.unwrap().organization_id.is_none() { if data.is_none() || data.unwrap().organization_id.is_none() {
let user_uuid = &headers.user.uuid; let user_id = &headers.user.uuid;
let policy_type = OrgPolicyType::PersonalOwnership; let policy_type = OrgPolicyType::PersonalOwnership;
if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, None, conn).await { if OrgPolicy::is_applicable_to_user(user_id, policy_type, None, conn).await {
err!("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.") err!("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.")
} }
} }
@ -428,8 +428,8 @@ pub async fn update_cipher_from_data(
cipher.user_uuid = Some(headers.user.uuid.clone()); cipher.user_uuid = Some(headers.user.uuid.clone());
} }
if let Some(ref folder_uuid) = data.folder_id { if let Some(ref folder_id) = data.folder_id {
if Folder::find_by_uuid_and_user(folder_uuid, &headers.user.uuid, conn).await.is_none() { if Folder::find_by_uuid_and_user(folder_id, &headers.user.uuid, conn).await.is_none() {
err!("Invalid folder", "Folder does not exist or belongs to another user"); err!("Invalid folder", "Folder does not exist or belongs to another user");
} }
} }
@ -513,7 +513,7 @@ pub async fn update_cipher_from_data(
if ut != UpdateType::None { if ut != UpdateType::None {
// Only log events for organizational ciphers // Only log events for organizational ciphers
if let Some(org_uuid) = &cipher.organization_uuid { if let Some(org_id) = &cipher.organization_uuid {
let event_type = match (&ut, transfer_cipher) { let event_type = match (&ut, transfer_cipher) {
(UpdateType::SyncCipherCreate, true) => EventType::CipherCreated, (UpdateType::SyncCipherCreate, true) => EventType::CipherCreated,
(UpdateType::SyncCipherUpdate, true) => EventType::CipherShared, (UpdateType::SyncCipherUpdate, true) => EventType::CipherShared,
@ -523,7 +523,7 @@ pub async fn update_cipher_from_data(
log_event( log_event(
event_type as i32, event_type as i32,
&cipher.uuid, &cipher.uuid,
org_uuid, org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
&headers.ip.ip, &headers.ip.ip,
@ -583,7 +583,7 @@ async fn post_ciphers_import(
Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| Some(f.uuid)).collect(); Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| Some(f.uuid)).collect();
let mut folders: Vec<FolderId> = Vec::with_capacity(data.folders.len()); let mut folders: Vec<FolderId> = Vec::with_capacity(data.folders.len());
for folder in data.folders.into_iter() { for folder in data.folders.into_iter() {
let folder_uuid = if existing_folders.contains(&folder.id) { let folder_id = if existing_folders.contains(&folder.id) {
folder.id.unwrap() folder.id.unwrap()
} else { } else {
let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.name); let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.name);
@ -591,7 +591,7 @@ async fn post_ciphers_import(
new_folder.uuid new_folder.uuid
}; };
folders.push(folder_uuid); folders.push(folder_id);
} }
// Read the relations between folders and ciphers // Read the relations between folders and ciphers
@ -603,8 +603,8 @@ async fn post_ciphers_import(
// Read and create the ciphers // Read and create the ciphers
for (index, mut cipher_data) in data.ciphers.into_iter().enumerate() { for (index, mut cipher_data) in data.ciphers.into_iter().enumerate() {
let folder_uuid = relations_map.get(&index).map(|i| folders[*i].clone()); let folder_id = relations_map.get(&index).map(|i| folders[*i].clone());
cipher_data.folder_id = folder_uuid; cipher_data.folder_id = folder_id;
let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone()); let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone());
update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None).await?; update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None).await?;
@ -618,42 +618,42 @@ async fn post_ciphers_import(
} }
/// Called when an org admin modifies an existing org cipher. /// Called when an org admin modifies an existing org cipher.
#[put("/ciphers/<uuid>/admin", data = "<data>")] #[put("/ciphers/<cipher_id>/admin", data = "<data>")]
async fn put_cipher_admin( async fn put_cipher_admin(
uuid: CipherId, cipher_id: CipherId,
data: Json<CipherData>, data: Json<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
put_cipher(uuid, data, headers, conn, nt).await put_cipher(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/admin", data = "<data>")] #[post("/ciphers/<cipher_id>/admin", data = "<data>")]
async fn post_cipher_admin( async fn post_cipher_admin(
uuid: CipherId, cipher_id: CipherId,
data: Json<CipherData>, data: Json<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_cipher(uuid, data, headers, conn, nt).await post_cipher(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>", data = "<data>")] #[post("/ciphers/<cipher_id>", data = "<data>")]
async fn post_cipher( async fn post_cipher(
uuid: CipherId, cipher_id: CipherId,
data: Json<CipherData>, data: Json<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
put_cipher(uuid, data, headers, conn, nt).await put_cipher(cipher_id, data, headers, conn, nt).await
} }
#[put("/ciphers/<uuid>", data = "<data>")] #[put("/ciphers/<cipher_id>", data = "<data>")]
async fn put_cipher( async fn put_cipher(
uuid: CipherId, cipher_id: CipherId,
data: Json<CipherData>, data: Json<CipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -661,7 +661,7 @@ async fn put_cipher(
) -> JsonResult { ) -> JsonResult {
let data: CipherData = data.into_inner(); 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(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -679,32 +679,32 @@ async fn put_cipher(
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
} }
#[post("/ciphers/<uuid>/partial", data = "<data>")] #[post("/ciphers/<cipher_id>/partial", data = "<data>")]
async fn post_cipher_partial( async fn post_cipher_partial(
uuid: CipherId, cipher_id: CipherId,
data: Json<PartialCipherData>, data: Json<PartialCipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
) -> JsonResult { ) -> JsonResult {
put_cipher_partial(uuid, data, headers, conn).await put_cipher_partial(cipher_id, data, headers, conn).await
} }
// Only update the folder and favorite for the user, since this cipher is read-only // Only update the folder and favorite for the user, since this cipher is read-only
#[put("/ciphers/<uuid>/partial", data = "<data>")] #[put("/ciphers/<cipher_id>/partial", data = "<data>")]
async fn put_cipher_partial( async fn put_cipher_partial(
uuid: CipherId, cipher_id: CipherId,
data: Json<PartialCipherData>, data: Json<PartialCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
let data: PartialCipherData = data.into_inner(); 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(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
if let Some(ref folder_uuid) = data.folder_id { if let Some(ref folder_id) = data.folder_id {
if Folder::find_by_uuid_and_user(folder_uuid, &headers.user.uuid, &mut conn).await.is_none() { if Folder::find_by_uuid_and_user(folder_id, &headers.user.uuid, &mut conn).await.is_none() {
err!("Invalid folder", "Folder does not exist or belongs to another user"); err!("Invalid folder", "Folder does not exist or belongs to another user");
} }
} }
@ -724,26 +724,26 @@ struct CollectionsAdminData {
collection_ids: Vec<CollectionId>, collection_ids: Vec<CollectionId>,
} }
#[put("/ciphers/<uuid>/collections_v2", data = "<data>")] #[put("/ciphers/<cipher_id>/collections_v2", data = "<data>")]
async fn put_collections2_update( async fn put_collections2_update(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_collections2_update(uuid, data, headers, conn, nt).await post_collections2_update(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/collections_v2", data = "<data>")] #[post("/ciphers/<cipher_id>/collections_v2", data = "<data>")]
async fn post_collections2_update( async fn post_collections2_update(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let cipher_details = post_collections_update(uuid, data, headers, conn, nt).await?; let cipher_details = post_collections_update(cipher_id, data, headers, conn, nt).await?;
Ok(Json(json!({ // AttachmentUploadDataResponseModel Ok(Json(json!({ // AttachmentUploadDataResponseModel
"object": "optionalCipherDetails", "object": "optionalCipherDetails",
"unavailable": false, "unavailable": false,
@ -751,20 +751,20 @@ async fn post_collections2_update(
}))) })))
} }
#[put("/ciphers/<uuid>/collections", data = "<data>")] #[put("/ciphers/<cipher_id>/collections", data = "<data>")]
async fn put_collections_update( async fn put_collections_update(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_collections_update(uuid, data, headers, conn, nt).await post_collections_update(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/collections", data = "<data>")] #[post("/ciphers/<cipher_id>/collections", data = "<data>")]
async fn post_collections_update( async fn post_collections_update(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -772,7 +772,7 @@ async fn post_collections_update(
) -> JsonResult { ) -> JsonResult {
let data: CollectionsAdminData = data.into_inner(); 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(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -828,20 +828,20 @@ async fn post_collections_update(
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
} }
#[put("/ciphers/<uuid>/collections-admin", data = "<data>")] #[put("/ciphers/<cipher_id>/collections-admin", data = "<data>")]
async fn put_collections_admin( async fn put_collections_admin(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
post_collections_admin(uuid, data, headers, conn, nt).await post_collections_admin(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/collections-admin", data = "<data>")] #[post("/ciphers/<cipher_id>/collections-admin", data = "<data>")]
async fn post_collections_admin( async fn post_collections_admin(
uuid: CipherId, cipher_id: CipherId,
data: Json<CollectionsAdminData>, data: Json<CollectionsAdminData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -849,7 +849,7 @@ async fn post_collections_admin(
) -> EmptyResult { ) -> EmptyResult {
let data: CollectionsAdminData = data.into_inner(); 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(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -914,9 +914,9 @@ struct ShareCipherData {
collection_ids: Vec<CollectionId>, collection_ids: Vec<CollectionId>,
} }
#[post("/ciphers/<uuid>/share", data = "<data>")] #[post("/ciphers/<cipher_id>/share", data = "<data>")]
async fn post_cipher_share( async fn post_cipher_share(
uuid: CipherId, cipher_id: CipherId,
data: Json<ShareCipherData>, data: Json<ShareCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -924,12 +924,12 @@ async fn post_cipher_share(
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner(); let data: ShareCipherData = data.into_inner();
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt).await
} }
#[put("/ciphers/<uuid>/share", data = "<data>")] #[put("/ciphers/<cipher_id>/share", data = "<data>")]
async fn put_cipher_share( async fn put_cipher_share(
uuid: CipherId, cipher_id: CipherId,
data: Json<ShareCipherData>, data: Json<ShareCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -937,7 +937,7 @@ async fn put_cipher_share(
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner(); let data: ShareCipherData = data.into_inner();
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -986,13 +986,13 @@ async fn put_cipher_share_selected(
} }
async fn share_cipher_by_uuid( async fn share_cipher_by_uuid(
uuid: &CipherId, cipher_id: &CipherId,
data: ShareCipherData, data: ShareCipherData,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { let mut cipher = match Cipher::find_by_uuid(cipher_id, conn).await {
Some(cipher) => { Some(cipher) => {
if cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { if cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await {
cipher cipher
@ -1005,9 +1005,9 @@ async fn share_cipher_by_uuid(
let mut shared_to_collections = vec![]; let mut shared_to_collections = vec![];
if let Some(organization_uuid) = &data.cipher.organization_id { if let Some(organization_id) = &data.cipher.organization_id {
for uuid in &data.collection_ids { for col_id in &data.collection_ids {
match Collection::find_by_uuid_and_org(uuid, organization_uuid, conn).await { match Collection::find_by_uuid_and_org(col_id, organization_id, conn).await {
None => err!("Invalid collection ID provided"), None => err!("Invalid collection ID provided"),
Some(collection) => { Some(collection) => {
if collection.is_writable_by_user(&headers.user.uuid, conn).await { if collection.is_writable_by_user(&headers.user.uuid, conn).await {
@ -1039,9 +1039,14 @@ async fn share_cipher_by_uuid(
/// Upstream added this v2 API to support direct download of attachments from /// Upstream added this v2 API to support direct download of attachments from
/// their object storage service. For self-hosted instances, it basically just /// their object storage service. For self-hosted instances, it basically just
/// redirects to the same location as before the v2 API. /// redirects to the same location as before the v2 API.
#[get("/ciphers/<uuid>/attachment/<attachment_id>")] #[get("/ciphers/<cipher_id>/attachment/<attachment_id>")]
async fn get_attachment(uuid: CipherId, attachment_id: AttachmentId, headers: Headers, mut conn: DbConn) -> JsonResult { async fn get_attachment(
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else { cipher_id: CipherId,
attachment_id: AttachmentId,
headers: Headers,
mut conn: DbConn,
) -> JsonResult {
let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1050,7 +1055,7 @@ async fn get_attachment(uuid: CipherId, attachment_id: AttachmentId, headers: He
} }
match Attachment::find_by_id(&attachment_id, &mut conn).await { match Attachment::find_by_id(&attachment_id, &mut conn).await {
Some(attachment) if uuid == attachment.cipher_uuid => Ok(Json(attachment.to_json(&headers.host))), Some(attachment) if cipher_id == attachment.cipher_uuid => Ok(Json(attachment.to_json(&headers.host))),
Some(_) => err!("Attachment doesn't belong to cipher"), Some(_) => err!("Attachment doesn't belong to cipher"),
None => err!("Attachment doesn't exist"), None => err!("Attachment doesn't exist"),
} }
@ -1074,14 +1079,14 @@ enum FileUploadType {
/// This redirects the client to the API it should use to upload the attachment. /// This redirects the client to the API it should use to upload the attachment.
/// For upstream's cloud-hosted service, it's an Azure object storage API. /// For upstream's cloud-hosted service, it's an Azure object storage API.
/// For self-hosted instances, it's another API on the local instance. /// For self-hosted instances, it's another API on the local instance.
#[post("/ciphers/<uuid>/attachment/v2", data = "<data>")] #[post("/ciphers/<cipher_id>/attachment/v2", data = "<data>")]
async fn post_attachment_v2( async fn post_attachment_v2(
uuid: CipherId, cipher_id: CipherId,
data: Json<AttachmentRequestData>, data: Json<AttachmentRequestData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else { let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1131,7 +1136,7 @@ struct UploadData<'f> {
/// database record, which is passed in as `attachment`. /// database record, which is passed in as `attachment`.
async fn save_attachment( async fn save_attachment(
mut attachment: Option<Attachment>, mut attachment: Option<Attachment>,
cipher_uuid: CipherId, cipher_id: CipherId,
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: &Headers, headers: &Headers,
mut conn: DbConn, mut conn: DbConn,
@ -1146,7 +1151,7 @@ async fn save_attachment(
err!("Attachment size can't be negative") 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_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1161,11 +1166,11 @@ async fn save_attachment(
Some(a) => a.file_size, // v2 API Some(a) => a.file_size, // v2 API
}; };
let size_limit = if let Some(ref user_uuid) = cipher.user_uuid { let size_limit = if let Some(ref user_id) = cipher.user_uuid {
match CONFIG.user_attachment_limit() { match CONFIG.user_attachment_limit() {
Some(0) => err!("Attachments are disabled"), Some(0) => err!("Attachments are disabled"),
Some(limit_kb) => { Some(limit_kb) => {
let already_used = Attachment::size_by_user(user_uuid, &mut conn).await; let already_used = Attachment::size_by_user(user_id, &mut conn).await;
let left = limit_kb let left = limit_kb
.checked_mul(1024) .checked_mul(1024)
.and_then(|l| l.checked_sub(already_used)) .and_then(|l| l.checked_sub(already_used))
@ -1183,11 +1188,11 @@ async fn save_attachment(
} }
None => None, None => None,
} }
} else if let Some(ref org_uuid) = cipher.organization_uuid { } else if let Some(ref org_id) = cipher.organization_uuid {
match CONFIG.org_attachment_limit() { match CONFIG.org_attachment_limit() {
Some(0) => err!("Attachments are disabled"), Some(0) => err!("Attachments are disabled"),
Some(limit_kb) => { Some(limit_kb) => {
let already_used = Attachment::size_by_org(org_uuid, &mut conn).await; let already_used = Attachment::size_by_org(org_id, &mut conn).await;
let left = limit_kb let left = limit_kb
.checked_mul(1024) .checked_mul(1024)
.and_then(|l| l.checked_sub(already_used)) .and_then(|l| l.checked_sub(already_used))
@ -1260,11 +1265,11 @@ async fn save_attachment(
err!("No attachment key provided") err!("No attachment key provided")
} }
let attachment = let attachment =
Attachment::new(file_id.clone(), cipher_uuid.clone(), encrypted_filename.unwrap(), size, data.key); Attachment::new(file_id.clone(), cipher_id.clone(), encrypted_filename.unwrap(), size, data.key);
attachment.save(&mut conn).await.expect("Error saving attachment"); attachment.save(&mut conn).await.expect("Error saving attachment");
} }
let folder_path = tokio::fs::canonicalize(&CONFIG.attachments_folder()).await?.join(cipher_uuid.as_ref()); let folder_path = tokio::fs::canonicalize(&CONFIG.attachments_folder()).await?.join(cipher_id.as_ref());
let file_path = folder_path.join(file_id.as_ref()); let file_path = folder_path.join(file_id.as_ref());
tokio::fs::create_dir_all(&folder_path).await?; tokio::fs::create_dir_all(&folder_path).await?;
@ -1282,11 +1287,11 @@ async fn save_attachment(
) )
.await; .await;
if let Some(org_uuid) = &cipher.organization_uuid { if let Some(org_id) = &cipher.organization_uuid {
log_event( log_event(
EventType::CipherAttachmentCreated as i32, EventType::CipherAttachmentCreated as i32,
&cipher.uuid, &cipher.uuid,
org_uuid, org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
&headers.ip.ip, &headers.ip.ip,
@ -1300,11 +1305,11 @@ async fn save_attachment(
/// v2 API for uploading the actual data content of an attachment. /// v2 API for uploading the actual data content of an attachment.
/// This route needs a rank specified so that Rocket prioritizes the /// This route needs a rank specified so that Rocket prioritizes the
/// /ciphers/<uuid>/attachment/v2 route, which would otherwise conflict /// /ciphers/<cipher_id>/attachment/v2 route, which would otherwise conflict
/// with this one. /// with this one.
#[post("/ciphers/<uuid>/attachment/<attachment_id>", format = "multipart/form-data", data = "<data>", rank = 1)] #[post("/ciphers/<cipher_id>/attachment/<attachment_id>", format = "multipart/form-data", data = "<data>", rank = 1)]
async fn post_attachment_v2_data( async fn post_attachment_v2_data(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
@ -1312,20 +1317,20 @@ async fn post_attachment_v2_data(
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let attachment = match Attachment::find_by_id(&attachment_id, &mut conn).await { let attachment = match Attachment::find_by_id(&attachment_id, &mut conn).await {
Some(attachment) if uuid == attachment.cipher_uuid => Some(attachment), Some(attachment) if cipher_id == attachment.cipher_uuid => Some(attachment),
Some(_) => err!("Attachment doesn't belong to cipher"), Some(_) => err!("Attachment doesn't belong to cipher"),
None => err!("Attachment doesn't exist"), None => err!("Attachment doesn't exist"),
}; };
save_attachment(attachment, uuid, data, &headers, conn, nt).await?; save_attachment(attachment, cipher_id, data, &headers, conn, nt).await?;
Ok(()) Ok(())
} }
/// Legacy API for creating an attachment associated with a cipher. /// Legacy API for creating an attachment associated with a cipher.
#[post("/ciphers/<uuid>/attachment", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<cipher_id>/attachment", format = "multipart/form-data", data = "<data>")]
async fn post_attachment( async fn post_attachment(
uuid: CipherId, cipher_id: CipherId,
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
@ -1335,111 +1340,121 @@ async fn post_attachment(
// the attachment database record as well as saving the data to disk. // the attachment database record as well as saving the data to disk.
let attachment = None; let attachment = None;
let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; let (cipher, mut conn) = save_attachment(attachment, cipher_id, data, &headers, conn, nt).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
} }
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<cipher_id>/attachment-admin", format = "multipart/form-data", data = "<data>")]
async fn post_attachment_admin( async fn post_attachment_admin(
uuid: CipherId, cipher_id: CipherId,
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_attachment(uuid, data, headers, conn, nt).await post_attachment(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<cipher_id>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
async fn post_attachment_share( async fn post_attachment_share(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await?; _delete_cipher_attachment_by_id(&cipher_id, &attachment_id, &headers, &mut conn, &nt).await?;
post_attachment(uuid, data, headers, conn, nt).await post_attachment(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")] #[post("/ciphers/<cipher_id>/attachment/<attachment_id>/delete-admin")]
async fn delete_attachment_post_admin( async fn delete_attachment_post_admin(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn, nt).await delete_attachment(cipher_id, attachment_id, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")] #[post("/ciphers/<cipher_id>/attachment/<attachment_id>/delete")]
async fn delete_attachment_post( async fn delete_attachment_post(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn, nt).await delete_attachment(cipher_id, attachment_id, headers, conn, nt).await
} }
#[delete("/ciphers/<uuid>/attachment/<attachment_id>")] #[delete("/ciphers/<cipher_id>/attachment/<attachment_id>")]
async fn delete_attachment( async fn delete_attachment(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await _delete_cipher_attachment_by_id(&cipher_id, &attachment_id, &headers, &mut conn, &nt).await
} }
#[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")] #[delete("/ciphers/<cipher_id>/attachment/<attachment_id>/admin")]
async fn delete_attachment_admin( async fn delete_attachment_admin(
uuid: CipherId, cipher_id: CipherId,
attachment_id: AttachmentId, attachment_id: AttachmentId,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await _delete_cipher_attachment_by_id(&cipher_id, &attachment_id, &headers, &mut conn, &nt).await
} }
#[post("/ciphers/<uuid>/delete")] #[post("/ciphers/<cipher_id>/delete")]
async fn delete_cipher_post(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher_post(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await _delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, false, &nt).await
// permanent delete // permanent delete
} }
#[post("/ciphers/<uuid>/delete-admin")] #[post("/ciphers/<cipher_id>/delete-admin")]
async fn delete_cipher_post_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher_post_admin(
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await cipher_id: CipherId,
headers: Headers,
mut conn: DbConn,
nt: Notify<'_>,
) -> EmptyResult {
_delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, false, &nt).await
// permanent delete // permanent delete
} }
#[put("/ciphers/<uuid>/delete")] #[put("/ciphers/<cipher_id>/delete")]
async fn delete_cipher_put(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher_put(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await _delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, true, &nt).await
// soft delete // soft delete
} }
#[put("/ciphers/<uuid>/delete-admin")] #[put("/ciphers/<cipher_id>/delete-admin")]
async fn delete_cipher_put_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher_put_admin(
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await cipher_id: CipherId,
headers: Headers,
mut conn: DbConn,
nt: Notify<'_>,
) -> EmptyResult {
_delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, true, &nt).await
} }
#[delete("/ciphers/<uuid>")] #[delete("/ciphers/<cipher_id>")]
async fn delete_cipher(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await _delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, false, &nt).await
// permanent delete // permanent delete
} }
#[delete("/ciphers/<uuid>/admin")] #[delete("/ciphers/<cipher_id>/admin")]
async fn delete_cipher_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher_admin(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await _delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, false, &nt).await
// permanent delete // permanent delete
} }
@ -1503,14 +1518,19 @@ async fn delete_cipher_selected_put_admin(
_delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete
} }
#[put("/ciphers/<uuid>/restore")] #[put("/ciphers/<cipher_id>/restore")]
async fn restore_cipher_put(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { async fn restore_cipher_put(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
_restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await _restore_cipher_by_uuid(&cipher_id, &headers, &mut conn, &nt).await
} }
#[put("/ciphers/<uuid>/restore-admin")] #[put("/ciphers/<cipher_id>/restore-admin")]
async fn restore_cipher_put_admin(uuid: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { async fn restore_cipher_put_admin(
_restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await cipher_id: CipherId,
headers: Headers,
mut conn: DbConn,
nt: Notify<'_>,
) -> JsonResult {
_restore_cipher_by_uuid(&cipher_id, &headers, &mut conn, &nt).await
} }
#[put("/ciphers/restore", data = "<data>")] #[put("/ciphers/restore", data = "<data>")]
@ -1538,30 +1558,30 @@ async fn move_cipher_selected(
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data = data.into_inner(); let data = data.into_inner();
let user_uuid = headers.user.uuid; let user_id = headers.user.uuid;
if let Some(ref folder_uuid) = data.folder_id { if let Some(ref folder_id) = data.folder_id {
if Folder::find_by_uuid_and_user(folder_uuid, &user_uuid, &mut conn).await.is_none() { if Folder::find_by_uuid_and_user(folder_id, &user_id, &mut conn).await.is_none() {
err!("Invalid folder", "Folder does not exist or belongs to another user"); err!("Invalid folder", "Folder does not exist or belongs to another user");
} }
} }
for uuid in data.ids { for cipher_id in data.ids {
let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else { let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &mut conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
if !cipher.is_accessible_to_user(&user_uuid, &mut conn).await { if !cipher.is_accessible_to_user(&user_id, &mut conn).await {
err!("Cipher is not accessible by user") err!("Cipher is not accessible by user")
} }
// Move cipher // Move cipher
cipher.move_to_folder(data.folder_id.clone(), &user_uuid, &mut conn).await?; cipher.move_to_folder(data.folder_id.clone(), &user_id, &mut conn).await?;
nt.send_cipher_update( nt.send_cipher_update(
UpdateType::SyncCipherUpdate, UpdateType::SyncCipherUpdate,
&cipher, &cipher,
&[user_uuid.clone()], &[user_id.clone()],
&headers.device.uuid, &headers.device.uuid,
None, None,
&mut conn, &mut conn,
@ -1650,13 +1670,13 @@ async fn delete_all(
} }
async fn _delete_cipher_by_uuid( async fn _delete_cipher_by_uuid(
uuid: &CipherId, cipher_id: &CipherId,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
soft_delete: bool, soft_delete: bool,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let Some(mut cipher) = Cipher::find_by_uuid(uuid, conn).await else { let Some(mut cipher) = Cipher::find_by_uuid(cipher_id, conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1689,13 +1709,13 @@ async fn _delete_cipher_by_uuid(
.await; .await;
} }
if let Some(org_uuid) = cipher.organization_uuid { if let Some(org_id) = cipher.organization_uuid {
let event_type = match soft_delete { let event_type = match soft_delete {
true => EventType::CipherSoftDeleted as i32, true => EventType::CipherSoftDeleted as i32,
false => EventType::CipherDeleted as i32, false => EventType::CipherDeleted as i32,
}; };
log_event(event_type, &cipher.uuid, &org_uuid, &headers.user.uuid, headers.device.atype, &headers.ip.ip, conn) log_event(event_type, &cipher.uuid, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, conn)
.await; .await;
} }
@ -1717,8 +1737,8 @@ async fn _delete_multiple_ciphers(
) -> EmptyResult { ) -> EmptyResult {
let data = data.into_inner(); let data = data.into_inner();
for uuid in data.ids { for cipher_id in data.ids {
if let error @ Err(_) = _delete_cipher_by_uuid(&uuid, &headers, &mut conn, soft_delete, &nt).await { if let error @ Err(_) = _delete_cipher_by_uuid(&cipher_id, &headers, &mut conn, soft_delete, &nt).await {
return error; return error;
}; };
} }
@ -1726,8 +1746,13 @@ async fn _delete_multiple_ciphers(
Ok(()) Ok(())
} }
async fn _restore_cipher_by_uuid(uuid: &CipherId, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { async fn _restore_cipher_by_uuid(
let Some(mut cipher) = Cipher::find_by_uuid(uuid, conn).await else { cipher_id: &CipherId,
headers: &Headers,
conn: &mut DbConn,
nt: &Notify<'_>,
) -> JsonResult {
let Some(mut cipher) = Cipher::find_by_uuid(cipher_id, conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1748,11 +1773,11 @@ async fn _restore_cipher_by_uuid(uuid: &CipherId, headers: &Headers, conn: &mut
) )
.await; .await;
if let Some(org_uuid) = &cipher.organization_uuid { if let Some(org_id) = &cipher.organization_uuid {
log_event( log_event(
EventType::CipherRestored as i32, EventType::CipherRestored as i32,
&cipher.uuid.clone(), &cipher.uuid.clone(),
org_uuid, org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
&headers.ip.ip, &headers.ip.ip,
@ -1773,8 +1798,8 @@ async fn _restore_multiple_ciphers(
let data = data.into_inner(); let data = data.into_inner();
let mut ciphers: Vec<Value> = Vec::new(); let mut ciphers: Vec<Value> = Vec::new();
for uuid in data.ids { for cipher_id in data.ids {
match _restore_cipher_by_uuid(&uuid, headers, conn, nt).await { match _restore_cipher_by_uuid(&cipher_id, headers, conn, nt).await {
Ok(json) => ciphers.push(json.into_inner()), Ok(json) => ciphers.push(json.into_inner()),
err => return err, err => return err,
} }
@ -1788,7 +1813,7 @@ async fn _restore_multiple_ciphers(
} }
async fn _delete_cipher_attachment_by_id( async fn _delete_cipher_attachment_by_id(
uuid: &CipherId, cipher_id: &CipherId,
attachment_id: &AttachmentId, attachment_id: &AttachmentId,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
@ -1798,11 +1823,11 @@ async fn _delete_cipher_attachment_by_id(
err!("Attachment doesn't exist") err!("Attachment doesn't exist")
}; };
if &attachment.cipher_uuid != uuid { if &attachment.cipher_uuid != cipher_id {
err!("Attachment from other cipher") err!("Attachment from other cipher")
} }
let Some(cipher) = Cipher::find_by_uuid(uuid, conn).await else { let Some(cipher) = Cipher::find_by_uuid(cipher_id, conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
}; };
@ -1822,11 +1847,11 @@ async fn _delete_cipher_attachment_by_id(
) )
.await; .await;
if let Some(org_uuid) = cipher.organization_uuid { if let Some(org_id) = cipher.organization_uuid {
log_event( log_event(
EventType::CipherAttachmentDeleted as i32, EventType::CipherAttachmentDeleted as i32,
&cipher.uuid, &cipher.uuid,
&org_uuid, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
&headers.ip.ip, &headers.ip.ip,
@ -1859,17 +1884,17 @@ pub enum CipherSyncType {
} }
impl CipherSyncData { impl CipherSyncData {
pub async fn new(user_uuid: &UserId, sync_type: CipherSyncType, conn: &mut DbConn) -> Self { pub async fn new(user_id: &UserId, sync_type: CipherSyncType, conn: &mut DbConn) -> Self {
let cipher_folders: HashMap<CipherId, FolderId>; let cipher_folders: HashMap<CipherId, FolderId>;
let cipher_favorites: HashSet<CipherId>; let cipher_favorites: HashSet<CipherId>;
match sync_type { match sync_type {
// User Sync supports Folders and Favorites // User Sync supports Folders and Favorites
CipherSyncType::User => { CipherSyncType::User => {
// Generate a HashMap with the Cipher UUID as key and the Folder UUID as value // Generate a HashMap with the Cipher UUID as key and the Folder UUID as value
cipher_folders = FolderCipher::find_by_user(user_uuid, conn).await.into_iter().collect(); cipher_folders = FolderCipher::find_by_user(user_id, conn).await.into_iter().collect();
// Generate a HashSet of all the Cipher UUID's which are marked as favorite // Generate a HashSet of all the Cipher UUID's which are marked as favorite
cipher_favorites = Favorite::get_all_cipher_uuid_by_user(user_uuid, conn).await.into_iter().collect(); cipher_favorites = Favorite::get_all_cipher_uuid_by_user(user_id, conn).await.into_iter().collect();
} }
// Organization Sync does not support Folders and Favorites. // Organization Sync does not support Folders and Favorites.
// If these are set, it will cause issues in the web-vault. // If these are set, it will cause issues in the web-vault.
@ -1880,15 +1905,15 @@ impl CipherSyncData {
} }
// Generate a list of Cipher UUID's containing a Vec with one or more Attachment records // 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 orgs = Membership::get_orgs_by_user(user_id, conn).await;
let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &orgs, conn).await; let attachments = Attachment::find_all_by_user_and_orgs(user_id, &orgs, conn).await;
let mut cipher_attachments: HashMap<CipherId, Vec<Attachment>> = HashMap::with_capacity(attachments.len()); let mut cipher_attachments: HashMap<CipherId, Vec<Attachment>> = HashMap::with_capacity(attachments.len());
for attachment in attachments { for attachment in attachments {
cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment); 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 // 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.clone(), conn).await; let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_id.clone(), conn).await;
let mut cipher_collections: HashMap<CipherId, Vec<CollectionId>> = let mut cipher_collections: HashMap<CipherId, Vec<CollectionId>> =
HashMap::with_capacity(user_cipher_collections.len()); HashMap::with_capacity(user_cipher_collections.len());
for (cipher, collection) in user_cipher_collections { for (cipher, collection) in user_cipher_collections {
@ -1897,10 +1922,10 @@ impl CipherSyncData {
// Generate a HashMap with the Organization UUID as key and the Membership record // Generate a HashMap with the Organization UUID as key and the Membership record
let members: HashMap<OrganizationId, Membership> = let members: HashMap<OrganizationId, Membership> =
Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect(); Membership::find_by_user(user_id, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
let user_collections: HashMap<CollectionId, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn) let user_collections: HashMap<CollectionId, CollectionUser> = CollectionUser::find_by_user(user_id, conn)
.await .await
.into_iter() .into_iter()
.map(|uc| (uc.collection_uuid.clone(), uc)) .map(|uc| (uc.collection_uuid.clone(), uc))
@ -1908,7 +1933,7 @@ impl CipherSyncData {
// Generate a HashMap with the collections_uuid as key and the CollectionGroup record // Generate a HashMap with the collections_uuid as key and the CollectionGroup record
let user_collections_groups: HashMap<CollectionId, CollectionGroup> = if CONFIG.org_groups_enabled() { let user_collections_groups: HashMap<CollectionId, CollectionGroup> = if CONFIG.org_groups_enabled() {
CollectionGroup::find_by_user(user_uuid, conn) CollectionGroup::find_by_user(user_id, conn)
.await .await
.into_iter() .into_iter()
.map(|collection_group| (collection_group.collections_uuid.clone(), collection_group)) .map(|collection_group| (collection_group.collections_uuid.clone(), collection_group))
@ -1919,7 +1944,7 @@ impl CipherSyncData {
// Get all organizations that the given user has full access to via group assignment // Get all organizations that the given user has full access to via group assignment
let user_group_full_access_for_organizations: HashSet<OrganizationId> = if CONFIG.org_groups_enabled() { let user_group_full_access_for_organizations: HashSet<OrganizationId> = if CONFIG.org_groups_enabled() {
Group::get_orgs_by_user_with_full_access(user_uuid, conn).await.into_iter().collect() Group::get_orgs_by_user_with_full_access(user_id, conn).await.into_iter().collect()
} else { } else {
HashSet::new() HashSet::new()
}; };

Datei anzeigen

@ -701,11 +701,11 @@ async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
fn is_valid_request( fn is_valid_request(
emergency_access: &EmergencyAccess, emergency_access: &EmergencyAccess,
requesting_user_uuid: &UserId, requesting_user_id: &UserId,
requested_access_type: EmergencyAccessType, requested_access_type: EmergencyAccessType,
) -> bool { ) -> bool {
emergency_access.grantee_uuid.is_some() emergency_access.grantee_uuid.is_some()
&& emergency_access.grantee_uuid.as_ref().unwrap() == requesting_user_uuid && emergency_access.grantee_uuid.as_ref().unwrap() == requesting_user_id
&& emergency_access.status == EmergencyAccessStatus::RecoveryApproved as i32 && emergency_access.status == EmergencyAccessStatus::RecoveryApproved as i32
&& emergency_access.atype == requested_access_type as i32 && emergency_access.atype == requested_access_type as i32
} }

Datei anzeigen

@ -180,11 +180,11 @@ async fn post_events_collect(data: Json<Vec<EventCollection>>, headers: Headers,
.await; .await;
} }
1600..=1699 => { 1600..=1699 => {
if let Some(org_uuid) = &event.organization_id { if let Some(org_id) = &event.organization_id {
_log_event( _log_event(
event.r#type, event.r#type,
org_uuid, org_id,
org_uuid, org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
Some(event_date), Some(event_date),
@ -197,11 +197,11 @@ async fn post_events_collect(data: Json<Vec<EventCollection>>, headers: Headers,
_ => { _ => {
if let Some(cipher_uuid) = &event.cipher_id { if let Some(cipher_uuid) = &event.cipher_id {
if let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &mut conn).await { if let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &mut conn).await {
if let Some(org_uuid) = cipher.organization_uuid { if let Some(org_id) = cipher.organization_uuid {
_log_event( _log_event(
event.r#type, event.r#type,
cipher_uuid, cipher_uuid,
&org_uuid, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
Some(event_date), Some(event_date),
@ -218,38 +218,38 @@ async fn post_events_collect(data: Json<Vec<EventCollection>>, headers: Headers,
Ok(()) Ok(())
} }
pub async fn log_user_event(event_type: i32, user_uuid: &UserId, device_type: i32, ip: &IpAddr, conn: &mut DbConn) { pub async fn log_user_event(event_type: i32, user_id: &UserId, device_type: i32, ip: &IpAddr, conn: &mut DbConn) {
if !CONFIG.org_events_enabled() { if !CONFIG.org_events_enabled() {
return; return;
} }
_log_user_event(event_type, user_uuid, device_type, None, ip, conn).await; _log_user_event(event_type, user_id, device_type, None, ip, conn).await;
} }
async fn _log_user_event( async fn _log_user_event(
event_type: i32, event_type: i32,
user_uuid: &UserId, user_id: &UserId,
device_type: i32, device_type: i32,
event_date: Option<NaiveDateTime>, event_date: Option<NaiveDateTime>,
ip: &IpAddr, ip: &IpAddr,
conn: &mut DbConn, conn: &mut DbConn,
) { ) {
let orgs = Membership::get_orgs_by_user(user_uuid, conn).await; let orgs = Membership::get_orgs_by_user(user_id, conn).await;
let mut events: Vec<Event> = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org let mut events: Vec<Event> = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org
// Upstream saves the event also without any org_uuid. // Upstream saves the event also without any org_id.
let mut event = Event::new(event_type, event_date); let mut event = Event::new(event_type, event_date);
event.user_uuid = Some(user_uuid.clone()); event.user_uuid = Some(user_id.clone());
event.act_user_uuid = Some(user_uuid.to_string()); event.act_user_uuid = Some(user_id.to_string());
event.device_type = Some(device_type); event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string()); event.ip_address = Some(ip.to_string());
events.push(event); events.push(event);
// For each org a user is a member of store these events per org // For each org a user is a member of store these events per org
for org_uuid in orgs { for org_id in orgs {
let mut event = Event::new(event_type, event_date); let mut event = Event::new(event_type, event_date);
event.user_uuid = Some(user_uuid.clone()); event.user_uuid = Some(user_id.clone());
event.org_uuid = Some(org_uuid); event.org_uuid = Some(org_id);
event.act_user_uuid = Some(user_uuid.to_string()); event.act_user_uuid = Some(user_id.to_string());
event.device_type = Some(device_type); event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string()); event.ip_address = Some(ip.to_string());
events.push(event); events.push(event);
@ -261,8 +261,8 @@ async fn _log_user_event(
pub async fn log_event( pub async fn log_event(
event_type: i32, event_type: i32,
source_uuid: &str, source_uuid: &str,
org_uuid: &OrganizationId, org_id: &OrganizationId,
act_user_uuid: &str, act_user_id: &str,
device_type: i32, device_type: i32,
ip: &IpAddr, ip: &IpAddr,
conn: &mut DbConn, conn: &mut DbConn,
@ -270,15 +270,15 @@ pub async fn log_event(
if !CONFIG.org_events_enabled() { if !CONFIG.org_events_enabled() {
return; return;
} }
_log_event(event_type, source_uuid, org_uuid, act_user_uuid, device_type, None, ip, conn).await; _log_event(event_type, source_uuid, org_id, act_user_id, device_type, None, ip, conn).await;
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn _log_event( async fn _log_event(
event_type: i32, event_type: i32,
source_uuid: &str, source_uuid: &str,
org_uuid: &OrganizationId, org_id: &OrganizationId,
act_user_uuid: &str, act_user_id: &str,
device_type: i32, device_type: i32,
event_date: Option<NaiveDateTime>, event_date: Option<NaiveDateTime>,
ip: &IpAddr, ip: &IpAddr,
@ -313,8 +313,8 @@ async fn _log_event(
_ => {} _ => {}
} }
event.org_uuid = Some(org_uuid.clone()); event.org_uuid = Some(org_id.clone());
event.act_user_uuid = Some(String::from(act_user_uuid)); event.act_user_uuid = Some(String::from(act_user_id));
event.device_type = Some(device_type); event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string()); event.ip_address = Some(ip.to_string());
event.save(conn).await.unwrap_or(()); event.save(conn).await.unwrap_or(());

Datei anzeigen

@ -23,9 +23,9 @@ async fn get_folders(headers: Headers, mut conn: DbConn) -> Json<Value> {
})) }))
} }
#[get("/folders/<uuid>")] #[get("/folders/<folder_id>")]
async fn get_folder(uuid: FolderId, headers: Headers, mut conn: DbConn) -> JsonResult { async fn get_folder(folder_id: FolderId, headers: Headers, mut conn: DbConn) -> JsonResult {
match Folder::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await { match Folder::find_by_uuid_and_user(&folder_id, &headers.user.uuid, &mut conn).await {
Some(folder) => Ok(Json(folder.to_json())), Some(folder) => Ok(Json(folder.to_json())),
_ => err!("Invalid folder", "Folder does not exist or belongs to another user"), _ => err!("Invalid folder", "Folder does not exist or belongs to another user"),
} }
@ -50,20 +50,20 @@ async fn post_folders(data: Json<FolderData>, headers: Headers, mut conn: DbConn
Ok(Json(folder.to_json())) Ok(Json(folder.to_json()))
} }
#[post("/folders/<uuid>", data = "<data>")] #[post("/folders/<folder_id>", data = "<data>")]
async fn post_folder( async fn post_folder(
uuid: FolderId, folder_id: FolderId,
data: Json<FolderData>, data: Json<FolderData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
put_folder(uuid, data, headers, conn, nt).await put_folder(folder_id, data, headers, conn, nt).await
} }
#[put("/folders/<uuid>", data = "<data>")] #[put("/folders/<folder_id>", data = "<data>")]
async fn put_folder( async fn put_folder(
uuid: FolderId, folder_id: FolderId,
data: Json<FolderData>, data: Json<FolderData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -71,7 +71,7 @@ async fn put_folder(
) -> JsonResult { ) -> JsonResult {
let data: FolderData = data.into_inner(); let data: FolderData = data.into_inner();
let Some(mut folder) = Folder::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(mut folder) = Folder::find_by_uuid_and_user(&folder_id, &headers.user.uuid, &mut conn).await else {
err!("Invalid folder", "Folder does not exist or belongs to another user") err!("Invalid folder", "Folder does not exist or belongs to another user")
}; };
@ -83,14 +83,14 @@ async fn put_folder(
Ok(Json(folder.to_json())) Ok(Json(folder.to_json()))
} }
#[post("/folders/<uuid>/delete")] #[post("/folders/<folder_id>/delete")]
async fn delete_folder_post(uuid: FolderId, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_folder_post(folder_id: FolderId, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
delete_folder(uuid, headers, conn, nt).await delete_folder(folder_id, headers, conn, nt).await
} }
#[delete("/folders/<uuid>")] #[delete("/folders/<folder_id>")]
async fn delete_folder(uuid: FolderId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_folder(folder_id: FolderId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let Some(folder) = Folder::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(folder) = Folder::find_by_uuid_and_user(&folder_id, &headers.user.uuid, &mut conn).await else {
err!("Invalid folder", "Folder does not exist or belongs to another user") err!("Invalid folder", "Folder does not exist or belongs to another user")
}; };

Datei anzeigen

@ -753,14 +753,13 @@ async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) ->
}))) })))
} }
async fn _get_org_details(org_id: &OrganizationId, host: &str, user_uuid: &UserId, conn: &mut DbConn) -> Value { async fn _get_org_details(org_id: &OrganizationId, host: &str, user_id: &UserId, conn: &mut DbConn) -> Value {
let ciphers = Cipher::find_by_org(org_id, conn).await; let ciphers = Cipher::find_by_org(org_id, conn).await;
let cipher_sync_data = CipherSyncData::new(user_uuid, CipherSyncType::Organization, conn).await; let cipher_sync_data = CipherSyncData::new(user_id, CipherSyncType::Organization, conn).await;
let mut ciphers_json = Vec::with_capacity(ciphers.len()); let mut ciphers_json = Vec::with_capacity(ciphers.len());
for c in ciphers { for c in ciphers {
ciphers_json ciphers_json.push(c.to_json(host, user_id, Some(&cipher_sync_data), CipherSyncType::Organization, conn).await);
.push(c.to_json(host, user_uuid, Some(&cipher_sync_data), CipherSyncType::Organization, conn).await);
} }
json!(ciphers_json) json!(ciphers_json)
} }

Datei anzeigen

@ -219,11 +219,11 @@ impl<'r> FromRequest<'r> for PublicToken {
Outcome::Success(conn) => conn, Outcome::Success(conn) => conn,
_ => err_handler!("Error getting DB"), _ => err_handler!("Error getting DB"),
}; };
let Some(org_uuid) = claims.client_id.strip_prefix("organization.") else { let Some(org_id) = claims.client_id.strip_prefix("organization.") else {
err_handler!("Malformed client_id") err_handler!("Malformed client_id")
}; };
let org_uuid: OrganizationId = org_uuid.to_string().into(); let org_id: OrganizationId = org_id.to_string().into();
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_uuid, &conn).await else { let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_id, &conn).await else {
err_handler!("Invalid client_id") err_handler!("Invalid client_id")
}; };
if org_api_key.org_uuid != claims.client_sub { if org_api_key.org_uuid != claims.client_sub {

Datei anzeigen

@ -79,9 +79,9 @@ pub struct SendData {
/// There is also a Vaultwarden-specific `sends_allowed` config setting that /// There is also a Vaultwarden-specific `sends_allowed` config setting that
/// controls this policy globally. /// controls this policy globally.
async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> EmptyResult { async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> EmptyResult {
let user_uuid = &headers.user.uuid; let user_id = &headers.user.uuid;
if !CONFIG.sends_allowed() if !CONFIG.sends_allowed()
|| OrgPolicy::is_applicable_to_user(user_uuid, OrgPolicyType::DisableSend, None, conn).await || OrgPolicy::is_applicable_to_user(user_id, OrgPolicyType::DisableSend, None, conn).await
{ {
err!("Due to an Enterprise Policy, you are only able to delete an existing Send.") err!("Due to an Enterprise Policy, you are only able to delete an existing Send.")
} }
@ -95,9 +95,9 @@ async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> Em
/// ///
/// Ref: https://bitwarden.com/help/article/policies/#send-options /// Ref: https://bitwarden.com/help/article/policies/#send-options
async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &mut DbConn) -> EmptyResult { async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &mut DbConn) -> EmptyResult {
let user_uuid = &headers.user.uuid; let user_id = &headers.user.uuid;
let hide_email = data.hide_email.unwrap_or(false); let hide_email = data.hide_email.unwrap_or(false);
if hide_email && OrgPolicy::is_hide_email_disabled(user_uuid, conn).await { if hide_email && OrgPolicy::is_hide_email_disabled(user_id, conn).await {
err!( err!(
"Due to an Enterprise Policy, you are not allowed to hide your email address \ "Due to an Enterprise Policy, you are not allowed to hide your email address \
from recipients when creating or editing a Send." from recipients when creating or editing a Send."
@ -106,7 +106,7 @@ async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, c
Ok(()) Ok(())
} }
fn create_send(data: SendData, user_uuid: UserId) -> ApiResult<Send> { fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> {
let data_val = if data.r#type == SendType::Text as i32 { let data_val = if data.r#type == SendType::Text as i32 {
data.text data.text
} else if data.r#type == SendType::File as i32 { } else if data.r#type == SendType::File as i32 {
@ -129,7 +129,7 @@ fn create_send(data: SendData, user_uuid: UserId) -> ApiResult<Send> {
} }
let mut send = Send::new(data.r#type, data.name, data_str, data.key, data.deletion_date.naive_utc()); let mut send = Send::new(data.r#type, data.name, data_str, data.key, data.deletion_date.naive_utc());
send.user_uuid = Some(user_uuid); send.user_uuid = Some(user_id);
send.notes = data.notes; send.notes = data.notes;
send.max_access_count = match data.max_access_count { send.max_access_count = match data.max_access_count {
Some(m) => Some(m.into_i32()?), Some(m) => Some(m.into_i32()?),
@ -157,11 +157,11 @@ async fn get_sends(headers: Headers, mut conn: DbConn) -> Json<Value> {
})) }))
} }
#[get("/sends/<uuid>")] #[get("/sends/<send_id>")]
async fn get_send(uuid: SendId, headers: Headers, mut conn: DbConn) -> JsonResult { async fn get_send(send_id: SendId, headers: Headers, mut conn: DbConn) -> JsonResult {
match Send::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await { match Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &mut conn).await {
Some(send) => Ok(Json(send.to_json())), Some(send) => Ok(Json(send.to_json())),
None => err!("Send not found", "Invalid uuid or does not belong to user"), None => err!("Send not found", "Invalid send uuid or does not belong to user"),
} }
} }
@ -352,9 +352,9 @@ pub struct SendFileData {
} }
// https://github.com/bitwarden/server/blob/66f95d1c443490b653e5a15d32977e2f5a3f9e32/src/Api/Tools/Controllers/SendsController.cs#L250 // https://github.com/bitwarden/server/blob/66f95d1c443490b653e5a15d32977e2f5a3f9e32/src/Api/Tools/Controllers/SendsController.cs#L250
#[post("/sends/<uuid>/file/<file_id>", format = "multipart/form-data", data = "<data>")] #[post("/sends/<send_id>/file/<file_id>", format = "multipart/form-data", data = "<data>")]
async fn post_send_file_v2_data( async fn post_send_file_v2_data(
uuid: SendId, send_id: SendId,
file_id: SendFileId, file_id: SendFileId,
data: Form<UploadDataV2<'_>>, data: Form<UploadDataV2<'_>>,
headers: Headers, headers: Headers,
@ -365,8 +365,8 @@ async fn post_send_file_v2_data(
let mut data = data.into_inner(); let mut data = data.into_inner();
let Some(send) = Send::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &mut conn).await else {
err!("Send not found. Unable to save the file.", "Invalid uuid or does not belong to user.") err!("Send not found. Unable to save the file.", "Invalid send uuid or does not belong to user.")
}; };
if send.atype != SendType::File as i32 { if send.atype != SendType::File as i32 {
@ -402,7 +402,7 @@ async fn post_send_file_v2_data(
err!("Send file size does not match.", format!("Expected a file size of {} got {size}", send_data.size)); err!("Send file size does not match.", format!("Expected a file size of {} got {size}", send_data.size));
} }
let folder_path = tokio::fs::canonicalize(&CONFIG.sends_folder()).await?.join(uuid); let folder_path = tokio::fs::canonicalize(&CONFIG.sends_folder()).await?.join(send_id);
let file_path = folder_path.join(file_id); let file_path = folder_path.join(file_id);
// Check if the file already exists, if that is the case do not overwrite it // Check if the file already exists, if that is the case do not overwrite it
@ -493,16 +493,16 @@ async fn post_access(
Ok(Json(send.to_json_access(&mut conn).await)) Ok(Json(send.to_json_access(&mut conn).await))
} }
#[post("/sends/<uuid>/access/file/<file_id>", data = "<data>")] #[post("/sends/<send_id>/access/file/<file_id>", data = "<data>")]
async fn post_access_file( async fn post_access_file(
uuid: SendId, send_id: SendId,
file_id: SendFileId, file_id: SendFileId,
data: Json<SendAccessData>, data: Json<SendAccessData>,
host: Host, host: Host,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let Some(mut send) = Send::find_by_uuid(&uuid, &mut conn).await else { let Some(mut send) = Send::find_by_uuid(&send_id, &mut conn).await else {
err_code!(SEND_INACCESSIBLE_MSG, 404) err_code!(SEND_INACCESSIBLE_MSG, 404)
}; };
@ -547,28 +547,28 @@ async fn post_access_file(
) )
.await; .await;
let token_claims = crate::auth::generate_send_claims(&uuid, &file_id); let token_claims = crate::auth::generate_send_claims(&send_id, &file_id);
let token = crate::auth::encode_jwt(&token_claims); let token = crate::auth::encode_jwt(&token_claims);
Ok(Json(json!({ Ok(Json(json!({
"object": "send-fileDownload", "object": "send-fileDownload",
"id": file_id, "id": file_id,
"url": format!("{}/api/sends/{}/{}?t={}", &host.host, uuid, file_id, token) "url": format!("{}/api/sends/{}/{}?t={}", &host.host, send_id, file_id, token)
}))) })))
} }
#[get("/sends/<uuid>/<file_id>?<t>")] #[get("/sends/<send_id>/<file_id>?<t>")]
async fn download_send(uuid: SendId, file_id: SendFileId, t: &str) -> Option<NamedFile> { async fn download_send(send_id: SendId, file_id: SendFileId, t: &str) -> Option<NamedFile> {
if let Ok(claims) = crate::auth::decode_send(t) { if let Ok(claims) = crate::auth::decode_send(t) {
if claims.sub == format!("{uuid}/{file_id}") { if claims.sub == format!("{send_id}/{file_id}") {
return NamedFile::open(Path::new(&CONFIG.sends_folder()).join(uuid).join(file_id)).await.ok(); return NamedFile::open(Path::new(&CONFIG.sends_folder()).join(send_id).join(file_id)).await.ok();
} }
} }
None None
} }
#[put("/sends/<uuid>", data = "<data>")] #[put("/sends/<send_id>", data = "<data>")]
async fn put_send( async fn put_send(
uuid: SendId, send_id: SendId,
data: Json<SendData>, data: Json<SendData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
@ -579,8 +579,8 @@ async fn put_send(
let data: SendData = data.into_inner(); let data: SendData = data.into_inner();
enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?;
let Some(mut send) = Send::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(mut send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &mut conn).await else {
err!("Send not found", "Send uuid is invalid or does not belong to user") err!("Send not found", "Send send_id is invalid or does not belong to user")
}; };
update_send_from_data(&mut send, data, &headers, &mut conn, &nt, UpdateType::SyncSendUpdate).await?; update_send_from_data(&mut send, data, &headers, &mut conn, &nt, UpdateType::SyncSendUpdate).await?;
@ -646,9 +646,9 @@ pub async fn update_send_from_data(
Ok(()) Ok(())
} }
#[delete("/sends/<uuid>")] #[delete("/sends/<send_id>")]
async fn delete_send(uuid: SendId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn delete_send(send_id: SendId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let Some(send) = Send::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &mut conn).await else {
err!("Send not found", "Invalid send uuid, or does not belong to user") err!("Send not found", "Invalid send uuid, or does not belong to user")
}; };
@ -665,11 +665,11 @@ async fn delete_send(uuid: SendId, headers: Headers, mut conn: DbConn, nt: Notif
Ok(()) Ok(())
} }
#[put("/sends/<uuid>/remove-password")] #[put("/sends/<send_id>/remove-password")]
async fn put_remove_password(uuid: SendId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { async fn put_remove_password(send_id: SendId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
enforce_disable_send_policy(&headers, &mut conn).await?; enforce_disable_send_policy(&headers, &mut conn).await?;
let Some(mut send) = Send::find_by_uuid_and_user(&uuid, &headers.user.uuid, &mut conn).await else { let Some(mut send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &mut conn).await else {
err!("Send not found", "Invalid send uuid, or does not belong to user") err!("Send not found", "Invalid send uuid, or does not belong to user")
}; };

Datei anzeigen

@ -95,7 +95,7 @@ async fn activate_authenticator_put(data: Json<EnableAuthenticatorData>, headers
} }
pub async fn validate_totp_code_str( pub async fn validate_totp_code_str(
user_uuid: &UserId, user_id: &UserId,
totp_code: &str, totp_code: &str,
secret: &str, secret: &str,
ip: &ClientIp, ip: &ClientIp,
@ -105,11 +105,11 @@ pub async fn validate_totp_code_str(
err!("TOTP code is not a number"); err!("TOTP code is not a number");
} }
validate_totp_code(user_uuid, totp_code, secret, ip, conn).await validate_totp_code(user_id, totp_code, secret, ip, conn).await
} }
pub async fn validate_totp_code( pub async fn validate_totp_code(
user_uuid: &UserId, user_id: &UserId,
totp_code: &str, totp_code: &str,
secret: &str, secret: &str,
ip: &ClientIp, ip: &ClientIp,
@ -121,11 +121,11 @@ pub async fn validate_totp_code(
err!("Invalid TOTP secret") err!("Invalid TOTP secret")
}; };
let mut twofactor = let mut twofactor = match TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Authenticator as i32, conn).await
match TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Authenticator as i32, conn).await { {
Some(tf) => tf, Some(tf) => tf,
_ => TwoFactor::new(user_uuid.clone(), TwoFactorType::Authenticator, secret.to_string()), _ => TwoFactor::new(user_id.clone(), TwoFactorType::Authenticator, secret.to_string()),
}; };
// The amount of steps back and forward in time // The amount of steps back and forward in time
// Also check if we need to disable time drifted TOTP codes. // Also check if we need to disable time drifted TOTP codes.

Datei anzeigen

@ -228,11 +228,11 @@ const AUTH_PREFIX: &str = "AUTH";
const DUO_PREFIX: &str = "TX"; const DUO_PREFIX: &str = "TX";
const APP_PREFIX: &str = "APP"; const APP_PREFIX: &str = "APP";
async fn get_user_duo_data(user_uuid: &UserId, conn: &mut DbConn) -> DuoStatus { async fn get_user_duo_data(user_id: &UserId, conn: &mut DbConn) -> DuoStatus {
let type_ = TwoFactorType::Duo as i32; let type_ = TwoFactorType::Duo as i32;
// If the user doesn't have an entry, disabled // If the user doesn't have an entry, disabled
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, type_, conn).await else {
return DuoStatus::Disabled(DuoData::global().is_some()); return DuoStatus::Disabled(DuoData::global().is_some());
}; };

Datei anzeigen

@ -59,10 +59,9 @@ async fn send_email_login(data: Json<SendEmailLoginData>, mut conn: DbConn) -> E
} }
/// Generate the token, save the data for later verification and send email to user /// Generate the token, save the data for later verification and send email to user
pub async fn send_token(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult { pub async fn send_token(user_id: &UserId, conn: &mut DbConn) -> EmptyResult {
let type_ = TwoFactorType::Email as i32; let type_ = TwoFactorType::Email as i32;
let mut twofactor = let mut twofactor = TwoFactor::find_by_user_and_type(user_id, type_, conn).await.map_res("Two factor not found")?;
TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await.map_res("Two factor not found")?;
let generated_token = crypto::generate_email_token(CONFIG.email_token_size()); let generated_token = crypto::generate_email_token(CONFIG.email_token_size());
@ -198,9 +197,9 @@ async fn email(data: Json<EmailData>, headers: Headers, mut conn: DbConn) -> Jso
} }
/// Validate the email code when used as TwoFactor token mechanism /// Validate the email code when used as TwoFactor token mechanism
pub async fn validate_email_code_str(user_uuid: &UserId, token: &str, data: &str, conn: &mut DbConn) -> EmptyResult { pub async fn validate_email_code_str(user_id: &UserId, token: &str, data: &str, conn: &mut DbConn) -> EmptyResult {
let mut email_data = EmailTokenData::from_json(data)?; let mut email_data = EmailTokenData::from_json(data)?;
let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn) let mut twofactor = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::Email as i32, conn)
.await .await
.map_res("Two factor not found")?; .map_res("Two factor not found")?;
let Some(issued_token) = &email_data.last_token else { let Some(issued_token) = &email_data.last_token else {
@ -327,8 +326,8 @@ pub fn obscure_email(email: &str) -> String {
format!("{}@{}", new_name, &domain) format!("{}@{}", new_name, &domain)
} }
pub async fn find_and_activate_email_2fa(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult { pub async fn find_and_activate_email_2fa(user_id: &UserId, conn: &mut DbConn) -> EmptyResult {
if let Some(user) = User::find_by_uuid(user_uuid, conn).await { if let Some(user) = User::find_by_uuid(user_id, conn).await {
activate_email_2fa(&user, conn).await activate_email_2fa(&user, conn).await
} else { } else {
err!("User not found!"); err!("User not found!");

Datei anzeigen

@ -173,7 +173,7 @@ async fn disable_twofactor_put(data: Json<DisableTwoFactorData>, headers: Header
pub async fn enforce_2fa_policy( pub async fn enforce_2fa_policy(
user: &User, user: &User,
act_uuid: &str, act_user_id: &str,
device_type: i32, device_type: i32,
ip: &std::net::IpAddr, ip: &std::net::IpAddr,
conn: &mut DbConn, conn: &mut DbConn,
@ -195,7 +195,7 @@ pub async fn enforce_2fa_policy(
EventType::OrganizationUserRevoked as i32, EventType::OrganizationUserRevoked as i32,
&member.uuid, &member.uuid,
&member.org_uuid, &member.org_uuid,
act_uuid, act_user_id,
device_type, device_type,
ip, ip,
conn, conn,
@ -208,14 +208,14 @@ pub async fn enforce_2fa_policy(
} }
pub async fn enforce_2fa_policy_for_org( pub async fn enforce_2fa_policy_for_org(
org_uuid: &OrganizationId, org_id: &OrganizationId,
act_uuid: &str, act_user_id: &str,
device_type: i32, device_type: i32,
ip: &std::net::IpAddr, ip: &std::net::IpAddr,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap(); let org = Organization::find_by_uuid(org_id, conn).await.unwrap();
for member in Membership::find_confirmed_by_org(org_uuid, conn).await.into_iter() { for member in Membership::find_confirmed_by_org(org_id, conn).await.into_iter() {
// Don't enforce the policy for Admins and Owners. // Don't enforce the policy for Admins and Owners.
if member.atype < MembershipType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() { if member.atype < MembershipType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() {
if CONFIG.mail_enabled() { if CONFIG.mail_enabled() {
@ -229,8 +229,8 @@ pub async fn enforce_2fa_policy_for_org(
log_event( log_event(
EventType::OrganizationUserRevoked as i32, EventType::OrganizationUserRevoked as i32,
&member.uuid, &member.uuid,
org_uuid, org_id,
act_uuid, act_user_id,
device_type, device_type,
ip, ip,
conn, conn,

Datei anzeigen

@ -104,11 +104,11 @@ async fn verify_otp(data: Json<ProtectedActionVerify>, headers: Headers, mut con
pub async fn validate_protected_action_otp( pub async fn validate_protected_action_otp(
otp: &str, otp: &str,
user_uuid: &UserId, user_id: &UserId,
delete_if_valid: bool, delete_if_valid: bool,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
let pa = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::ProtectedActions as i32, conn) let pa = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::ProtectedActions as i32, conn)
.await .await
.map_res("Protected action token not found, try sending the code again or restart the process")?; .map_res("Protected action token not found, try sending the code again or restart the process")?;
let mut pa_data = ProtectedActionData::from_json(&pa.data)?; let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

Datei anzeigen

@ -352,20 +352,20 @@ async fn delete_webauthn(data: Json<DeleteU2FData>, headers: Headers, mut conn:
} }
pub async fn get_webauthn_registrations( pub async fn get_webauthn_registrations(
user_uuid: &UserId, user_id: &UserId,
conn: &mut DbConn, conn: &mut DbConn,
) -> Result<(bool, Vec<WebauthnRegistration>), Error> { ) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
let type_ = TwoFactorType::Webauthn as i32; let type_ = TwoFactorType::Webauthn as i32;
match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { match TwoFactor::find_by_user_and_type(user_id, type_, conn).await {
Some(tf) => Ok((tf.enabled, serde_json::from_str(&tf.data)?)), Some(tf) => Ok((tf.enabled, serde_json::from_str(&tf.data)?)),
None => Ok((false, Vec::new())), // If no data, return empty list None => Ok((false, Vec::new())), // If no data, return empty list
} }
} }
pub async fn generate_webauthn_login(user_uuid: &UserId, conn: &mut DbConn) -> JsonResult { pub async fn generate_webauthn_login(user_id: &UserId, conn: &mut DbConn) -> JsonResult {
// Load saved credentials // Load saved credentials
let creds: Vec<Credential> = let creds: Vec<Credential> =
get_webauthn_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.credential).collect(); get_webauthn_registrations(user_id, conn).await?.1.into_iter().map(|r| r.credential).collect();
if creds.is_empty() { if creds.is_empty() {
err!("No Webauthn devices registered") err!("No Webauthn devices registered")
@ -376,7 +376,7 @@ pub async fn generate_webauthn_login(user_uuid: &UserId, conn: &mut DbConn) -> J
let (response, state) = WebauthnConfig::load().generate_challenge_authenticate_options(creds, Some(ext))?; let (response, state) = WebauthnConfig::load().generate_challenge_authenticate_options(creds, Some(ext))?;
// Save the challenge state for later validation // Save the challenge state for later validation
TwoFactor::new(user_uuid.clone(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?) TwoFactor::new(user_id.clone(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?)
.save(conn) .save(conn)
.await?; .await?;
@ -384,9 +384,9 @@ pub async fn generate_webauthn_login(user_uuid: &UserId, conn: &mut DbConn) -> J
Ok(Json(serde_json::to_value(response.public_key)?)) Ok(Json(serde_json::to_value(response.public_key)?))
} }
pub async fn validate_webauthn_login(user_uuid: &UserId, response: &str, conn: &mut DbConn) -> EmptyResult { pub async fn validate_webauthn_login(user_id: &UserId, response: &str, conn: &mut DbConn) -> EmptyResult {
let type_ = TwoFactorType::WebauthnLoginChallenge as i32; let type_ = TwoFactorType::WebauthnLoginChallenge as i32;
let state = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { let state = match TwoFactor::find_by_user_and_type(user_id, type_, conn).await {
Some(tf) => { Some(tf) => {
let state: AuthenticationState = serde_json::from_str(&tf.data)?; let state: AuthenticationState = serde_json::from_str(&tf.data)?;
tf.delete(conn).await?; tf.delete(conn).await?;
@ -403,7 +403,7 @@ pub async fn validate_webauthn_login(user_uuid: &UserId, response: &str, conn: &
let rsp: PublicKeyCredentialCopy = serde_json::from_str(response)?; let rsp: PublicKeyCredentialCopy = serde_json::from_str(response)?;
let rsp: PublicKeyCredential = rsp.into(); let rsp: PublicKeyCredential = rsp.into();
let mut registrations = get_webauthn_registrations(user_uuid, conn).await?.1; let mut registrations = get_webauthn_registrations(user_id, conn).await?.1;
// If the credential we received is migrated from U2F, enable the U2F compatibility // If the credential we received is migrated from U2F, enable the U2F compatibility
//let use_u2f = registrations.iter().any(|r| r.migrated && r.credential.cred_id == rsp.raw_id.0); //let use_u2f = registrations.iter().any(|r| r.migrated && r.credential.cred_id == rsp.raw_id.0);
@ -413,7 +413,7 @@ pub async fn validate_webauthn_login(user_uuid: &UserId, response: &str, conn: &
if &reg.credential.cred_id == cred_id { if &reg.credential.cred_id == cred_id {
reg.credential.counter = auth_data.counter; reg.credential.counter = auth_data.counter;
TwoFactor::new(user_uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?) TwoFactor::new(user_id.clone(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?)
.save(conn) .save(conn)
.await?; .await?;
return Ok(()); return Ok(());

Datei anzeigen

@ -92,10 +92,10 @@ async fn generate_yubikey(data: Json<PasswordOrOtpData>, headers: Headers, mut c
data.validate(&user, false, &mut conn).await?; data.validate(&user, false, &mut conn).await?;
let user_uuid = &user.uuid; let user_id = &user.uuid;
let yubikey_type = TwoFactorType::YubiKey as i32; let yubikey_type = TwoFactorType::YubiKey as i32;
let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &mut conn).await; let r = TwoFactor::find_by_user_and_type(user_id, yubikey_type, &mut conn).await;
if let Some(r) = r { if let Some(r) = r {
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?; let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;

Datei anzeigen

@ -31,7 +31,7 @@ pub fn routes() -> Vec<Route> {
async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult { async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult {
let data: ConnectData = data.into_inner(); let data: ConnectData = data.into_inner();
let mut user_uuid: Option<UserId> = None; let mut user_id: Option<UserId> = None;
let login_result = match data.grant_type.as_ref() { let login_result = match data.grant_type.as_ref() {
"refresh_token" => { "refresh_token" => {
@ -48,7 +48,7 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn:
_check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_name, "device_name cannot be blank")?;
_check_is_some(&data.device_type, "device_type cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?;
_password_login(data, &mut user_uuid, &mut conn, &client_header.ip).await _password_login(data, &mut user_id, &mut conn, &client_header.ip).await
} }
"client_credentials" => { "client_credentials" => {
_check_is_some(&data.client_id, "client_id cannot be blank")?; _check_is_some(&data.client_id, "client_id cannot be blank")?;
@ -59,17 +59,17 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn:
_check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_name, "device_name cannot be blank")?;
_check_is_some(&data.device_type, "device_type cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?;
_api_key_login(data, &mut user_uuid, &mut conn, &client_header.ip).await _api_key_login(data, &mut user_id, &mut conn, &client_header.ip).await
} }
t => err!("Invalid type", t), t => err!("Invalid type", t),
}; };
if let Some(user_uuid) = user_uuid { if let Some(user_id) = user_id {
match &login_result { match &login_result {
Ok(_) => { Ok(_) => {
log_user_event( log_user_event(
EventType::UserLoggedIn as i32, EventType::UserLoggedIn as i32,
&user_uuid, &user_id,
client_header.device_type, client_header.device_type,
&client_header.ip.ip, &client_header.ip.ip,
&mut conn, &mut conn,
@ -80,7 +80,7 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn:
if let Some(ev) = e.get_event() { if let Some(ev) = e.get_event() {
log_user_event( log_user_event(
ev.event as i32, ev.event as i32,
&user_uuid, &user_id,
client_header.device_type, client_header.device_type,
&client_header.ip.ip, &client_header.ip.ip,
&mut conn, &mut conn,
@ -141,7 +141,7 @@ struct MasterPasswordPolicy {
async fn _password_login( async fn _password_login(
data: ConnectData, data: ConnectData,
user_uuid: &mut Option<UserId>, user_id: &mut Option<UserId>,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp, ip: &ClientIp,
) -> JsonResult { ) -> JsonResult {
@ -161,8 +161,8 @@ async fn _password_login(
err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)) err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username))
}; };
// Set the user_uuid here to be passed back used for event logging. // Set the user_id here to be passed back used for event logging.
*user_uuid = Some(user.uuid.clone()); *user_id = Some(user.uuid.clone());
// Check if the user is disabled // Check if the user is disabled
if !user.enabled { if !user.enabled {
@ -359,7 +359,7 @@ async fn _password_login(
async fn _api_key_login( async fn _api_key_login(
data: ConnectData, data: ConnectData,
user_uuid: &mut Option<UserId>, user_id: &mut Option<UserId>,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp, ip: &ClientIp,
) -> JsonResult { ) -> JsonResult {
@ -368,7 +368,7 @@ async fn _api_key_login(
// Validate scope // Validate scope
match data.scope.as_ref().unwrap().as_ref() { match data.scope.as_ref().unwrap().as_ref() {
"api" => _user_api_key_login(data, user_uuid, conn, ip).await, "api" => _user_api_key_login(data, user_id, conn, ip).await,
"api.organization" => _organization_api_key_login(data, conn, ip).await, "api.organization" => _organization_api_key_login(data, conn, ip).await,
_ => err!("Scope not supported"), _ => err!("Scope not supported"),
} }
@ -376,22 +376,22 @@ async fn _api_key_login(
async fn _user_api_key_login( async fn _user_api_key_login(
data: ConnectData, data: ConnectData,
user_uuid: &mut Option<UserId>, user_id: &mut Option<UserId>,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp, ip: &ClientIp,
) -> JsonResult { ) -> JsonResult {
// Get the user via the client_id // Get the user via the client_id
let client_id = data.client_id.as_ref().unwrap(); let client_id = data.client_id.as_ref().unwrap();
let Some(client_user_uuid) = client_id.strip_prefix("user.") else { let Some(client_user_id) = client_id.strip_prefix("user.") else {
err!("Malformed client_id", format!("IP: {}.", ip.ip)) err!("Malformed client_id", format!("IP: {}.", ip.ip))
}; };
let client_user_uuid: UserId = client_user_uuid.into(); let client_user_id: UserId = client_user_id.into();
let Some(user) = User::find_by_uuid(&client_user_uuid, conn).await else { let Some(user) = User::find_by_uuid(&client_user_id, conn).await else {
err!("Invalid client_id", format!("IP: {}.", ip.ip)) err!("Invalid client_id", format!("IP: {}.", ip.ip))
}; };
// Set the user_uuid here to be passed back used for event logging. // Set the user_id here to be passed back used for event logging.
*user_uuid = Some(user.uuid.clone()); *user_id = Some(user.uuid.clone());
// Check if the user is disabled // Check if the user is disabled
if !user.enabled { if !user.enabled {
@ -470,11 +470,11 @@ async fn _user_api_key_login(
async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &ClientIp) -> JsonResult { async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &ClientIp) -> JsonResult {
// Get the org via the client_id // Get the org via the client_id
let client_id = data.client_id.as_ref().unwrap(); let client_id = data.client_id.as_ref().unwrap();
let Some(org_uuid) = client_id.strip_prefix("organization.") else { let Some(org_id) = client_id.strip_prefix("organization.") else {
err!("Malformed client_id", format!("IP: {}.", ip.ip)) err!("Malformed client_id", format!("IP: {}.", ip.ip))
}; };
let org_uuid: OrganizationId = org_uuid.to_string().into(); let org_id: OrganizationId = org_id.to_string().into();
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_uuid, conn).await else { let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_id, conn).await else {
err!("Invalid client_id", format!("IP: {}.", ip.ip)) err!("Invalid client_id", format!("IP: {}.", ip.ip))
}; };
@ -616,7 +616,7 @@ fn _selected_data(tf: Option<TwoFactor>) -> ApiResult<String> {
async fn _json_err_twofactor( async fn _json_err_twofactor(
providers: &[i32], providers: &[i32],
user_uuid: &UserId, user_id: &UserId,
data: &ConnectData, data: &ConnectData,
conn: &mut DbConn, conn: &mut DbConn,
) -> ApiResult<Value> { ) -> ApiResult<Value> {
@ -637,12 +637,12 @@ async fn _json_err_twofactor(
Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ }
Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => { Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => {
let request = webauthn::generate_webauthn_login(user_uuid, conn).await?; let request = webauthn::generate_webauthn_login(user_id, conn).await?;
result["TwoFactorProviders2"][provider.to_string()] = request.0; result["TwoFactorProviders2"][provider.to_string()] = request.0;
} }
Some(TwoFactorType::Duo) => { Some(TwoFactorType::Duo) => {
let email = match User::find_by_uuid(user_uuid, conn).await { let email = match User::find_by_uuid(user_id, conn).await {
Some(u) => u.email, Some(u) => u.email,
None => err!("User does not exist"), None => err!("User does not exist"),
}; };
@ -674,7 +674,7 @@ async fn _json_err_twofactor(
} }
Some(tf_type @ TwoFactorType::YubiKey) => { Some(tf_type @ TwoFactorType::YubiKey) => {
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type as i32, conn).await else {
err!("No YubiKey devices registered") err!("No YubiKey devices registered")
}; };
@ -686,13 +686,13 @@ async fn _json_err_twofactor(
} }
Some(tf_type @ TwoFactorType::Email) => { Some(tf_type @ TwoFactorType::Email) => {
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await else { let Some(twofactor) = TwoFactor::find_by_user_and_type(user_id, tf_type as i32, conn).await else {
err!("No twofactor email registered") err!("No twofactor email registered")
}; };
// Send email immediately if email is the only 2FA option // Send email immediately if email is the only 2FA option
if providers.len() == 1 { if providers.len() == 1 {
email::send_token(user_uuid, conn).await? email::send_token(user_id, conn).await?
} }
let email_data = email::EmailTokenData::from_json(&twofactor.data)?; let email_data = email::EmailTokenData::from_json(&twofactor.data)?;

Datei anzeigen

@ -328,8 +328,8 @@ pub struct WebSocketUsers {
} }
impl WebSocketUsers { impl WebSocketUsers {
async fn send_update(&self, user_uuid: &UserId, data: &[u8]) { async fn send_update(&self, user_id: &UserId, data: &[u8]) {
if let Some(user) = self.map.get(user_uuid.as_ref()).map(|v| v.clone()) { if let Some(user) = self.map.get(user_id.as_ref()).map(|v| v.clone()) {
for (_, sender) in user.iter() { for (_, sender) in user.iter() {
if let Err(e) = sender.send(Message::binary(data)).await { if let Err(e) = sender.send(Message::binary(data)).await {
error!("Error sending WS update {e}"); error!("Error sending WS update {e}");
@ -413,7 +413,7 @@ impl WebSocketUsers {
&self, &self,
ut: UpdateType, ut: UpdateType,
cipher: &Cipher, cipher: &Cipher,
user_uuids: &[UserId], user_ids: &[UserId],
acting_device_uuid: &DeviceId, acting_device_uuid: &DeviceId,
collection_uuids: Option<Vec<CollectionId>>, collection_uuids: Option<Vec<CollectionId>>,
conn: &mut DbConn, conn: &mut DbConn,
@ -422,10 +422,10 @@ impl WebSocketUsers {
if *NOTIFICATIONS_DISABLED { if *NOTIFICATIONS_DISABLED {
return; return;
} }
let org_uuid = convert_option(cipher.organization_uuid.as_deref()); let org_id = convert_option(cipher.organization_uuid.as_deref());
// Depending if there are collections provided or not, we need to have different values for the following variables. // Depending if there are collections provided or not, we need to have different values for the following variables.
// The user_uuid should be `null`, and the revision date should be set to now, else the clients won't sync the collection change. // The user_uuid should be `null`, and the revision date should be set to now, else the clients won't sync the collection change.
let (user_uuid, collection_uuids, revision_date) = if let Some(collection_uuids) = collection_uuids { let (user_id, collection_uuids, revision_date) = if let Some(collection_uuids) = collection_uuids {
( (
Value::Nil, Value::Nil,
Value::Array(collection_uuids.into_iter().map(|v| v.to_string().into()).collect::<Vec<Value>>()), Value::Array(collection_uuids.into_iter().map(|v| v.to_string().into()).collect::<Vec<Value>>()),
@ -438,8 +438,8 @@ impl WebSocketUsers {
let data = create_update( let data = create_update(
vec![ vec![
("Id".into(), cipher.uuid.to_string().into()), ("Id".into(), cipher.uuid.to_string().into()),
("UserId".into(), user_uuid), ("UserId".into(), user_id),
("OrganizationId".into(), org_uuid), ("OrganizationId".into(), org_id),
("CollectionIds".into(), collection_uuids), ("CollectionIds".into(), collection_uuids),
("RevisionDate".into(), revision_date), ("RevisionDate".into(), revision_date),
], ],
@ -448,12 +448,12 @@ impl WebSocketUsers {
); );
if CONFIG.enable_websocket() { if CONFIG.enable_websocket() {
for uuid in user_uuids { for uuid in user_ids {
self.send_update(uuid, &data).await; self.send_update(uuid, &data).await;
} }
} }
if CONFIG.push_enabled() && user_uuids.len() == 1 { if CONFIG.push_enabled() && user_ids.len() == 1 {
push_cipher_update(ut, cipher, acting_device_uuid, conn).await; push_cipher_update(ut, cipher, acting_device_uuid, conn).await;
} }
} }
@ -462,7 +462,7 @@ impl WebSocketUsers {
&self, &self,
ut: UpdateType, ut: UpdateType,
send: &DbSend, send: &DbSend,
user_uuids: &[UserId], user_ids: &[UserId],
acting_device_uuid: &DeviceId, acting_device_uuid: &DeviceId,
conn: &mut DbConn, conn: &mut DbConn,
) { ) {
@ -470,12 +470,12 @@ impl WebSocketUsers {
if *NOTIFICATIONS_DISABLED { if *NOTIFICATIONS_DISABLED {
return; return;
} }
let user_uuid = convert_option(send.user_uuid.as_deref()); let user_id = convert_option(send.user_uuid.as_deref());
let data = create_update( let data = create_update(
vec![ vec![
("Id".into(), send.uuid.to_string().into()), ("Id".into(), send.uuid.to_string().into()),
("UserId".into(), user_uuid), ("UserId".into(), user_id),
("RevisionDate".into(), serialize_date(send.revision_date)), ("RevisionDate".into(), serialize_date(send.revision_date)),
], ],
ut, ut,
@ -483,18 +483,18 @@ impl WebSocketUsers {
); );
if CONFIG.enable_websocket() { if CONFIG.enable_websocket() {
for uuid in user_uuids { for uuid in user_ids {
self.send_update(uuid, &data).await; self.send_update(uuid, &data).await;
} }
} }
if CONFIG.push_enabled() && user_uuids.len() == 1 { if CONFIG.push_enabled() && user_ids.len() == 1 {
push_send_update(ut, send, acting_device_uuid, conn).await; push_send_update(ut, send, acting_device_uuid, conn).await;
} }
} }
pub async fn send_auth_request( pub async fn send_auth_request(
&self, &self,
user_uuid: &UserId, user_id: &UserId,
auth_request_uuid: &String, auth_request_uuid: &String,
acting_device_uuid: &DeviceId, acting_device_uuid: &DeviceId,
conn: &mut DbConn, conn: &mut DbConn,
@ -504,22 +504,22 @@ impl WebSocketUsers {
return; return;
} }
let data = create_update( let data = create_update(
vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.to_string().into())], vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_id.to_string().into())],
UpdateType::AuthRequest, UpdateType::AuthRequest,
Some(acting_device_uuid.to_string()), Some(acting_device_uuid.to_string()),
); );
if CONFIG.enable_websocket() { if CONFIG.enable_websocket() {
self.send_update(user_uuid, &data).await; self.send_update(user_id, &data).await;
} }
if CONFIG.push_enabled() { if CONFIG.push_enabled() {
push_auth_request(user_uuid.clone(), auth_request_uuid.to_string(), conn).await; push_auth_request(user_id.clone(), auth_request_uuid.to_string(), conn).await;
} }
} }
pub async fn send_auth_response( pub async fn send_auth_response(
&self, &self,
user_uuid: &UserId, user_id: &UserId,
auth_response_uuid: &str, auth_response_uuid: &str,
approving_device_uuid: DeviceId, approving_device_uuid: DeviceId,
conn: &mut DbConn, conn: &mut DbConn,
@ -529,16 +529,16 @@ impl WebSocketUsers {
return; return;
} }
let data = create_update( let data = create_update(
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())], vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_id.to_string().into())],
UpdateType::AuthRequestResponse, UpdateType::AuthRequestResponse,
Some(approving_device_uuid.to_string()), Some(approving_device_uuid.to_string()),
); );
if CONFIG.enable_websocket() { if CONFIG.enable_websocket() {
self.send_update(user_uuid, &data).await; self.send_update(user_id, &data).await;
} }
if CONFIG.push_enabled() { if CONFIG.push_enabled() {
push_auth_response(user_uuid.clone(), auth_response_uuid.to_string(), approving_device_uuid, conn).await; push_auth_response(user_id.clone(), auth_response_uuid.to_string(), approving_device_uuid, conn).await;
} }
} }
} }
@ -557,16 +557,16 @@ impl AnonymousWebSocketSubscriptions {
} }
} }
pub async fn send_auth_response(&self, user_uuid: &UserId, auth_response_uuid: &str) { pub async fn send_auth_response(&self, user_id: &UserId, auth_response_uuid: &str) {
if !CONFIG.enable_websocket() { if !CONFIG.enable_websocket() {
return; return;
} }
let data = create_anonymous_update( let data = create_anonymous_update(
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())], vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_id.to_string().into())],
UpdateType::AuthRequestResponse, UpdateType::AuthRequestResponse,
user_uuid.clone(), user_id.clone(),
); );
self.send_update(user_uuid, &data).await; self.send_update(user_id, &data).await;
} }
} }

Datei anzeigen

@ -126,15 +126,15 @@ pub async fn register_push_device(device: &mut Device, conn: &mut crate::db::DbC
Ok(()) Ok(())
} }
pub async fn unregister_push_device(push_uuid: Option<String>) -> EmptyResult { pub async fn unregister_push_device(push_id: Option<String>) -> EmptyResult {
if !CONFIG.push_enabled() || push_uuid.is_none() { if !CONFIG.push_enabled() || push_id.is_none() {
return Ok(()); return Ok(());
} }
let auth_push_token = get_auth_push_token().await?; let auth_push_token = get_auth_push_token().await?;
let auth_header = format!("Bearer {}", &auth_push_token); let auth_header = format!("Bearer {}", &auth_push_token);
match make_http_request(Method::DELETE, &(CONFIG.push_relay_uri() + "/push/" + &push_uuid.unwrap()))? match make_http_request(Method::DELETE, &(CONFIG.push_relay_uri() + "/push/" + &push_id.unwrap()))?
.header(AUTHORIZATION, auth_header) .header(AUTHORIZATION, auth_header)
.send() .send()
.await .await
@ -148,24 +148,24 @@ pub async fn unregister_push_device(push_uuid: Option<String>) -> EmptyResult {
pub async fn push_cipher_update( pub async fn push_cipher_update(
ut: UpdateType, ut: UpdateType,
cipher: &Cipher, cipher: &Cipher,
acting_device_uuid: &DeviceId, acting_device_id: &DeviceId,
conn: &mut crate::db::DbConn, conn: &mut crate::db::DbConn,
) { ) {
// We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too. // We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too.
if cipher.organization_uuid.is_some() { if cipher.organization_uuid.is_some() {
return; return;
}; };
let Some(user_uuid) = &cipher.user_uuid else { let Some(user_id) = &cipher.user_uuid else {
debug!("Cipher has no uuid"); debug!("Cipher has no uuid");
return; return;
}; };
if Device::check_user_has_push_device(user_uuid, conn).await { if Device::check_user_has_push_device(user_id, conn).await {
send_to_push_relay(json!({ send_to_push_relay(json!({
"userId": user_uuid, "userId": user_id,
"organizationId": (), "organizationId": (),
"deviceId": acting_device_uuid, "deviceId": acting_device_id,
"identifier": acting_device_uuid, "identifier": acting_device_id,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": cipher.uuid, "Id": cipher.uuid,
@ -178,14 +178,14 @@ pub async fn push_cipher_update(
} }
} }
pub fn push_logout(user: &User, acting_device_uuid: Option<String>) { pub fn push_logout(user: &User, acting_device_id: Option<String>) {
let acting_device_uuid: Value = acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| Value::Null); let acting_device_id: Value = acting_device_id.map(|v| v.into()).unwrap_or_else(|| Value::Null);
tokio::task::spawn(send_to_push_relay(json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": user.uuid, "userId": user.uuid,
"organizationId": (), "organizationId": (),
"deviceId": acting_device_uuid, "deviceId": acting_device_id,
"identifier": acting_device_uuid, "identifier": acting_device_id,
"type": UpdateType::LogOut as i32, "type": UpdateType::LogOut as i32,
"payload": { "payload": {
"UserId": user.uuid, "UserId": user.uuid,
@ -211,15 +211,15 @@ pub fn push_user_update(ut: UpdateType, user: &User) {
pub async fn push_folder_update( pub async fn push_folder_update(
ut: UpdateType, ut: UpdateType,
folder: &Folder, folder: &Folder,
acting_device_uuid: &DeviceId, acting_device_id: &DeviceId,
conn: &mut crate::db::DbConn, conn: &mut crate::db::DbConn,
) { ) {
if Device::check_user_has_push_device(&folder.user_uuid, conn).await { if Device::check_user_has_push_device(&folder.user_uuid, conn).await {
tokio::task::spawn(send_to_push_relay(json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": folder.user_uuid, "userId": folder.user_uuid,
"organizationId": (), "organizationId": (),
"deviceId": acting_device_uuid, "deviceId": acting_device_id,
"identifier": acting_device_uuid, "identifier": acting_device_id,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": folder.uuid, "Id": folder.uuid,
@ -230,19 +230,14 @@ pub async fn push_folder_update(
} }
} }
pub async fn push_send_update( pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_id: &DeviceId, conn: &mut crate::db::DbConn) {
ut: UpdateType,
send: &Send,
acting_device_uuid: &DeviceId,
conn: &mut crate::db::DbConn,
) {
if let Some(s) = &send.user_uuid { if let Some(s) = &send.user_uuid {
if Device::check_user_has_push_device(s, conn).await { if Device::check_user_has_push_device(s, conn).await {
tokio::task::spawn(send_to_push_relay(json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": send.user_uuid, "userId": send.user_uuid,
"organizationId": (), "organizationId": (),
"deviceId": acting_device_uuid, "deviceId": acting_device_id,
"identifier": acting_device_uuid, "identifier": acting_device_id,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": send.uuid, "Id": send.uuid,
@ -289,38 +284,38 @@ async fn send_to_push_relay(notification_data: Value) {
}; };
} }
pub async fn push_auth_request(user_uuid: UserId, auth_request_uuid: String, conn: &mut crate::db::DbConn) { pub async fn push_auth_request(user_id: UserId, auth_request_id: String, conn: &mut crate::db::DbConn) {
if Device::check_user_has_push_device(&user_uuid, conn).await { if Device::check_user_has_push_device(&user_id, conn).await {
tokio::task::spawn(send_to_push_relay(json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": user_uuid, "userId": user_id,
"organizationId": (), "organizationId": (),
"deviceId": null, "deviceId": null,
"identifier": null, "identifier": null,
"type": UpdateType::AuthRequest as i32, "type": UpdateType::AuthRequest as i32,
"payload": { "payload": {
"Id": auth_request_uuid, "Id": auth_request_id,
"UserId": user_uuid, "UserId": user_id,
} }
}))); })));
} }
} }
pub async fn push_auth_response( pub async fn push_auth_response(
user_uuid: UserId, user_id: UserId,
auth_request_uuid: String, auth_request_id: String,
approving_device_uuid: DeviceId, approving_device_id: DeviceId,
conn: &mut crate::db::DbConn, conn: &mut crate::db::DbConn,
) { ) {
if Device::check_user_has_push_device(&user_uuid, conn).await { if Device::check_user_has_push_device(&user_id, conn).await {
tokio::task::spawn(send_to_push_relay(json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": user_uuid, "userId": user_id,
"organizationId": (), "organizationId": (),
"deviceId": approving_device_uuid, "deviceId": approving_device_id,
"identifier": approving_device_uuid, "identifier": approving_device_id,
"type": UpdateType::AuthRequestResponse as i32, "type": UpdateType::AuthRequestResponse as i32,
"payload": { "payload": {
"Id": auth_request_uuid, "Id": auth_request_id,
"UserId": user_uuid, "UserId": user_id,
} }
}))); })));
} }

Datei anzeigen

@ -196,16 +196,16 @@ async fn web_files(p: PathBuf) -> Cached<Option<NamedFile>> {
Cached::long(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join(p)).await.ok(), true) Cached::long(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join(p)).await.ok(), true)
} }
#[get("/attachments/<uuid>/<file_id>?<token>")] #[get("/attachments/<cipher_id>/<file_id>?<token>")]
async fn attachments(uuid: CipherId, file_id: AttachmentId, token: String) -> Option<NamedFile> { async fn attachments(cipher_id: CipherId, file_id: AttachmentId, token: String) -> Option<NamedFile> {
let Ok(claims) = decode_file_download(&token) else { let Ok(claims) = decode_file_download(&token) else {
return None; return None;
}; };
if claims.sub != uuid || claims.file_id != file_id { if claims.sub != cipher_id || claims.file_id != file_id {
return None; return None;
} }
NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid.as_ref()).join(file_id.as_ref())).await.ok() NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(cipher_id.as_ref()).join(file_id.as_ref())).await.ok()
} }
// We use DbConn here to let the alive healthcheck also verify the database connection. // We use DbConn here to let the alive healthcheck also verify the database connection.

Datei anzeigen

@ -482,19 +482,19 @@ impl<'r> FromRequest<'r> for Headers {
err_handler!("Invalid claim") err_handler!("Invalid claim")
}; };
let device_uuid = claims.device; let device_id = claims.device;
let user_uuid = claims.sub; let user_id = claims.sub;
let mut conn = match DbConn::from_request(request).await { let mut conn = match DbConn::from_request(request).await {
Outcome::Success(conn) => conn, Outcome::Success(conn) => conn,
_ => err_handler!("Error getting DB"), _ => err_handler!("Error getting DB"),
}; };
let Some(device) = Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &mut conn).await else { let Some(device) = Device::find_by_uuid_and_user(&device_id, &user_id, &mut conn).await else {
err_handler!("Invalid device id") err_handler!("Invalid device id")
}; };
let Some(user) = User::find_by_uuid(&user_uuid, &mut conn).await else { let Some(user) = User::find_by_uuid(&user_id, &mut conn).await else {
err_handler!("Device has no user associated") err_handler!("Device has no user associated")
}; };