Merge ClientIp with Headers.

Since we now use the `ClientIp` Guard on a lot more places, it also
increases the size of binary, and the macro generated code because of
this extra Guard. By merging the `ClientIp` Guard with the several
`Header` guards we have it reduces the amount of code generated
(including LLVM IR), but also a small speedup in build time.

I also spotted some small `json!()` optimizations which also reduced the
amount of code generated.
pull/3332/head
BlackDex 2 years ago
parent f21089900e
commit 9e5b94924f
No known key found for this signature in database
GPG Key ID: 58C80A2AA6C765E1

@ -369,7 +369,7 @@ async fn get_user_json(uuid: String, _token: AdminToken, mut conn: DbConn) -> Js
} }
#[post("/users/<uuid>/delete")] #[post("/users/<uuid>/delete")]
async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: ClientIp) -> EmptyResult { async fn delete_user(uuid: String, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(&uuid, &mut conn).await?; let user = get_user_or_404(&uuid, &mut conn).await?;
// Get the user_org records before deleting the actual user // Get the user_org records before deleting the actual user
@ -383,7 +383,7 @@ async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: Cli
user_org.org_uuid, user_org.org_uuid,
String::from(ACTING_ADMIN_USER), String::from(ACTING_ADMIN_USER),
14, // Use UnknownBrowser type 14, // Use UnknownBrowser type
&ip.ip, &token.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -443,12 +443,7 @@ struct UserOrgTypeData {
} }
#[post("/users/org_type", data = "<data>")] #[post("/users/org_type", data = "<data>")]
async fn update_user_org_type( async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
data: Json<UserOrgTypeData>,
_token: AdminToken,
mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult {
let data: UserOrgTypeData = data.into_inner(); let data: UserOrgTypeData = data.into_inner();
let mut user_to_edit = let mut user_to_edit =
@ -489,7 +484,7 @@ async fn update_user_org_type(
data.org_uuid, data.org_uuid,
String::from(ACTING_ADMIN_USER), String::from(ACTING_ADMIN_USER),
14, // Use UnknownBrowser type 14, // Use UnknownBrowser type
&ip.ip, &token.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -724,15 +719,24 @@ async fn backup_db(_token: AdminToken, mut conn: DbConn) -> EmptyResult {
} }
} }
pub struct AdminToken {} pub struct AdminToken {
ip: ClientIp,
}
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for AdminToken { impl<'r> FromRequest<'r> for AdminToken {
type Error = &'static str; type Error = &'static str;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let ip = match ClientIp::from_request(request).await {
Outcome::Success(ip) => ip,
_ => err_handler!("Error getting Client IP"),
};
if CONFIG.disable_admin_token() { if CONFIG.disable_admin_token() {
Outcome::Success(Self {}) Outcome::Success(Self {
ip,
})
} else { } else {
let cookies = request.cookies(); let cookies = request.cookies();
@ -741,19 +745,16 @@ impl<'r> FromRequest<'r> for AdminToken {
None => return Outcome::Failure((Status::Unauthorized, "Unauthorized")), None => return Outcome::Failure((Status::Unauthorized, "Unauthorized")),
}; };
let ip = match ClientIp::from_request(request).await {
Outcome::Success(ip) => ip.ip,
_ => err_handler!("Error getting Client IP"),
};
if decode_admin(access_token).is_err() { if decode_admin(access_token).is_err() {
// Remove admin cookie // Remove admin cookie
cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish()); cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish());
error!("Invalid or expired admin JWT. IP: {}.", ip); error!("Invalid or expired admin JWT. IP: {}.", &ip.ip);
return Outcome::Failure((Status::Unauthorized, "Session expired")); return Outcome::Failure((Status::Unauthorized, "Session expired"));
} }
Outcome::Success(Self {}) Outcome::Success(Self {
ip,
})
} }
} }
} }

