@ -10,8 +10,8 @@ use rocket::{
use serde_json ::Value ;
use crate ::{
api ::{ self , EmptyResult, JsonResult , JsonUpcase , Notify , PasswordData , UpdateType } ,
auth ::Headers ,
api ::{ self , core::log_event , EmptyResult, JsonResult , JsonUpcase , Notify , PasswordData , UpdateType } ,
auth ::{ ClientIp , Headers } ,
crypto ,
db ::{ models ::* , DbConn , DbPool } ,
CONFIG ,
@ -247,9 +247,10 @@ async fn post_ciphers_admin(
data : JsonUpcase < ShareCipherData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
post_ciphers_create ( data , headers , conn , nt) . await
post_ciphers_create ( data , headers , conn , ip, nt) . await
}
/// Called when creating a new org-owned cipher, or cloning a cipher (whether
@ -260,6 +261,7 @@ async fn post_ciphers_create(
data : JsonUpcase < ShareCipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
let mut data : ShareCipherData = data . into_inner ( ) . data ;
@ -287,12 +289,18 @@ async fn post_ciphers_create(
// or otherwise), we can just ignore this field entirely.
data . Cipher . LastKnownRevisionDate = None ;
share_cipher_by_uuid ( & cipher . uuid , data , & headers , & mut conn , & nt) . await
share_cipher_by_uuid ( & cipher . uuid , data , & headers , & mut conn , & ip, & nt) . await
}
/// Called when creating a new user-owned cipher.
#[ post( " /ciphers " , data = " <data> " ) ]
async fn post_ciphers ( data : JsonUpcase < CipherData > , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> JsonResult {
async fn post_ciphers (
data : JsonUpcase < CipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
let mut data : CipherData = data . into_inner ( ) . data ;
// The web/browser clients set this field to null as expected, but the
@ -302,7 +310,7 @@ async fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, mut conn:
data . LastKnownRevisionDate = None ;
let mut cipher = Cipher ::new ( data . Type , data . Name . clone ( ) ) ;
update_cipher_from_data ( & mut cipher , data , & headers , false , & mut conn , & nt, UpdateType ::CipherCreate ) . await ? ;
update_cipher_from_data ( & mut cipher , data , & headers , false , & mut conn , & ip, & nt, UpdateType ::CipherCreate ) . await ? ;
Ok ( Json ( cipher . to_json ( & headers . host , & headers . user . uuid , None , & mut conn ) . await ) )
}
@ -329,12 +337,14 @@ async fn enforce_personal_ownership_policy(
Ok ( ( ) )
}
#[ allow(clippy::too_many_arguments) ]
pub async fn update_cipher_from_data (
cipher : & mut Cipher ,
data : CipherData ,
headers : & Headers ,
shared_to_collection : bool ,
conn : & mut DbConn ,
ip : & ClientIp ,
nt : & Notify < ' _ > ,
ut : UpdateType ,
) -> EmptyResult {
@ -356,6 +366,9 @@ pub async fn update_cipher_from_data(
err ! ( "Organization mismatch. Please resync the client before updating the cipher" )
}
// Check if this cipher is being transferred from a personal to an organization vault
let transfer_cipher = cipher . organization_uuid . is_none ( ) & & data . OrganizationId . is_some ( ) ;
if let Some ( org_id ) = data . OrganizationId {
match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , conn ) . await {
None = > err ! ( "You don't have permission to add item to organization" ) ,
@ -460,6 +473,26 @@ pub async fn update_cipher_from_data(
cipher . set_favorite ( data . Favorite , & headers . user . uuid , conn ) . await ? ;
if ut ! = UpdateType ::None {
// Only log events for organizational ciphers
if let Some ( org_uuid ) = & cipher . organization_uuid {
let event_type = match ( & ut , transfer_cipher ) {
( UpdateType ::CipherCreate , true ) = > EventType ::CipherCreated ,
( UpdateType ::CipherUpdate , true ) = > EventType ::CipherShared ,
( _ , _ ) = > EventType ::CipherUpdated ,
} ;
log_event (
event_type as i32 ,
& cipher . uuid ,
String ::from ( org_uuid ) ,
headers . user . uuid . clone ( ) ,
headers . device . atype ,
& ip . ip ,
conn ,
)
. await ;
}
nt . send_cipher_update ( ut , cipher , & cipher . update_users_revision ( conn ) . await ) . await ;
}
@ -488,6 +521,7 @@ async fn post_ciphers_import(
data : JsonUpcase < ImportData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
enforce_personal_ownership_policy ( None , & headers , & mut conn ) . await ? ;
@ -516,7 +550,8 @@ async fn post_ciphers_import(
cipher_data . FolderId = folder_uuid ;
let mut cipher = Cipher ::new ( cipher_data . Type , cipher_data . Name . clone ( ) ) ;
update_cipher_from_data ( & mut cipher , cipher_data , & headers , false , & mut conn , & nt , UpdateType ::None ) . await ? ;
update_cipher_from_data ( & mut cipher , cipher_data , & headers , false , & mut conn , & ip , & nt , UpdateType ::None )
. await ? ;
}
let mut user = headers . user ;
@ -532,9 +567,10 @@ async fn put_cipher_admin(
data : JsonUpcase < CipherData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
put_cipher ( uuid , data , headers , conn , nt) . await
put_cipher ( uuid , data , headers , conn , ip, nt) . await
}
#[ post( " /ciphers/<uuid>/admin " , data = " <data> " ) ]
@ -543,9 +579,10 @@ async fn post_cipher_admin(
data : JsonUpcase < CipherData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
post_cipher ( uuid , data , headers , conn , nt) . await
post_cipher ( uuid , data , headers , conn , ip, nt) . await
}
#[ post( " /ciphers/<uuid> " , data = " <data> " ) ]
@ -554,9 +591,10 @@ async fn post_cipher(
data : JsonUpcase < CipherData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
put_cipher ( uuid , data , headers , conn , nt) . await
put_cipher ( uuid , data , headers , conn , ip, nt) . await
}
#[ put( " /ciphers/<uuid> " , data = " <data> " ) ]
@ -565,6 +603,7 @@ async fn put_cipher(
data : JsonUpcase < CipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
let data : CipherData = data . into_inner ( ) . data ;
@ -583,7 +622,7 @@ async fn put_cipher(
err ! ( "Cipher is not write accessible" )
}
update_cipher_from_data ( & mut cipher , data , & headers , false , & mut conn , & nt, UpdateType ::CipherUpdate ) . await ? ;
update_cipher_from_data ( & mut cipher , data , & headers , false , & mut conn , & ip, & nt, UpdateType ::CipherUpdate ) . await ? ;
Ok ( Json ( cipher . to_json ( & headers . host , & headers . user . uuid , None , & mut conn ) . await ) )
}
@ -600,8 +639,9 @@ async fn put_collections_update(
data : JsonUpcase < CollectionsAdminData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
) -> EmptyResult {
post_collections_admin ( uuid , data , headers , conn ). await
post_collections_admin ( uuid , data , headers , conn , ip ). await
}
#[ post( " /ciphers/<uuid>/collections " , data = " <data> " ) ]
@ -610,8 +650,9 @@ async fn post_collections_update(
data : JsonUpcase < CollectionsAdminData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
) -> EmptyResult {
post_collections_admin ( uuid , data , headers , conn ). await
post_collections_admin ( uuid , data , headers , conn , ip ). await
}
#[ put( " /ciphers/<uuid>/collections-admin " , data = " <data> " ) ]
@ -620,8 +661,9 @@ async fn put_collections_admin(
data : JsonUpcase < CollectionsAdminData > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
) -> EmptyResult {
post_collections_admin ( uuid , data , headers , conn ). await
post_collections_admin ( uuid , data , headers , conn , ip ). await
}
#[ post( " /ciphers/<uuid>/collections-admin " , data = " <data> " ) ]
@ -630,6 +672,7 @@ async fn post_collections_admin(
data : JsonUpcase < CollectionsAdminData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
) -> EmptyResult {
let data : CollectionsAdminData = data . into_inner ( ) . data ;
@ -665,6 +708,17 @@ async fn post_collections_admin(
}
}
log_event (
EventType ::CipherUpdatedCollections as i32 ,
& cipher . uuid ,
cipher . organization_uuid . unwrap ( ) ,
headers . user . uuid . clone ( ) ,
headers . device . atype ,
& ip . ip ,
& mut conn ,
)
. await ;
Ok ( ( ) )
}
@ -681,11 +735,12 @@ async fn post_cipher_share(
data : JsonUpcase < ShareCipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
let data : ShareCipherData = data . into_inner ( ) . data ;
share_cipher_by_uuid ( & uuid , data , & headers , & mut conn , & nt) . await
share_cipher_by_uuid ( & uuid , data , & headers , & mut conn , & ip, & nt) . await
}
#[ put( " /ciphers/<uuid>/share " , data = " <data> " ) ]
@ -694,11 +749,12 @@ async fn put_cipher_share(
data : JsonUpcase < ShareCipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
let data : ShareCipherData = data . into_inner ( ) . data ;
share_cipher_by_uuid ( & uuid , data , & headers , & mut conn , & nt) . await
share_cipher_by_uuid ( & uuid , data , & headers , & mut conn , & ip, & nt) . await
}
#[ derive(Deserialize) ]
@ -713,6 +769,7 @@ async fn put_cipher_share_selected(
data : JsonUpcase < ShareSelectedCipherData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
let mut data : ShareSelectedCipherData = data . into_inner ( ) . data ;
@ -740,7 +797,7 @@ async fn put_cipher_share_selected(
} ;
match shared_cipher_data . Cipher . Id . take ( ) {
Some ( id ) = > share_cipher_by_uuid ( & id , shared_cipher_data , & headers , & mut conn , & nt) . await ? ,
Some ( id ) = > share_cipher_by_uuid ( & id , shared_cipher_data , & headers , & mut conn , & ip, & nt) . await ? ,
None = > err ! ( "Request missing ids field" ) ,
} ;
}
@ -753,6 +810,7 @@ async fn share_cipher_by_uuid(
data : ShareCipherData ,
headers : & Headers ,
conn : & mut DbConn ,
ip : & ClientIp ,
nt : & Notify < ' _ > ,
) -> JsonResult {
let mut cipher = match Cipher ::find_by_uuid ( uuid , conn ) . await {
@ -768,37 +826,30 @@ async fn share_cipher_by_uuid(
let mut shared_to_collection = false ;
match data . Cipher . OrganizationId . clone ( ) {
// If we don't get an organization ID, we don't do anything
// No error because this is used when using the Clone functionality
None = > { }
Some ( organization_uuid ) = > {
for uuid in & data . CollectionIds {
match Collection ::find_by_uuid_and_org ( uuid , & organization_uuid , conn ) . await {
None = > err ! ( "Invalid collection ID provided" ) ,
Some ( collection ) = > {
if collection . is_writable_by_user ( & headers . user . uuid , conn ) . await {
CollectionCipher ::save ( & cipher . uuid , & collection . uuid , conn ) . await ? ;
shared_to_collection = true ;
} else {
err ! ( "No rights to modify the collection" )
}
if let Some ( organization_uuid ) = & data . Cipher . OrganizationId {
for uuid in & data . CollectionIds {
match Collection ::find_by_uuid_and_org ( uuid , organization_uuid , conn ) . await {
None = > err ! ( "Invalid collection ID provided" ) ,
Some ( collection ) = > {
if collection . is_writable_by_user ( & headers . user . uuid , conn ) . await {
CollectionCipher ::save ( & cipher . uuid , & collection . uuid , conn ) . await ? ;
shared_to_collection = true ;
} else {
err ! ( "No rights to modify the collection" )
}
}
}
}
} ;
update_cipher_from_data (
& mut cipher ,
data . Cipher ,
headers ,
shared_to_collection ,
conn ,
nt ,
UpdateType ::CipherUpdate ,
)
. await ? ;
// When LastKnownRevisionDate is None, it is a new cipher, so send CipherCreate.
let ut = if data . Cipher . LastKnownRevisionDate . is_some ( ) {
UpdateType ::CipherUpdate
} else {
UpdateType ::CipherCreate
} ;
update_cipher_from_data ( & mut cipher , data . Cipher , headers , shared_to_collection , conn , ip , nt , ut ) . await ? ;
Ok ( Json ( cipher . to_json ( & headers . host , & headers . user . uuid , None , conn ) . await ) )
}
@ -893,6 +944,7 @@ async fn save_attachment(
data : Form < UploadData < ' _ > > ,
headers : & Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> Result < ( Cipher , DbConn ) , crate ::error ::Error > {
let cipher = match Cipher ::find_by_uuid ( & cipher_uuid , & mut conn ) . await {
@ -1011,6 +1063,19 @@ async fn save_attachment(
nt . send_cipher_update ( UpdateType ::CipherUpdate , & cipher , & cipher . update_users_revision ( & mut conn ) . await ) . await ;
if let Some ( org_uuid ) = & cipher . organization_uuid {
log_event (
EventType ::CipherAttachmentCreated as i32 ,
& cipher . uuid ,
String ::from ( org_uuid ) ,
headers . user . uuid . clone ( ) ,
headers . device . atype ,
& ip . ip ,
& mut conn ,
)
. await ;
}
Ok ( ( cipher , conn ) )
}
@ -1025,6 +1090,7 @@ async fn post_attachment_v2_data(
data : Form < UploadData < ' _ > > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
let attachment = match Attachment ::find_by_id ( & attachment_id , & mut conn ) . await {
@ -1033,7 +1099,7 @@ async fn post_attachment_v2_data(
None = > err ! ( "Attachment doesn't exist" ) ,
} ;
save_attachment ( attachment , uuid , data , & headers , conn , nt) . await ? ;
save_attachment ( attachment , uuid , data , & headers , conn , ip, nt) . await ? ;
Ok ( ( ) )
}
@ -1045,13 +1111,14 @@ async fn post_attachment(
data : Form < UploadData < ' _ > > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
// Setting this as None signifies to save_attachment() that it should create
// the attachment database record as well as saving the data to disk.
let attachment = None ;
let ( cipher , mut conn ) = save_attachment ( attachment , uuid , data , & headers , conn , nt) . await ? ;
let ( cipher , mut conn ) = save_attachment ( attachment , uuid , data , & headers , conn , ip, nt) . await ? ;
Ok ( Json ( cipher . to_json ( & headers . host , & headers . user . uuid , None , & mut conn ) . await ) )
}
@ -1062,9 +1129,10 @@ async fn post_attachment_admin(
data : Form < UploadData < ' _ > > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
post_attachment ( uuid , data , headers , conn , nt) . await
post_attachment ( uuid , data , headers , conn , ip, nt) . await
}
#[ post( " /ciphers/<uuid>/attachment/<attachment_id>/share " , format = " multipart/form-data " , data = " <data> " ) ]
@ -1074,10 +1142,11 @@ async fn post_attachment_share(
data : Form < UploadData < ' _ > > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & nt) . await ? ;
post_attachment ( uuid , data , headers , conn , nt) . await
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & ip, & nt) . await ? ;
post_attachment ( uuid , data , headers , conn , ip, nt) . await
}
#[ post( " /ciphers/<uuid>/attachment/<attachment_id>/delete-admin " ) ]
@ -1086,9 +1155,10 @@ async fn delete_attachment_post_admin(
attachment_id : String ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
delete_attachment ( uuid , attachment_id , headers , conn , nt) . await
delete_attachment ( uuid , attachment_id , headers , conn , ip, nt) . await
}
#[ post( " /ciphers/<uuid>/attachment/<attachment_id>/delete " ) ]
@ -1097,9 +1167,10 @@ async fn delete_attachment_post(
attachment_id : String ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
delete_attachment ( uuid , attachment_id , headers , conn , nt) . await
delete_attachment ( uuid , attachment_id , headers , conn , ip, nt) . await
}
#[ delete( " /ciphers/<uuid>/attachment/<attachment_id> " ) ]
@ -1108,9 +1179,10 @@ async fn delete_attachment(
attachment_id : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & nt) . await
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & ip, & nt) . await
}
#[ delete( " /ciphers/<uuid>/attachment/<attachment_id>/admin " ) ]
@ -1119,39 +1191,70 @@ async fn delete_attachment_admin(
attachment_id : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & nt) . await
_delete_cipher_attachment_by_id ( & uuid , & attachment_id , & headers , & mut conn , & ip, & nt) . await
}
#[ post( " /ciphers/<uuid>/delete " ) ]
async fn delete_cipher_post ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & nt ) . await
async fn delete_cipher_post (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & ip , & nt ) . await // permanent delete
}
#[ post( " /ciphers/<uuid>/delete-admin " ) ]
async fn delete_cipher_post_admin ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & nt ) . await
async fn delete_cipher_post_admin (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & ip , & nt ) . await // permanent delete
}
#[ put( " /ciphers/<uuid>/delete " ) ]
async fn delete_cipher_put ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , true , & nt ) . await
async fn delete_cipher_put (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , true , & ip , & nt ) . await // soft delete
}
#[ put( " /ciphers/<uuid>/delete-admin " ) ]
async fn delete_cipher_put_admin ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , true , & nt ) . await
async fn delete_cipher_put_admin (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , true , & ip , & nt ) . await
}
#[ delete( " /ciphers/<uuid> " ) ]
async fn delete_cipher ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & nt ) . await
async fn delete_cipher ( uuid : String , headers : Headers , mut conn : DbConn , ip: ClientIp , nt: Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & ip, & nt) . await // permanent delete
}
#[ delete( " /ciphers/<uuid>/admin " ) ]
async fn delete_cipher_admin ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & nt ) . await
async fn delete_cipher_admin (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_cipher_by_uuid ( & uuid , & headers , & mut conn , false , & ip , & nt ) . await // permanent delete
}
#[ delete( " /ciphers " , data = " <data> " ) ]
@ -1159,9 +1262,10 @@ async fn delete_cipher_selected(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_multiple_ciphers ( data , headers , conn , false , nt) . await
_delete_multiple_ciphers ( data , headers , conn , false , ip, nt) . await // permanent delete
}
#[ post( " /ciphers/delete " , data = " <data> " ) ]
@ -1169,9 +1273,10 @@ async fn delete_cipher_selected_post(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_multiple_ciphers ( data , headers , conn , false , nt) . await
_delete_multiple_ciphers ( data , headers , conn , false , ip, nt) . await // permanent delete
}
#[ put( " /ciphers/delete " , data = " <data> " ) ]
@ -1179,9 +1284,10 @@ async fn delete_cipher_selected_put(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
_delete_multiple_ciphers ( data , headers , conn , true , nt) . await // soft delete
_delete_multiple_ciphers ( data , headers , conn , true , ip, nt) . await // soft delete
}
#[ delete( " /ciphers/admin " , data = " <data> " ) ]
@ -1189,9 +1295,10 @@ async fn delete_cipher_selected_admin(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
delete_cipher_ selected ( data , headers , conn , nt ) . await
_ delete_multiple_ ciphers( data , headers , conn , false , ip , nt ) . await // permanent delete
}
#[ post( " /ciphers/delete-admin " , data = " <data> " ) ]
@ -1199,9 +1306,10 @@ async fn delete_cipher_selected_post_admin(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
delete_cipher_selected_post ( data , headers , conn , nt ) . await
_delete_multiple_ciphers ( data , headers , conn , false , ip , nt ) . await // permanent delete
}
#[ put( " /ciphers/delete-admin " , data = " <data> " ) ]
@ -1209,19 +1317,32 @@ async fn delete_cipher_selected_put_admin(
data : JsonUpcase < Value > ,
headers : Headers ,
conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
delete_cipher_ selected_put ( data , headers , conn , nt ) . await
_ delete_multiple_ ciphers( data , headers , conn , true , ip , nt ) . await // soft delete
}
#[ put( " /ciphers/<uuid>/restore " ) ]
async fn restore_cipher_put ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> JsonResult {
_restore_cipher_by_uuid ( & uuid , & headers , & mut conn , & nt ) . await
async fn restore_cipher_put (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
_restore_cipher_by_uuid ( & uuid , & headers , & mut conn , & ip , & nt ) . await
}
#[ put( " /ciphers/<uuid>/restore-admin " ) ]
async fn restore_cipher_put_admin ( uuid : String , headers : Headers , mut conn : DbConn , nt : Notify < ' _ > ) -> JsonResult {
_restore_cipher_by_uuid ( & uuid , & headers , & mut conn , & nt ) . await
async fn restore_cipher_put_admin (
uuid : String ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
_restore_cipher_by_uuid ( & uuid , & headers , & mut conn , & ip , & nt ) . await
}
#[ put( " /ciphers/restore " , data = " <data> " ) ]
@ -1229,9 +1350,10 @@ async fn restore_cipher_selected(
data : JsonUpcase < Value > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> JsonResult {
_restore_multiple_ciphers ( data , & headers , & mut conn , & nt ) . await
_restore_multiple_ciphers ( data , & headers , & mut conn , ip , & nt ) . await
}
#[ derive(Deserialize) ]
@ -1303,6 +1425,7 @@ async fn delete_all(
data : JsonUpcase < PasswordData > ,
headers : Headers ,
mut conn : DbConn ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
let data : PasswordData = data . into_inner ( ) . data ;
@ -1323,6 +1446,18 @@ async fn delete_all(
if user_org . atype = = UserOrgType ::Owner {
Cipher ::delete_all_by_organization ( & org_data . org_id , & mut conn ) . await ? ;
nt . send_user_update ( UpdateType ::Vault , & user ) . await ;
log_event (
EventType ::OrganizationPurgedVault as i32 ,
& org_data . org_id ,
org_data . org_id . clone ( ) ,
user . uuid ,
headers . device . atype ,
& ip . ip ,
& mut conn ,
)
. await ;
Ok ( ( ) )
} else {
err ! ( "You don't have permission to purge the organization vault" ) ;
@ -1354,6 +1489,7 @@ async fn _delete_cipher_by_uuid(
headers : & Headers ,
conn : & mut DbConn ,
soft_delete : bool ,
ip : & ClientIp ,
nt : & Notify < ' _ > ,
) -> EmptyResult {
let mut cipher = match Cipher ::find_by_uuid ( uuid , conn ) . await {
@ -1374,6 +1510,16 @@ async fn _delete_cipher_by_uuid(
nt . send_cipher_update ( UpdateType ::CipherDelete , & cipher , & cipher . update_users_revision ( conn ) . await ) . await ;
}
if let Some ( org_uuid ) = cipher . organization_uuid {
let event_type = match soft_delete {
true = > EventType ::CipherSoftDeleted as i32 ,
false = > EventType ::CipherDeleted as i32 ,
} ;
log_event ( event_type , & cipher . uuid , org_uuid , headers . user . uuid . clone ( ) , headers . device . atype , & ip . ip , conn )
. await ;
}
Ok ( ( ) )
}
@ -1382,6 +1528,7 @@ async fn _delete_multiple_ciphers(
headers : Headers ,
mut conn : DbConn ,
soft_delete : bool ,
ip : ClientIp ,
nt : Notify < ' _ > ,
) -> EmptyResult {
let data : Value = data . into_inner ( ) . data ;
@ -1395,7 +1542,7 @@ async fn _delete_multiple_ciphers(
} ;
for uuid in uuids {
if let error @ Err ( _ ) = _delete_cipher_by_uuid ( uuid , & headers , & mut conn , soft_delete , & nt) . await {
if let error @ Err ( _ ) = _delete_cipher_by_uuid ( uuid , & headers , & mut conn , soft_delete , & ip, & nt) . await {
return error ;
} ;
}
@ -1403,7 +1550,13 @@ async fn _delete_multiple_ciphers(
Ok ( ( ) )
}
async fn _restore_cipher_by_uuid ( uuid : & str , headers : & Headers , conn : & mut DbConn , nt : & Notify < ' _ > ) -> JsonResult {
async fn _restore_cipher_by_uuid (
uuid : & str ,
headers : & Headers ,
conn : & mut DbConn ,
ip : & ClientIp ,
nt : & Notify < ' _ > ,
) -> JsonResult {
let mut cipher = match Cipher ::find_by_uuid ( uuid , conn ) . await {
Some ( cipher ) = > cipher ,
None = > err ! ( "Cipher doesn't exist" ) ,
@ -1417,6 +1570,19 @@ async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbCon
cipher . save ( conn ) . await ? ;
nt . send_cipher_update ( UpdateType ::CipherUpdate , & cipher , & cipher . update_users_revision ( conn ) . await ) . await ;
if let Some ( org_uuid ) = & cipher . organization_uuid {
log_event (
EventType ::CipherRestored as i32 ,
& cipher . uuid . clone ( ) ,
String ::from ( org_uuid ) ,
headers . user . uuid . clone ( ) ,
headers . device . atype ,
& ip . ip ,
conn ,
)
. await ;
}
Ok ( Json ( cipher . to_json ( & headers . host , & headers . user . uuid , None , conn ) . await ) )
}
@ -1424,6 +1590,7 @@ async fn _restore_multiple_ciphers(
data : JsonUpcase < Value > ,
headers : & Headers ,
conn : & mut DbConn ,
ip : ClientIp ,
nt : & Notify < ' _ > ,
) -> JsonResult {
let data : Value = data . into_inner ( ) . data ;
@ -1438,7 +1605,7 @@ async fn _restore_multiple_ciphers(
let mut ciphers : Vec < Value > = Vec ::new ( ) ;
for uuid in uuids {
match _restore_cipher_by_uuid ( uuid , headers , conn , nt ) . await {
match _restore_cipher_by_uuid ( uuid , headers , conn , & ip , nt ) . await {
Ok ( json ) = > ciphers . push ( json . into_inner ( ) ) ,
err = > return err ,
}
@ -1456,6 +1623,7 @@ async fn _delete_cipher_attachment_by_id(
attachment_id : & str ,
headers : & Headers ,
conn : & mut DbConn ,
ip : & ClientIp ,
nt : & Notify < ' _ > ,
) -> EmptyResult {
let attachment = match Attachment ::find_by_id ( attachment_id , conn ) . await {
@ -1479,6 +1647,18 @@ async fn _delete_cipher_attachment_by_id(
// Delete attachment
attachment . delete ( conn ) . await ? ;
nt . send_cipher_update ( UpdateType ::CipherUpdate , & cipher , & cipher . update_users_revision ( conn ) . await ) . await ;
if let Some ( org_uuid ) = cipher . organization_uuid {
log_event (
EventType ::CipherAttachmentDeleted as i32 ,
& cipher . uuid ,
org_uuid ,
headers . user . uuid . clone ( ) ,
headers . device . atype ,
& ip . ip ,
conn ,
)
. await ;
}
Ok ( ( ) )
}