@ -6,7 +6,7 @@ use crate::{
api::{ api::{
core::log_user_event, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType, core::log_user_event, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType,
}, },
auth::{decode_delete, decode_invite, decode_verify_email, ClientIp, Headers}, auth::{decode_delete, decode_invite, decode_verify_email, Headers},
crypto, crypto,
db::{models::*, DbConn}, db::{models::*, DbConn},
mail, CONFIG, mail, CONFIG,
@ -305,7 +305,6 @@ async fn post_password(
data: JsonUpcase<ChangePassData>, data: JsonUpcase<ChangePassData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data: ChangePassData = data.into_inner().data; let data: ChangePassData = data.into_inner().data;
@ -318,7 +317,8 @@ async fn post_password(
user.password_hint = clean_password_hint(&data.MasterPasswordHint); user.password_hint = clean_password_hint(&data.MasterPasswordHint);
enforce_password_hint_setting(&user.password_hint)?; enforce_password_hint_setting(&user.password_hint)?;
log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn)
.await;
user.set_password( user.set_password(
&data.NewMasterPasswordHash, &data.NewMasterPasswordHash,
@ -414,13 +414,7 @@ struct KeyData {
} }
#[post("/accounts/key", data = "<data>")] #[post("/accounts/key", data = "<data>")]
async fn post_rotatekey( async fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
data: JsonUpcase<KeyData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>,
) -> EmptyResult {
let data: KeyData = data.into_inner().data; let data: KeyData = data.into_inner().data;
if !headers.user.check_valid_password(&data.MasterPasswordHash) { if !headers.user.check_valid_password(&data.MasterPasswordHash) {
@ -466,7 +460,7 @@ async fn post_rotatekey(
// Prevent triggering cipher updates via WebSockets by settings UpdateType::None // Prevent triggering cipher updates via WebSockets by settings UpdateType::None
// The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues. // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues.
// We force the users to logout after the user has been saved to try and prevent these issues. // We force the users to logout after the user has been saved to try and prevent these issues.
update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None)
.await? .await?
} }

@ -11,7 +11,7 @@ use serde_json::Value;
use crate::{ use crate::{
api::{self, core::log_event, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType}, api::{self, core::log_event, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType},
auth::{ClientIp, Headers}, auth::Headers,
crypto, crypto,
db::{models::*, DbConn, DbPool}, db::{models::*, DbConn, DbPool},
CONFIG, CONFIG,
@ -263,10 +263,9 @@ async fn post_ciphers_admin(
data: JsonUpcase<ShareCipherData>, data: JsonUpcase<ShareCipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_ciphers_create(data, headers, conn, ip, nt).await post_ciphers_create(data, headers, conn, nt).await
} }
/// Called when creating a new org-owned cipher, or cloning a cipher (whether /// Called when creating a new org-owned cipher, or cloning a cipher (whether
@ -277,7 +276,6 @@ async fn post_ciphers_create(
data: JsonUpcase<ShareCipherData>, data: JsonUpcase<ShareCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let mut data: ShareCipherData = data.into_inner().data; let mut data: ShareCipherData = data.into_inner().data;
@ -305,18 +303,12 @@ async fn post_ciphers_create(
// or otherwise), we can just ignore this field entirely. // or otherwise), we can just ignore this field entirely.
data.Cipher.LastKnownRevisionDate = None; data.Cipher.LastKnownRevisionDate = None;
share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &ip, &nt).await share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt).await
} }
/// Called when creating a new user-owned cipher. /// Called when creating a new user-owned cipher.
#[post("/ciphers", data = "<data>")] #[post("/ciphers", data = "<data>")]
async fn post_ciphers( async fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
data: JsonUpcase<CipherData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>,
) -> JsonResult {
let mut data: CipherData = data.into_inner().data; let mut data: CipherData = data.into_inner().data;
// The web/browser clients set this field to null as expected, but the // The web/browser clients set this field to null as expected, but the
@ -326,8 +318,7 @@ async fn post_ciphers(
data.LastKnownRevisionDate = None; data.LastKnownRevisionDate = None;
let mut cipher = Cipher::new(data.Type, data.Name.clone()); let mut cipher = Cipher::new(data.Type, data.Name.clone());
update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate) update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherCreate).await?;
.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))
} }
@ -354,14 +345,12 @@ async fn enforce_personal_ownership_policy(
Ok(()) Ok(())
} }
#[allow(clippy::too_many_arguments)]
pub async fn update_cipher_from_data( pub async fn update_cipher_from_data(
cipher: &mut Cipher, cipher: &mut Cipher,
data: CipherData, data: CipherData,
headers: &Headers, headers: &Headers,
shared_to_collection: bool, shared_to_collection: bool,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
ut: UpdateType, ut: UpdateType,
) -> EmptyResult { ) -> EmptyResult {
@ -517,7 +506,7 @@ pub async fn update_cipher_from_data(
String::from(org_uuid), String::from(org_uuid),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -551,7 +540,6 @@ async fn post_ciphers_import(
data: JsonUpcase<ImportData>, data: JsonUpcase<ImportData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
enforce_personal_ownership_policy(None, &headers, &mut conn).await?; enforce_personal_ownership_policy(None, &headers, &mut conn).await?;
@ -586,8 +574,7 @@ async fn post_ciphers_import(
cipher_data.FolderId = folder_uuid; cipher_data.FolderId = folder_uuid;
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await?;
.await?;
} }
let mut user = headers.user; let mut user = headers.user;
@ -603,10 +590,9 @@ async fn put_cipher_admin(
data: JsonUpcase<CipherData>, data: JsonUpcase<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
put_cipher(uuid, data, headers, conn, ip, nt).await put_cipher(uuid, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/admin", data = "<data>")] #[post("/ciphers/<uuid>/admin", data = "<data>")]
@ -615,10 +601,9 @@ async fn post_cipher_admin(
data: JsonUpcase<CipherData>, data: JsonUpcase<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_cipher(uuid, data, headers, conn, ip, nt).await post_cipher(uuid, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>", data = "<data>")] #[post("/ciphers/<uuid>", data = "<data>")]
@ -627,10 +612,9 @@ async fn post_cipher(
data: JsonUpcase<CipherData>, data: JsonUpcase<CipherData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
put_cipher(uuid, data, headers, conn, ip, nt).await put_cipher(uuid, data, headers, conn, nt).await
} }
#[put("/ciphers/<uuid>", data = "<data>")] #[put("/ciphers/<uuid>", data = "<data>")]
@ -639,7 +623,6 @@ async fn put_cipher(
data: JsonUpcase<CipherData>, data: JsonUpcase<CipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let data: CipherData = data.into_inner().data; let data: CipherData = data.into_inner().data;
@ -658,8 +641,7 @@ async fn put_cipher(
err!("Cipher is not write accessible") err!("Cipher is not write accessible")
} }
update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate) update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherUpdate).await?;
.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))
} }
@ -720,9 +702,8 @@ async fn put_collections_update(
data: JsonUpcase<CollectionsAdminData>, data: JsonUpcase<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
post_collections_admin(uuid, data, headers, conn, ip).await post_collections_admin(uuid, data, headers, conn).await
} }
#[post("/ciphers/<uuid>/collections", data = "<data>")] #[post("/ciphers/<uuid>/collections", data = "<data>")]
@ -731,9 +712,8 @@ async fn post_collections_update(
data: JsonUpcase<CollectionsAdminData>, data: JsonUpcase<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
post_collections_admin(uuid, data, headers, conn, ip).await post_collections_admin(uuid, data, headers, conn).await
} }
#[put("/ciphers/<uuid>/collections-admin", data = "<data>")] #[put("/ciphers/<uuid>/collections-admin", data = "<data>")]
@ -742,9 +722,8 @@ async fn put_collections_admin(
data: JsonUpcase<CollectionsAdminData>, data: JsonUpcase<CollectionsAdminData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
post_collections_admin(uuid, data, headers, conn, ip).await post_collections_admin(uuid, data, headers, conn).await
} }
#[post("/ciphers/<uuid>/collections-admin", data = "<data>")] #[post("/ciphers/<uuid>/collections-admin", data = "<data>")]
@ -753,7 +732,6 @@ async fn post_collections_admin(
data: JsonUpcase<CollectionsAdminData>, data: JsonUpcase<CollectionsAdminData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
let data: CollectionsAdminData = data.into_inner().data; let data: CollectionsAdminData = data.into_inner().data;
@ -795,7 +773,7 @@ async fn post_collections_admin(
cipher.organization_uuid.unwrap(), cipher.organization_uuid.unwrap(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -816,12 +794,11 @@ async fn post_cipher_share(
data: JsonUpcase<ShareCipherData>, data: JsonUpcase<ShareCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner().data; let data: ShareCipherData = data.into_inner().data;
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await
} }
#[put("/ciphers/<uuid>/share", data = "<data>")] #[put("/ciphers/<uuid>/share", data = "<data>")]
@ -830,12 +807,11 @@ async fn put_cipher_share(
data: JsonUpcase<ShareCipherData>, data: JsonUpcase<ShareCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner().data; let data: ShareCipherData = data.into_inner().data;
share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -850,7 +826,6 @@ async fn put_cipher_share_selected(
data: JsonUpcase<ShareSelectedCipherData>, data: JsonUpcase<ShareSelectedCipherData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let mut data: ShareSelectedCipherData = data.into_inner().data; let mut data: ShareSelectedCipherData = data.into_inner().data;
@ -878,7 +853,7 @@ async fn put_cipher_share_selected(
}; };
match shared_cipher_data.Cipher.Id.take() { match shared_cipher_data.Cipher.Id.take() {
Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &ip, &nt).await?, Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?,
None => err!("Request missing ids field"), None => err!("Request missing ids field"),
}; };
} }
@ -891,7 +866,6 @@ async fn share_cipher_by_uuid(
data: ShareCipherData, data: ShareCipherData,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
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(uuid, conn).await {
@ -930,7 +904,7 @@ async fn share_cipher_by_uuid(
UpdateType::SyncCipherCreate UpdateType::SyncCipherCreate
}; };
update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?; update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, nt, ut).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await))
} }
@ -1025,7 +999,6 @@ async fn save_attachment(
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: &Headers, headers: &Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> Result<(Cipher, DbConn), crate::error::Error> { ) -> Result<(Cipher, DbConn), crate::error::Error> {
let cipher = match Cipher::find_by_uuid(&cipher_uuid, &mut conn).await { let cipher = match Cipher::find_by_uuid(&cipher_uuid, &mut conn).await {
@ -1144,7 +1117,7 @@ async fn save_attachment(
String::from(org_uuid), String::from(org_uuid),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1164,7 +1137,6 @@ async fn post_attachment_v2_data(
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
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 {
@ -1173,7 +1145,7 @@ async fn post_attachment_v2_data(
None => err!("Attachment doesn't exist"), None => err!("Attachment doesn't exist"),
}; };
save_attachment(attachment, uuid, data, &headers, conn, ip, nt).await?; save_attachment(attachment, uuid, data, &headers, conn, nt).await?;
Ok(()) Ok(())
} }
@ -1185,14 +1157,13 @@ async fn post_attachment(
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
// Setting this as None signifies to save_attachment() that it should create // Setting this as None signifies to save_attachment() that it should create
// 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, ip, nt).await?; let (cipher, mut conn) = save_attachment(attachment, uuid, 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))
} }
@ -1203,10 +1174,9 @@ async fn post_attachment_admin(
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_attachment(uuid, data, headers, conn, ip, nt).await post_attachment(uuid, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
@ -1216,11 +1186,10 @@ async fn post_attachment_share(
data: Form<UploadData<'_>>, data: Form<UploadData<'_>>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await?; _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await?;
post_attachment(uuid, data, headers, conn, ip, nt).await post_attachment(uuid, data, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
@ -1229,10 +1198,9 @@ async fn delete_attachment_post_admin(
attachment_id: String, attachment_id: String,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await delete_attachment(uuid, attachment_id, headers, conn, nt).await
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
@ -1241,10 +1209,9 @@ async fn delete_attachment_post(
attachment_id: String, attachment_id: String,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await delete_attachment(uuid, attachment_id, headers, conn, nt).await
} }
#[delete("/ciphers/<uuid>/attachment/<attachment_id>")] #[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
@ -1253,10 +1220,9 @@ async fn delete_attachment(
attachment_id: String, attachment_id: String,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await
} }
#[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")] #[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")]
@ -1265,70 +1231,44 @@ async fn delete_attachment_admin(
attachment_id: String, attachment_id: String,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await
} }
#[post("/ciphers/<uuid>/delete")] #[post("/ciphers/<uuid>/delete")]
async fn delete_cipher_post( async fn delete_cipher_post(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
uuid: String, _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
headers: Headers, // permanent delete
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")] #[post("/ciphers/<uuid>/delete-admin")]
async fn delete_cipher_post_admin( async fn delete_cipher_post_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
uuid: String, _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
headers: Headers, // permanent delete
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")] #[put("/ciphers/<uuid>/delete")]
async fn delete_cipher_put( async fn delete_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
uuid: String, _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await
headers: Headers, // soft delete
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")] #[put("/ciphers/<uuid>/delete-admin")]
async fn delete_cipher_put_admin( async fn delete_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
uuid: String, _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await
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>")] #[delete("/ciphers/<uuid>")]
async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, ip: ClientIp, nt: Notify<'_>) -> EmptyResult { async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
_delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
// permanent delete
} }
#[delete("/ciphers/<uuid>/admin")] #[delete("/ciphers/<uuid>/admin")]
async fn delete_cipher_admin( async fn delete_cipher_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
uuid: String, _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await
headers: Headers, // permanent delete
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>")] #[delete("/ciphers", data = "<data>")]
@ -1336,10 +1276,9 @@ async fn delete_cipher_selected(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete
} }
#[post("/ciphers/delete", data = "<data>")] #[post("/ciphers/delete", data = "<data>")]
@ -1347,10 +1286,9 @@ async fn delete_cipher_selected_post(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete
} }
#[put("/ciphers/delete", data = "<data>")] #[put("/ciphers/delete", data = "<data>")]
@ -1358,10 +1296,9 @@ async fn delete_cipher_selected_put(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete
} }
#[delete("/ciphers/admin", data = "<data>")] #[delete("/ciphers/admin", data = "<data>")]
@ -1369,10 +1306,9 @@ async fn delete_cipher_selected_admin(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete
} }
#[post("/ciphers/delete-admin", data = "<data>")] #[post("/ciphers/delete-admin", data = "<data>")]
@ -1380,10 +1316,9 @@ async fn delete_cipher_selected_post_admin(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete
} }
#[put("/ciphers/delete-admin", data = "<data>")] #[put("/ciphers/delete-admin", data = "<data>")]
@ -1391,32 +1326,19 @@ async fn delete_cipher_selected_put_admin(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete
} }
#[put("/ciphers/<uuid>/restore")] #[put("/ciphers/<uuid>/restore")]
async fn restore_cipher_put( async fn restore_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
uuid: String, _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await
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")] #[put("/ciphers/<uuid>/restore-admin")]
async fn restore_cipher_put_admin( async fn restore_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult {
uuid: String, _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await
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>")] #[put("/ciphers/restore", data = "<data>")]
@ -1424,10 +1346,9 @@ async fn restore_cipher_selected(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
_restore_multiple_ciphers(data, &headers, &mut conn, ip, &nt).await _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -1499,7 +1420,6 @@ async fn delete_all(
data: JsonUpcase<PasswordData>, data: JsonUpcase<PasswordData>,
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data: PasswordData = data.into_inner().data; let data: PasswordData = data.into_inner().data;
@ -1527,7 +1447,7 @@ async fn delete_all(
org_data.org_id.clone(), org_data.org_id.clone(),
user.uuid, user.uuid,
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1563,7 +1483,6 @@ async fn _delete_cipher_by_uuid(
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
soft_delete: bool, soft_delete: bool,
ip: &ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await {
@ -1602,8 +1521,16 @@ async fn _delete_cipher_by_uuid(
false => EventType::CipherDeleted 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) log_event(
.await; event_type,
&cipher.uuid,
org_uuid,
headers.user.uuid.clone(),
headers.device.atype,
&headers.ip.ip,
conn,
)
.await;
} }
Ok(()) Ok(())
@ -1614,7 +1541,6 @@ async fn _delete_multiple_ciphers(
headers: Headers, headers: Headers,
mut conn: DbConn, mut conn: DbConn,
soft_delete: bool, soft_delete: bool,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data: Value = data.into_inner().data; let data: Value = data.into_inner().data;
@ -1628,7 +1554,7 @@ async fn _delete_multiple_ciphers(
}; };
for uuid in uuids { for uuid in uuids {
if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &ip, &nt).await { if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &nt).await {
return error; return error;
}; };
} }
@ -1636,13 +1562,7 @@ async fn _delete_multiple_ciphers(
Ok(()) Ok(())
} }
async fn _restore_cipher_by_uuid( async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult {
uuid: &str,
headers: &Headers,
conn: &mut DbConn,
ip: &ClientIp,
nt: &Notify<'_>,
) -> JsonResult {
let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await {
Some(cipher) => cipher, Some(cipher) => cipher,
None => err!("Cipher doesn't exist"), None => err!("Cipher doesn't exist"),
@ -1669,7 +1589,7 @@ async fn _restore_cipher_by_uuid(
String::from(org_uuid), String::from(org_uuid),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -1682,7 +1602,6 @@ async fn _restore_multiple_ciphers(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
ip: ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let data: Value = data.into_inner().data; let data: Value = data.into_inner().data;
@ -1697,7 +1616,7 @@ async fn _restore_multiple_ciphers(
let mut ciphers: Vec<Value> = Vec::new(); let mut ciphers: Vec<Value> = Vec::new();
for uuid in uuids { for uuid in uuids {
match _restore_cipher_by_uuid(uuid, headers, conn, &ip, nt).await { match _restore_cipher_by_uuid(uuid, headers, conn, nt).await {
Ok(json) => ciphers.push(json.into_inner()), Ok(json) => ciphers.push(json.into_inner()),
err => return err, err => return err,
} }
@ -1715,7 +1634,6 @@ async fn _delete_cipher_attachment_by_id(
attachment_id: &str, attachment_id: &str,
headers: &Headers, headers: &Headers,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let attachment = match Attachment::find_by_id(attachment_id, conn).await { let attachment = match Attachment::find_by_id(attachment_id, conn).await {
@ -1752,7 +1670,7 @@ async fn _delete_cipher_attachment_by_id(
org_uuid, org_uuid,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;

@ -6,7 +6,7 @@ use serde_json::Value;
use crate::{ use crate::{
api::{EmptyResult, JsonResult, JsonUpcaseVec}, api::{EmptyResult, JsonResult, JsonUpcaseVec},
auth::{AdminHeaders, ClientIp, Headers}, auth::{AdminHeaders, Headers},
db::{ db::{
models::{Cipher, Event, UserOrganization}, models::{Cipher, Event, UserOrganization},
DbConn, DbPool, DbConn, DbPool,
@ -161,12 +161,7 @@ struct EventCollection {
// https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Events/Controllers/CollectController.cs // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Events/Controllers/CollectController.cs
// https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs
#[post("/collect", format = "application/json", data = "<data>")] #[post("/collect", format = "application/json", data = "<data>")]
async fn post_events_collect( async fn post_events_collect(data: JsonUpcaseVec<EventCollection>, headers: Headers, mut conn: DbConn) -> EmptyResult {
data: JsonUpcaseVec<EventCollection>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult {
if !CONFIG.org_events_enabled() { if !CONFIG.org_events_enabled() {
return Ok(()); return Ok(());
} }
@ -180,7 +175,7 @@ async fn post_events_collect(
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
Some(event_date), Some(event_date),
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -194,7 +189,7 @@ async fn post_events_collect(
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
Some(event_date), Some(event_date),
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -211,7 +206,7 @@ async fn post_events_collect(
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
Some(event_date), Some(event_date),
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;

@ -9,7 +9,7 @@ use crate::{
ApiResult, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData, ApiResult, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData,
UpdateType, UpdateType,
}, },
auth::{decode_invite, AdminHeaders, ClientIp, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
db::{models::*, DbConn}, db::{models::*, DbConn},
error::Error, error::Error,
mail, mail,
@ -209,7 +209,7 @@ async fn post_delete_organization(
} }
#[post("/organizations/<org_id>/leave")] #[post("/organizations/<org_id>/leave")]
async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn, ip: ClientIp) -> EmptyResult { async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult {
match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await {
None => err!("User not part of organization"), None => err!("User not part of organization"),
Some(user_org) => { Some(user_org) => {
@ -225,7 +225,7 @@ async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn,
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -249,9 +249,8 @@ async fn put_organization(
headers: OwnerHeaders, headers: OwnerHeaders,
data: JsonUpcase<OrganizationUpdateData>, data: JsonUpcase<OrganizationUpdateData>,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
post_organization(org_id, headers, data, conn, ip).await post_organization(org_id, headers, data, conn).await
} }
#[post("/organizations/<org_id>", data = "<data>")] #[post("/organizations/<org_id>", data = "<data>")]
@ -260,7 +259,6 @@ async fn post_organization(
headers: OwnerHeaders, headers: OwnerHeaders,
data: JsonUpcase<OrganizationUpdateData>, data: JsonUpcase<OrganizationUpdateData>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
let data: OrganizationUpdateData = data.into_inner().data; let data: OrganizationUpdateData = data.into_inner().data;
@ -280,7 +278,7 @@ async fn post_organization(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -380,7 +378,6 @@ async fn post_organization_collections(
headers: ManagerHeadersLoose, headers: ManagerHeadersLoose,
data: JsonUpcase<NewCollectionData>, data: JsonUpcase<NewCollectionData>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
let data: NewCollectionData = data.into_inner().data; let data: NewCollectionData = data.into_inner().data;
@ -398,7 +395,7 @@ async fn post_organization_collections(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -433,9 +430,8 @@ async fn put_organization_collection_update(
headers: ManagerHeaders, headers: ManagerHeaders,
data: JsonUpcase<NewCollectionData>, data: JsonUpcase<NewCollectionData>,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
post_organization_collection_update(org_id, col_id, headers, data, conn, ip).await post_organization_collection_update(org_id, col_id, headers, data, conn).await
} }
#[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")] #[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
@ -445,7 +441,6 @@ async fn post_organization_collection_update(
headers: ManagerHeaders, headers: ManagerHeaders,
data: JsonUpcase<NewCollectionData>, data: JsonUpcase<NewCollectionData>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
let data: NewCollectionData = data.into_inner().data; let data: NewCollectionData = data.into_inner().data;
@ -472,7 +467,7 @@ async fn post_organization_collection_update(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -548,7 +543,6 @@ async fn delete_organization_collection(
col_id: String, col_id: String,
headers: ManagerHeaders, headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
match Collection::find_by_uuid(&col_id, &mut conn).await { match Collection::find_by_uuid(&col_id, &mut conn).await {
None => err!("Collection not found"), None => err!("Collection not found"),
@ -560,7 +554,7 @@ async fn delete_organization_collection(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -586,9 +580,8 @@ async fn post_organization_collection_delete(
headers: ManagerHeaders, headers: ManagerHeaders,
_data: JsonUpcase<DeleteCollectionData>, _data: JsonUpcase<DeleteCollectionData>,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
delete_organization_collection(org_id, col_id, headers, conn, ip).await delete_organization_collection(org_id, col_id, headers, conn).await
} }
#[get("/organizations/<org_id>/collections/<coll_id>/details")] #[get("/organizations/<org_id>/collections/<coll_id>/details")]
@ -828,7 +821,6 @@ async fn send_invite(
data: JsonUpcase<InviteData>, data: JsonUpcase<InviteData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
let data: InviteData = data.into_inner().data; let data: InviteData = data.into_inner().data;
@ -903,7 +895,7 @@ async fn send_invite(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1101,7 +1093,6 @@ async fn bulk_confirm_invite(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> Json<Value> { ) -> Json<Value> {
let data = data.into_inner().data; let data = data.into_inner().data;
@ -1112,8 +1103,7 @@ async fn bulk_confirm_invite(
for invite in keys { for invite in keys {
let org_user_id = invite["Id"].as_str().unwrap_or_default(); let org_user_id = invite["Id"].as_str().unwrap_or_default();
let user_key = invite["Key"].as_str().unwrap_or_default(); let user_key = invite["Key"].as_str().unwrap_or_default();
let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip, &nt).await let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &nt).await {
{
Ok(_) => String::new(), Ok(_) => String::new(),
Err(e) => format!("{e:?}"), Err(e) => format!("{e:?}"),
}; };
@ -1144,12 +1134,11 @@ async fn confirm_invite(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data = data.into_inner().data; let data = data.into_inner().data;
let user_key = data["Key"].as_str().unwrap_or_default(); let user_key = data["Key"].as_str().unwrap_or_default();
_confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip, &nt).await _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &nt).await
} }
async fn _confirm_invite( async fn _confirm_invite(
@ -1158,7 +1147,6 @@ async fn _confirm_invite(
key: &str, key: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
if key.is_empty() || org_user_id.is_empty() { if key.is_empty() || org_user_id.is_empty() {
@ -1201,7 +1189,7 @@ async fn _confirm_invite(
String::from(org_id), String::from(org_id),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -1264,9 +1252,8 @@ async fn put_organization_user(
data: JsonUpcase<EditUserData>, data: JsonUpcase<EditUserData>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
edit_user(org_id, org_user_id, data, headers, conn, ip).await edit_user(org_id, org_user_id, data, headers, conn).await
} }
#[post("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)] #[post("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
@ -1276,7 +1263,6 @@ async fn edit_user(
data: JsonUpcase<EditUserData>, data: JsonUpcase<EditUserData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
let data: EditUserData = data.into_inner().data; let data: EditUserData = data.into_inner().data;
@ -1365,7 +1351,7 @@ async fn edit_user(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1379,14 +1365,13 @@ async fn bulk_delete_user(
data: JsonUpcase<OrgBulkIds>, data: JsonUpcase<OrgBulkIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> Json<Value> { ) -> Json<Value> {
let data: OrgBulkIds = data.into_inner().data; let data: OrgBulkIds = data.into_inner().data;
let mut bulk_response = Vec::new(); let mut bulk_response = Vec::new();
for org_user_id in data.Ids { for org_user_id in data.Ids {
let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await { let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await {
Ok(_) => String::new(), Ok(_) => String::new(),
Err(e) => format!("{e:?}"), Err(e) => format!("{e:?}"),
}; };
@ -1413,10 +1398,9 @@ async fn delete_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await
} }
#[post("/organizations/<org_id>/users/<org_user_id>/delete")] #[post("/organizations/<org_id>/users/<org_user_id>/delete")]
@ -1425,10 +1409,9 @@ async fn post_delete_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await
} }
async fn _delete_user( async fn _delete_user(
@ -1436,7 +1419,6 @@ async fn _delete_user(
org_user_id: &str, org_user_id: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
nt: &Notify<'_>, nt: &Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
@ -1461,7 +1443,7 @@ async fn _delete_user(
String::from(org_id), String::from(org_id),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -1536,7 +1518,6 @@ async fn post_org_import(
data: JsonUpcase<ImportData>, data: JsonUpcase<ImportData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let data: ImportData = data.into_inner().data; let data: ImportData = data.into_inner().data;
@ -1569,9 +1550,7 @@ async fn post_org_import(
let mut ciphers = Vec::new(); let mut ciphers = Vec::new();
for cipher_data in data.Ciphers { for cipher_data in data.Ciphers {
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await.ok();
.await
.ok();
ciphers.push(cipher); ciphers.push(cipher);
} }
@ -1657,7 +1636,6 @@ async fn put_policy(
data: Json<PolicyData>, data: Json<PolicyData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
let data: PolicyData = data.into_inner(); let data: PolicyData = data.into_inner();
@ -1690,7 +1668,7 @@ async fn put_policy(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1724,7 +1702,7 @@ async fn put_policy(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1749,7 +1727,7 @@ async fn put_policy(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1825,13 +1803,7 @@ struct OrgImportData {
} }
#[post("/organizations/<org_id>/import", data = "<data>")] #[post("/organizations/<org_id>/import", data = "<data>")]
async fn import( async fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
org_id: String,
data: JsonUpcase<OrgImportData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult {
let data = data.into_inner().data; let data = data.into_inner().data;
// TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way // TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way
@ -1857,7 +1829,7 @@ async fn import(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1887,7 +1859,7 @@ async fn import(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1923,7 +1895,7 @@ async fn import(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -1944,9 +1916,8 @@ async fn deactivate_organization_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
_revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await
} }
// Pre web-vault v2022.9.x endpoint // Pre web-vault v2022.9.x endpoint
@ -1956,9 +1927,8 @@ async fn bulk_deactivate_organization_user(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> Json<Value> { ) -> Json<Value> {
bulk_revoke_organization_user(org_id, data, headers, conn, ip).await bulk_revoke_organization_user(org_id, data, headers, conn).await
} }
#[put("/organizations/<org_id>/users/<org_user_id>/revoke")] #[put("/organizations/<org_id>/users/<org_user_id>/revoke")]
@ -1967,9 +1937,8 @@ async fn revoke_organization_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
_revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await
} }
#[put("/organizations/<org_id>/users/revoke", data = "<data>")] #[put("/organizations/<org_id>/users/revoke", data = "<data>")]
@ -1978,7 +1947,6 @@ async fn bulk_revoke_organization_user(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> Json<Value> { ) -> Json<Value> {
let data = data.into_inner().data; let data = data.into_inner().data;
@ -1987,7 +1955,7 @@ async fn bulk_revoke_organization_user(
Some(org_users) => { Some(org_users) => {
for org_user_id in org_users { for org_user_id in org_users {
let org_user_id = org_user_id.as_str().unwrap_or_default(); let org_user_id = org_user_id.as_str().unwrap_or_default();
let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn).await {
Ok(_) => String::new(), Ok(_) => String::new(),
Err(e) => format!("{e:?}"), Err(e) => format!("{e:?}"),
}; };
@ -2016,7 +1984,6 @@ async fn _revoke_organization_user(
org_user_id: &str, org_user_id: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
) -> EmptyResult { ) -> EmptyResult {
match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
Some(mut user_org) if user_org.status > UserOrgStatus::Revoked as i32 => { Some(mut user_org) if user_org.status > UserOrgStatus::Revoked as i32 => {
@ -2041,7 +2008,7 @@ async fn _revoke_organization_user(
org_id.to_string(), org_id.to_string(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -2059,9 +2026,8 @@ async fn activate_organization_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
_restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await
} }
// Pre web-vault v2022.9.x endpoint // Pre web-vault v2022.9.x endpoint
@ -2071,9 +2037,8 @@ async fn bulk_activate_organization_user(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> Json<Value> { ) -> Json<Value> {
bulk_restore_organization_user(org_id, data, headers, conn, ip).await bulk_restore_organization_user(org_id, data, headers, conn).await
} }
#[put("/organizations/<org_id>/users/<org_user_id>/restore")] #[put("/organizations/<org_id>/users/<org_user_id>/restore")]
@ -2082,9 +2047,8 @@ async fn restore_organization_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
_restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await
} }
#[put("/organizations/<org_id>/users/restore", data = "<data>")] #[put("/organizations/<org_id>/users/restore", data = "<data>")]
@ -2093,7 +2057,6 @@ async fn bulk_restore_organization_user(
data: JsonUpcase<Value>, data: JsonUpcase<Value>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> Json<Value> { ) -> Json<Value> {
let data = data.into_inner().data; let data = data.into_inner().data;
@ -2102,7 +2065,7 @@ async fn bulk_restore_organization_user(
Some(org_users) => { Some(org_users) => {
for org_user_id in org_users { for org_user_id in org_users {
let org_user_id = org_user_id.as_str().unwrap_or_default(); let org_user_id = org_user_id.as_str().unwrap_or_default();
let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn).await {
Ok(_) => String::new(), Ok(_) => String::new(),
Err(e) => format!("{e:?}"), Err(e) => format!("{e:?}"),
}; };
@ -2131,7 +2094,6 @@ async fn _restore_organization_user(
org_user_id: &str, org_user_id: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
ip: &ClientIp,
) -> EmptyResult { ) -> EmptyResult {
match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
Some(mut user_org) if user_org.status < UserOrgStatus::Accepted as i32 => { Some(mut user_org) if user_org.status < UserOrgStatus::Accepted as i32 => {
@ -2165,7 +2127,7 @@ async fn _restore_organization_user(
org_id.to_string(), org_id.to_string(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -2277,9 +2239,8 @@ async fn post_group(
data: JsonUpcase<GroupRequest>, data: JsonUpcase<GroupRequest>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
put_group(org_id, group_id, data, headers, conn, ip).await put_group(org_id, group_id, data, headers, conn).await
} }
#[post("/organizations/<org_id>/groups", data = "<data>")] #[post("/organizations/<org_id>/groups", data = "<data>")]
@ -2288,7 +2249,6 @@ async fn post_groups(
headers: AdminHeaders, headers: AdminHeaders,
data: JsonUpcase<GroupRequest>, data: JsonUpcase<GroupRequest>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
@ -2303,12 +2263,12 @@ async fn post_groups(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
add_update_group(group, group_request.Collections, group_request.Users, &org_id, &headers, &ip, &mut conn).await add_update_group(group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await
} }
#[put("/organizations/<org_id>/groups/<group_id>", data = "<data>")] #[put("/organizations/<org_id>/groups/<group_id>", data = "<data>")]
@ -2318,7 +2278,6 @@ async fn put_group(
data: JsonUpcase<GroupRequest>, data: JsonUpcase<GroupRequest>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> JsonResult { ) -> JsonResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
@ -2341,13 +2300,12 @@ async fn put_group(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
add_update_group(updated_group, group_request.Collections, group_request.Users, &org_id, &headers, &ip, &mut conn) add_update_group(updated_group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await
.await
} }
async fn add_update_group( async fn add_update_group(
@ -2356,7 +2314,6 @@ async fn add_update_group(
users: Vec<String>, users: Vec<String>,
org_id: &str, org_id: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
ip: &ClientIp,
conn: &mut DbConn, conn: &mut DbConn,
) -> JsonResult { ) -> JsonResult {
group.save(conn).await?; group.save(conn).await?;
@ -2376,7 +2333,7 @@ async fn add_update_group(
String::from(org_id), String::from(org_id),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
conn, conn,
) )
.await; .await;
@ -2406,24 +2363,12 @@ async fn get_group_details(_org_id: String, group_id: String, _headers: AdminHea
} }
#[post("/organizations/<org_id>/groups/<group_id>/delete")] #[post("/organizations/<org_id>/groups/<group_id>/delete")]
async fn post_delete_group( async fn post_delete_group(org_id: String, group_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
org_id: String, delete_group(org_id, group_id, headers, conn).await
group_id: String,
headers: AdminHeaders,
conn: DbConn,
ip: ClientIp,
) -> EmptyResult {
delete_group(org_id, group_id, headers, conn, ip).await
} }
#[delete("/organizations/<org_id>/groups/<group_id>")] #[delete("/organizations/<org_id>/groups/<group_id>")]
async fn delete_group( async fn delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
org_id: String,
group_id: String,
headers: AdminHeaders,
mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
} }
@ -2439,7 +2384,7 @@ async fn delete_group(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -2488,7 +2433,6 @@ async fn put_group_users(
headers: AdminHeaders, headers: AdminHeaders,
data: JsonVec<String>, data: JsonVec<String>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
@ -2512,7 +2456,7 @@ async fn put_group_users(
org_id.clone(), org_id.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -2551,9 +2495,8 @@ async fn post_user_groups(
data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, data: JsonUpcase<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
put_user_groups(org_id, org_user_id, data, headers, conn, ip).await put_user_groups(org_id, org_user_id, data, headers, conn).await
} }
#[put("/organizations/<org_id>/users/<org_user_id>/groups", data = "<data>")] #[put("/organizations/<org_id>/users/<org_user_id>/groups", data = "<data>")]
@ -2563,7 +2506,6 @@ async fn put_user_groups(
data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, data: JsonUpcase<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
@ -2588,7 +2530,7 @@ async fn put_user_groups(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -2603,9 +2545,8 @@ async fn post_delete_group_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
delete_group_user(org_id, group_id, org_user_id, headers, conn, ip).await delete_group_user(org_id, group_id, org_user_id, headers, conn).await
} }
#[delete("/organizations/<org_id>/groups/<group_id>/users/<org_user_id>")] #[delete("/organizations/<org_id>/groups/<group_id>/users/<org_user_id>")]
@ -2615,7 +2556,6 @@ async fn delete_group_user(
org_user_id: String, org_user_id: String,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
@ -2637,7 +2577,7 @@ async fn delete_group_user(
org_id, org_id,
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -2679,7 +2619,6 @@ async fn put_reset_password(
headers: AdminHeaders, headers: AdminHeaders,
data: JsonUpcase<OrganizationUserResetPasswordRequest>, data: JsonUpcase<OrganizationUserResetPasswordRequest>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
let org = match Organization::find_by_uuid(&org_id, &mut conn).await { let org = match Organization::find_by_uuid(&org_id, &mut conn).await {
@ -2725,7 +2664,7 @@ async fn put_reset_password(
org.uuid.clone(), org.uuid.clone(),
headers.user.uuid.clone(), headers.user.uuid.clone(),
headers.device.atype, headers.device.atype,
&ip.ip, &headers.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
@ -2812,7 +2751,6 @@ async fn put_reset_password_enrollment(
headers: Headers, headers: Headers,
data: JsonUpcase<OrganizationUserResetPasswordEnrollmentRequest>, data: JsonUpcase<OrganizationUserResetPasswordEnrollmentRequest>,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp,
) -> EmptyResult { ) -> EmptyResult {
let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await {
Some(u) => u, Some(u) => u,
@ -2838,7 +2776,8 @@ async fn put_reset_password_enrollment(
EventType::OrganizationUserResetPasswordWithdraw as i32 EventType::OrganizationUserResetPasswordWithdraw as i32
}; };
log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &ip.ip, &mut conn).await; log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &mut conn)
.await;
Ok(()) Ok(())
} }

@ -57,7 +57,6 @@ struct EnableAuthenticatorData {
async fn activate_authenticator( async fn activate_authenticator(
data: JsonUpcase<EnableAuthenticatorData>, data: JsonUpcase<EnableAuthenticatorData>,
headers: Headers, headers: Headers,
ip: ClientIp,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
let data: EnableAuthenticatorData = data.into_inner().data; let data: EnableAuthenticatorData = data.into_inner().data;
@ -82,11 +81,11 @@ async fn activate_authenticator(
} }
// Validate the token provided with the key, and save new twofactor // Validate the token provided with the key, and save new twofactor
validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &mut conn).await?; validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &headers.ip, &mut conn).await?;
_generate_recover_code(&mut user, &mut conn).await; _generate_recover_code(&mut user, &mut conn).await;
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
Ok(Json(json!({ Ok(Json(json!({
"Enabled": true, "Enabled": true,
@ -99,10 +98,9 @@ async fn activate_authenticator(
async fn activate_authenticator_put( async fn activate_authenticator_put(
data: JsonUpcase<EnableAuthenticatorData>, data: JsonUpcase<EnableAuthenticatorData>,
headers: Headers, headers: Headers,
ip: ClientIp,
conn: DbConn, conn: DbConn,
) -> JsonResult { ) -> JsonResult {
activate_authenticator(data, headers, ip, conn).await activate_authenticator(data, headers, conn).await
} }
pub async fn validate_totp_code_str( pub async fn validate_totp_code_str(

@ -8,7 +8,7 @@ use crate::{
core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase, core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase,
PasswordData, PasswordData,
}, },
auth::{ClientIp, Headers}, auth::Headers,
crypto, crypto,
db::{ db::{
models::{EventType, TwoFactor, TwoFactorType, User}, models::{EventType, TwoFactor, TwoFactorType, User},
@ -155,7 +155,7 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool {
} }
#[post("/two-factor/duo", data = "<data>")] #[post("/two-factor/duo", data = "<data>")]
async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut conn: DbConn) -> JsonResult {
let data: EnableDuoData = data.into_inner().data; let data: EnableDuoData = data.into_inner().data;
let mut user = headers.user; let mut user = headers.user;
@ -178,7 +178,7 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut con
_generate_recover_code(&mut user, &mut conn).await; _generate_recover_code(&mut user, &mut conn).await;
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
Ok(Json(json!({ Ok(Json(json!({
"Enabled": true, "Enabled": true,
@ -190,8 +190,8 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut con
} }
#[put("/two-factor/duo", data = "<data>")] #[put("/two-factor/duo", data = "<data>")]
async fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn, ip: ClientIp) -> JsonResult { async fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult {
activate_duo(data, headers, conn, ip).await activate_duo(data, headers, conn).await
} }
async fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult { async fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult {

@ -7,7 +7,7 @@ use crate::{
core::{log_user_event, two_factor::_generate_recover_code}, core::{log_user_event, two_factor::_generate_recover_code},
EmptyResult, JsonResult, JsonUpcase, PasswordData, EmptyResult, JsonResult, JsonUpcase, PasswordData,
}, },
auth::{ClientIp, Headers}, auth::Headers,
crypto, crypto,
db::{ db::{
models::{EventType, TwoFactor, TwoFactorType}, models::{EventType, TwoFactor, TwoFactorType},
@ -90,7 +90,7 @@ async fn get_email(data: JsonUpcase<PasswordData>, headers: Headers, mut conn: D
let twofactor_data = EmailTokenData::from_json(&x.data)?; let twofactor_data = EmailTokenData::from_json(&x.data)?;
(true, json!(twofactor_data.email)) (true, json!(twofactor_data.email))
} }
_ => (false, json!(null)), _ => (false, serde_json::value::Value::Null),
}; };
Ok(Json(json!({ Ok(Json(json!({
@ -150,7 +150,7 @@ struct EmailData {
/// Verify email belongs to user and can be used for 2FA email codes. /// Verify email belongs to user and can be used for 2FA email codes.
#[put("/two-factor/email", data = "<data>")] #[put("/two-factor/email", data = "<data>")]
async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn) -> JsonResult {
let data: EmailData = data.into_inner().data; let data: EmailData = data.into_inner().data;
let mut user = headers.user; let mut user = headers.user;
@ -180,7 +180,7 @@ async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn,
_generate_recover_code(&mut user, &mut conn).await; _generate_recover_code(&mut user, &mut conn).await;
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
Ok(Json(json!({ Ok(Json(json!({
"Email": email_data.email, "Email": email_data.email,

@ -6,7 +6,7 @@ use serde_json::Value;
use crate::{ use crate::{
api::{core::log_user_event, JsonResult, JsonUpcase, NumberOrString, PasswordData}, api::{core::log_user_event, JsonResult, JsonUpcase, NumberOrString, PasswordData},
auth::{ClientHeaders, ClientIp, Headers}, auth::{ClientHeaders, Headers},
crypto, crypto,
db::{models::*, DbConn, DbPool}, db::{models::*, DbConn, DbPool},
mail, CONFIG, mail, CONFIG,
@ -73,12 +73,7 @@ struct RecoverTwoFactor {
} }
#[post("/two-factor/recover", data = "<data>")] #[post("/two-factor/recover", data = "<data>")]
async fn recover( async fn recover(data: JsonUpcase<RecoverTwoFactor>, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult {
data: JsonUpcase<RecoverTwoFactor>,
client_headers: ClientHeaders,
mut conn: DbConn,
ip: ClientIp,
) -> JsonResult {
let data: RecoverTwoFactor = data.into_inner().data; let data: RecoverTwoFactor = data.into_inner().data;
use crate::db::models::User; use crate::db::models::User;
@ -102,12 +97,19 @@ async fn recover(
// Remove all twofactors from the user // Remove all twofactors from the user
TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
log_user_event(EventType::UserRecovered2fa as i32, &user.uuid, client_headers.device_type, &ip.ip, &mut conn).await; log_user_event(
EventType::UserRecovered2fa as i32,
&user.uuid,
client_headers.device_type,
&client_headers.ip.ip,
&mut conn,
)
.await;
// Remove the recovery code, not needed without twofactors // Remove the recovery code, not needed without twofactors
user.totp_recover = None; user.totp_recover = None;
user.save(&mut conn).await?; user.save(&mut conn).await?;
Ok(Json(json!({}))) Ok(Json(Value::Object(serde_json::Map::new())))
} }
async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) { async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) {
@ -126,12 +128,7 @@ struct DisableTwoFactorData {
} }
#[post("/two-factor/disable", data = "<data>")] #[post("/two-factor/disable", data = "<data>")]
async fn disable_twofactor( async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
data: JsonUpcase<DisableTwoFactorData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
) -> JsonResult {
let data: DisableTwoFactorData = data.into_inner().data; let data: DisableTwoFactorData = data.into_inner().data;
let password_hash = data.MasterPasswordHash; let password_hash = data.MasterPasswordHash;
let user = headers.user; let user = headers.user;
@ -144,7 +141,8 @@ async fn disable_twofactor(
if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await {
twofactor.delete(&mut conn).await?; twofactor.delete(&mut conn).await?;
log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn)
.await;
} }
let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty(); let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty();
@ -173,13 +171,8 @@ async fn disable_twofactor(
} }
#[put("/two-factor/disable", data = "<data>")] #[put("/two-factor/disable", data = "<data>")]
async fn disable_twofactor_put( async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
data: JsonUpcase<DisableTwoFactorData>, disable_twofactor(data, headers, conn).await
headers: Headers,
conn: DbConn,
ip: ClientIp,
) -> JsonResult {
disable_twofactor(data, headers, conn, ip).await
} }
pub async fn send_incomplete_2fa_notifications(pool: DbPool) { pub async fn send_incomplete_2fa_notifications(pool: DbPool) {

@ -9,7 +9,7 @@ use crate::{
core::{log_user_event, two_factor::_generate_recover_code}, core::{log_user_event, two_factor::_generate_recover_code},
EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData,
}, },
auth::{ClientIp, Headers}, auth::Headers,
db::{ db::{
models::{EventType, TwoFactor, TwoFactorType}, models::{EventType, TwoFactor, TwoFactorType},
DbConn, DbConn,
@ -242,12 +242,7 @@ impl From<PublicKeyCredentialCopy> for PublicKeyCredential {
} }
#[post("/two-factor/webauthn", data = "<data>")] #[post("/two-factor/webauthn", data = "<data>")]
async fn activate_webauthn( async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, mut conn: DbConn) -> JsonResult {
data: JsonUpcase<EnableWebauthnData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
) -> JsonResult {
let data: EnableWebauthnData = data.into_inner().data; let data: EnableWebauthnData = data.into_inner().data;
let mut user = headers.user; let mut user = headers.user;
@ -286,7 +281,7 @@ async fn activate_webauthn(
.await?; .await?;
_generate_recover_code(&mut user, &mut conn).await; _generate_recover_code(&mut user, &mut conn).await;
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
let keys_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect(); let keys_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect();
Ok(Json(json!({ Ok(Json(json!({
@ -297,13 +292,8 @@ async fn activate_webauthn(
} }
#[put("/two-factor/webauthn", data = "<data>")] #[put("/two-factor/webauthn", data = "<data>")]
async fn activate_webauthn_put( async fn activate_webauthn_put(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
data: JsonUpcase<EnableWebauthnData>, activate_webauthn(data, headers, conn).await
headers: Headers,
conn: DbConn,
ip: ClientIp,
) -> JsonResult {
activate_webauthn(data, headers, conn, ip).await
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

@ -8,7 +8,7 @@ use crate::{
core::{log_user_event, two_factor::_generate_recover_code}, core::{log_user_event, two_factor::_generate_recover_code},
EmptyResult, JsonResult, JsonUpcase, PasswordData, EmptyResult, JsonResult, JsonUpcase, PasswordData,
}, },
auth::{ClientIp, Headers}, auth::Headers,
db::{ db::{
models::{EventType, TwoFactor, TwoFactorType}, models::{EventType, TwoFactor, TwoFactorType},
DbConn, DbConn,
@ -47,7 +47,7 @@ fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
} }
fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value { fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
let mut result = json!({}); let mut result = Value::Object(serde_json::Map::new());
for (i, key) in yubikeys.into_iter().enumerate() { for (i, key) in yubikeys.into_iter().enumerate() {
result[format!("Key{}", i + 1)] = Value::String(key); result[format!("Key{}", i + 1)] = Value::String(key);
@ -118,12 +118,7 @@ async fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, mut
} }
#[post("/two-factor/yubikey", data = "<data>")] #[post("/two-factor/yubikey", data = "<data>")]
async fn activate_yubikey( async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, mut conn: DbConn) -> JsonResult {
data: JsonUpcase<EnableYubikeyData>,
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
) -> JsonResult {
let data: EnableYubikeyData = data.into_inner().data; let data: EnableYubikeyData = data.into_inner().data;
let mut user = headers.user; let mut user = headers.user;
@ -169,7 +164,7 @@ async fn activate_yubikey(
_generate_recover_code(&mut user, &mut conn).await; _generate_recover_code(&mut user, &mut conn).await;
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
let mut result = jsonify_yubikeys(yubikey_metadata.Keys); let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
@ -181,13 +176,8 @@ async fn activate_yubikey(
} }
#[put("/two-factor/yubikey", data = "<data>")] #[put("/two-factor/yubikey", data = "<data>")]
async fn activate_yubikey_put( async fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
data: JsonUpcase<EnableYubikeyData>, activate_yubikey(data, headers, conn).await
headers: Headers,
conn: DbConn,
ip: ClientIp,
) -> JsonResult {
activate_yubikey(data, headers, conn, ip).await
} }
pub async fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult { pub async fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult {

@ -25,7 +25,7 @@ pub fn routes() -> Vec<Route> {
} }
#[post("/connect/token", data = "<data>")] #[post("/connect/token", data = "<data>")]
async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn, ip: ClientIp) -> 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<String> = None; let mut user_uuid: Option<String> = None;
@ -45,7 +45,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, &ip).await _password_login(data, &mut user_uuid, &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")?;
@ -56,7 +56,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")?;
_api_key_login(data, &mut user_uuid, &mut conn, &ip).await _api_key_login(data, &mut user_uuid, &mut conn, &client_header.ip).await
} }
t => err!("Invalid type", t), t => err!("Invalid type", t),
}; };
@ -68,14 +68,21 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn:
EventType::UserLoggedIn as i32, EventType::UserLoggedIn as i32,
&user_uuid, &user_uuid,
client_header.device_type, client_header.device_type,
&ip.ip, &client_header.ip.ip,
&mut conn, &mut conn,
) )
.await; .await;
} }
Err(e) => { Err(e) => {
if let Some(ev) = e.get_event() { if let Some(ev) = e.get_event() {
log_user_event(ev.event as i32, &user_uuid, client_header.device_type, &ip.ip, &mut conn).await log_user_event(
ev.event as i32,
&user_uuid,
client_header.device_type,
&client_header.ip.ip,
&mut conn,
)
.await
} }
} }
} }

@ -10,8 +10,7 @@ use std::{
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use rmpv::Value; use rmpv::Value;
use rocket::{serde::json::Json, Route}; use rocket::Route;
use serde_json::Value as JsonValue;
use tokio::{ use tokio::{
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
sync::mpsc::Sender, sync::mpsc::Sender,
@ -23,13 +22,12 @@ use tokio_tungstenite::{
use crate::{ use crate::{
api::EmptyResult, api::EmptyResult,
auth::Headers,
db::models::{Cipher, Folder, Send, User}, db::models::{Cipher, Folder, Send, User},
Error, CONFIG, Error, CONFIG,
}; };
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![negotiate, websockets_err] routes![websockets_err]
} }
#[get("/hub")] #[get("/hub")]
@ -51,29 +49,6 @@ fn websockets_err() -> EmptyResult {
} }
} }
#[post("/hub/negotiate")]
fn negotiate(_headers: Headers) -> Json<JsonValue> {
use crate::crypto;
use data_encoding::BASE64URL;
let conn_id = crypto::encode_random_bytes::<16>(BASE64URL);
let mut available_transports: Vec<JsonValue> = Vec::new();
if CONFIG.websocket_enabled() {
available_transports.push(json!({"transport":"WebSockets", "transferFormats":["Text","Binary"]}));
}
// TODO: Implement transports
// Rocket WS support: https://github.com/SergioBenitez/Rocket/issues/90
// Rocket SSE support: https://github.com/SergioBenitez/Rocket/issues/33
// {"transport":"ServerSentEvents", "transferFormats":["Text"]},
// {"transport":"LongPolling", "transferFormats":["Text","Binary"]}
Json(json!({
"connectionId": conn_id,
"availableTransports": available_transports
}))
}
// //
// Websockets server // Websockets server
// //

@ -318,6 +318,7 @@ impl<'r> FromRequest<'r> for Host {
pub struct ClientHeaders { pub struct ClientHeaders {
pub host: String, pub host: String,
pub device_type: i32, pub device_type: i32,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -326,6 +327,10 @@ impl<'r> FromRequest<'r> for ClientHeaders {
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let host = try_outcome!(Host::from_request(request).await).host; let host = try_outcome!(Host::from_request(request).await).host;
let ip = match ClientIp::from_request(request).await {
Outcome::Success(ip) => ip,
_ => err_handler!("Error getting Client IP"),
};
// When unknown or unable to parse, return 14, which is 'Unknown Browser' // When unknown or unable to parse, return 14, which is 'Unknown Browser'
let device_type: i32 = let device_type: i32 =
request.headers().get_one("device-type").map(|d| d.parse().unwrap_or(14)).unwrap_or_else(|| 14); request.headers().get_one("device-type").map(|d| d.parse().unwrap_or(14)).unwrap_or_else(|| 14);
@ -333,6 +338,7 @@ impl<'r> FromRequest<'r> for ClientHeaders {
Outcome::Success(ClientHeaders { Outcome::Success(ClientHeaders {
host, host,
device_type, device_type,
ip,
}) })
} }
} }
@ -341,6 +347,7 @@ pub struct Headers {
pub host: String, pub host: String,
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -351,6 +358,10 @@ impl<'r> FromRequest<'r> for Headers {
let headers = request.headers(); let headers = request.headers();
let host = try_outcome!(Host::from_request(request).await).host; let host = try_outcome!(Host::from_request(request).await).host;
let ip = match ClientIp::from_request(request).await {
Outcome::Success(ip) => ip,
_ => err_handler!("Error getting Client IP"),
};
// Get access_token // Get access_token
let access_token: &str = match headers.get_one("Authorization") { let access_token: &str = match headers.get_one("Authorization") {
@ -420,6 +431,7 @@ impl<'r> FromRequest<'r> for Headers {
host, host,
device, device,
user, user,
ip,
}) })
} }
} }
@ -431,6 +443,7 @@ pub struct OrgHeaders {
pub org_user_type: UserOrgType, pub org_user_type: UserOrgType,
pub org_user: UserOrganization, pub org_user: UserOrganization,
pub org_id: String, pub org_id: String,
pub ip: ClientIp,
} }
// org_id is usually the second path param ("/organizations/<org_id>"), // org_id is usually the second path param ("/organizations/<org_id>"),
@ -491,6 +504,7 @@ impl<'r> FromRequest<'r> for OrgHeaders {
}, },
org_user, org_user,
org_id, org_id,
ip: headers.ip,
}) })
} }
_ => err_handler!("Error getting the organization id"), _ => err_handler!("Error getting the organization id"),
@ -504,6 +518,7 @@ pub struct AdminHeaders {
pub user: User, pub user: User,
pub org_user_type: UserOrgType, pub org_user_type: UserOrgType,
pub client_version: Option<String>, pub client_version: Option<String>,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -520,6 +535,7 @@ impl<'r> FromRequest<'r> for AdminHeaders {
user: headers.user, user: headers.user,
org_user_type: headers.org_user_type, org_user_type: headers.org_user_type,
client_version, client_version,
ip: headers.ip,
}) })
} else { } else {
err_handler!("You need to be Admin or Owner to call this endpoint") err_handler!("You need to be Admin or Owner to call this endpoint")
@ -533,6 +549,7 @@ impl From<AdminHeaders> for Headers {
host: h.host, host: h.host,
device: h.device, device: h.device,
user: h.user, user: h.user,
ip: h.ip,
} }
} }
} }
@ -564,6 +581,7 @@ pub struct ManagerHeaders {
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub org_user_type: UserOrgType, pub org_user_type: UserOrgType,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -599,6 +617,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
org_user_type: headers.org_user_type, org_user_type: headers.org_user_type,
ip: headers.ip,
}) })
} else { } else {
err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
@ -612,6 +631,7 @@ impl From<ManagerHeaders> for Headers {
host: h.host, host: h.host,
device: h.device, device: h.device,
user: h.user, user: h.user,
ip: h.ip,
} }
} }
} }
@ -623,6 +643,7 @@ pub struct ManagerHeadersLoose {
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub org_user_type: UserOrgType, pub org_user_type: UserOrgType,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -637,6 +658,7 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose {
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
org_user_type: headers.org_user_type, org_user_type: headers.org_user_type,
ip: headers.ip,
}) })
} else { } else {
err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
@ -650,6 +672,7 @@ impl From<ManagerHeadersLoose> for Headers {
host: h.host, host: h.host,
device: h.device, device: h.device,
user: h.user, user: h.user,
ip: h.ip,
} }
} }
} }
@ -658,6 +681,7 @@ pub struct OwnerHeaders {
pub host: String, pub host: String,
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub ip: ClientIp,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -671,6 +695,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders {
host: headers.host, host: headers.host,
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
ip: headers.ip,
}) })
} else { } else {
err_handler!("You need to be Owner to call this endpoint") err_handler!("You need to be Owner to call this endpoint")

@ -151,7 +151,8 @@ impl Cipher {
// Get the type_data or a default to an empty json object '{}'. // Get the type_data or a default to an empty json object '{}'.
// If not passing an empty object, mobile clients will crash. // If not passing an empty object, mobile clients will crash.
let mut type_data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({})); let mut type_data_json: Value =
serde_json::from_str(&self.data).unwrap_or_else(|_| Value::Object(serde_json::Map::new()));
// NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream
// Set the first element of the Uris array as Uri, this is needed several (mobile) clients. // Set the first element of the Uris array as Uri, this is needed several (mobile) clients.
@ -170,10 +171,10 @@ impl Cipher {
// NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream
// data_json should always contain the following keys with every atype // data_json should always contain the following keys with every atype
data_json["Fields"] = json!(fields_json); data_json["Fields"] = fields_json.clone();
data_json["Name"] = json!(self.name); data_json["Name"] = json!(self.name);
data_json["Notes"] = json!(self.notes); data_json["Notes"] = json!(self.notes);
data_json["PasswordHistory"] = json!(password_history_json); data_json["PasswordHistory"] = password_history_json.clone();
let collection_ids = if let Some(cipher_sync_data) = cipher_sync_data { let collection_ids = if let Some(cipher_sync_data) = cipher_sync_data {
if let Some(cipher_collections) = cipher_sync_data.cipher_collections.get(&self.uuid) { if let Some(cipher_collections) = cipher_sync_data.cipher_collections.get(&self.uuid) {

@ -250,6 +250,14 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
log::LevelFilter::Off log::LevelFilter::Off
}; };
// Only show rocket underscore `_` logs when the level is Debug or higher
// Else this will bloat the log output with useless messages.
let rocket_underscore_level = if level >= log::LevelFilter::Debug {
log::LevelFilter::Warn
} else {
log::LevelFilter::Off
};
let mut logger = fern::Dispatch::new() let mut logger = fern::Dispatch::new()
.level(level) .level(level)
// Hide unknown certificate errors if using self-signed // Hide unknown certificate errors if using self-signed
@ -257,7 +265,7 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
// Hide failed to close stream messages // Hide failed to close stream messages
.level_for("hyper::server", log::LevelFilter::Warn) .level_for("hyper::server", log::LevelFilter::Warn)
// Silence rocket logs // Silence rocket logs
.level_for("_", log::LevelFilter::Warn) .level_for("_", rocket_underscore_level)
.level_for("rocket::launch", log::LevelFilter::Error) .level_for("rocket::launch", log::LevelFilter::Error)
.level_for("rocket::launch_", log::LevelFilter::Error) .level_for("rocket::launch_", log::LevelFilter::Error)
.level_for("rocket::rocket", log::LevelFilter::Warn) .level_for("rocket::rocket", log::LevelFilter::Warn)
@ -269,7 +277,8 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
// Prevent cookie_store logs // Prevent cookie_store logs
.level_for("cookie_store", log::LevelFilter::Off) .level_for("cookie_store", log::LevelFilter::Off)
// Variable level for trust-dns used by reqwest // Variable level for trust-dns used by reqwest
.level_for("trust_dns_proto", trust_dns_level) .level_for("trust_dns_resolver::name_server::name_server", trust_dns_level)
.level_for("trust_dns_proto::xfer", trust_dns_level)
.level_for("diesel_logger", diesel_logger_level) .level_for("diesel_logger", diesel_logger_level)
.chain(std::io::stdout()); .chain(std::io::stdout());
@ -277,9 +286,9 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
// This can contain sensitive information we do not want in the default debug/trace logging. // This can contain sensitive information we do not want in the default debug/trace logging.
if CONFIG.smtp_debug() { if CONFIG.smtp_debug() {
println!( println!(
"[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!" "[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!\n\
[WARNING] Only enable SMTP_DEBUG during troubleshooting!\n"
); );
println!("[WARNING] Only enable SMTP_DEBUG during troubleshooting!\n");
logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Debug) logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Debug)
} else { } else {
logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Off) logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Off)

@ -231,8 +231,7 @@ impl<'r> FromParam<'r> for SafeString {
// Log all the routes from the main paths list, and the attachments endpoint // Log all the routes from the main paths list, and the attachments endpoint
// Effectively ignores, any static file route, and the alive endpoint // Effectively ignores, any static file route, and the alive endpoint
const LOGGED_ROUTES: [&str; 6] = const LOGGED_ROUTES: [&str; 5] = ["/api", "/admin", "/identity", "/icons", "/attachments"];
["/api", "/admin", "/identity", "/icons", "/notifications/hub/negotiate", "/attachments"];
// Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts // Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts
pub struct BetterLogging(pub bool); pub struct BetterLogging(pub bool);
@ -588,7 +587,7 @@ impl<'de> Visitor<'de> for UpCaseVisitor {
fn upcase_value(value: Value) -> Value { fn upcase_value(value: Value) -> Value {
if let Value::Object(map) = value { if let Value::Object(map) = value {
let mut new_value = json!({}); let mut new_value = Value::Object(serde_json::Map::new());
for (key, val) in map.into_iter() { for (key, val) in map.into_iter() {
let processed_key = _process_key(&key); let processed_key = _process_key(&key);
@ -597,7 +596,7 @@ fn upcase_value(value: Value) -> Value {
new_value new_value
} else if let Value::Array(array) = value { } else if let Value::Array(array) = value {
// Initialize array with null values // Initialize array with null values
let mut new_value = json!(vec![Value::Null; array.len()]); let mut new_value = Value::Array(vec![Value::Null; array.len()]);
for (index, val) in array.into_iter().enumerate() { for (index, val) in array.into_iter().enumerate() {
new_value[index] = upcase_value(val); new_value[index] = upcase_value(val);

Loading…
Cancel
Save