mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 10:18:19 +02:00 
			
		
		
		
	Some refactoring and optimizations (#5291)
- Refactored several code to use more modern syntax - Made some checks a bit more strict - Updated crates Signed-off-by: BlackDex <black.dex@gmail.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							fd51230044
						
					
				
				
					commit
					9cd400db6c
				
			
							
								
								
									
										36
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -489,9 +489,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "cc" | name = "cc" | ||||||
| version = "1.2.3" | version = "1.2.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" | checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "shlex", |  "shlex", | ||||||
| ] | ] | ||||||
| @@ -1524,7 +1524,7 @@ dependencies = [ | |||||||
|  "http 1.2.0", |  "http 1.2.0", | ||||||
|  "hyper 1.5.1", |  "hyper 1.5.1", | ||||||
|  "hyper-util", |  "hyper-util", | ||||||
|  "rustls 0.23.19", |  "rustls 0.23.20", | ||||||
|  "rustls-pki-types", |  "rustls-pki-types", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "tokio-rustls 0.26.1", |  "tokio-rustls 0.26.1", | ||||||
| @@ -2618,9 +2618,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "quanta" | name = "quanta" | ||||||
| version = "0.12.3" | version = "0.12.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" | checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "crossbeam-utils", |  "crossbeam-utils", | ||||||
|  "libc", |  "libc", | ||||||
| @@ -2704,9 +2704,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "redox_syscall" | name = "redox_syscall" | ||||||
| version = "0.5.7" | version = "0.5.8" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" | checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
| ] | ] | ||||||
| @@ -3031,9 +3031,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustls" | name = "rustls" | ||||||
| version = "0.23.19" | version = "0.23.20" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" | checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "once_cell", |  "once_cell", | ||||||
|  "rustls-pki-types", |  "rustls-pki-types", | ||||||
| @@ -3062,9 +3062,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustls-pki-types" | name = "rustls-pki-types" | ||||||
| version = "1.10.0" | version = "1.10.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" | checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustls-webpki" | name = "rustls-webpki" | ||||||
| @@ -3173,15 +3173,15 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "semver" | name = "semver" | ||||||
| version = "1.0.23" | version = "1.0.24" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" | checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde" | name = "serde" | ||||||
| version = "1.0.215" | version = "1.0.216" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "serde_derive", |  "serde_derive", | ||||||
| ] | ] | ||||||
| @@ -3198,9 +3198,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde_derive" | name = "serde_derive" | ||||||
| version = "1.0.215" | version = "1.0.216" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @@ -3652,7 +3652,7 @@ version = "0.26.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" | checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "rustls 0.23.19", |  "rustls 0.23.20", | ||||||
|  "tokio", |  "tokio", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -495,11 +495,11 @@ struct UserOrgTypeData { | |||||||
| async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult { | async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult { | ||||||
|     let data: UserOrgTypeData = data.into_inner(); |     let data: UserOrgTypeData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut user_to_edit = |     let Some(mut user_to_edit) = | ||||||
|         match UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await { |         UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await | ||||||
|             Some(user) => user, |     else { | ||||||
|             None => err!("The specified user isn't member of the organization"), |         err!("The specified user isn't member of the organization") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     let new_type = match UserOrgType::from_str(&data.user_type.into_string()) { |     let new_type = match UserOrgType::from_str(&data.user_type.into_string()) { | ||||||
|         Some(new_type) => new_type as i32, |         Some(new_type) => new_type as i32, | ||||||
| @@ -602,9 +602,8 @@ async fn get_json_api<T: DeserializeOwned>(url: &str) -> Result<T, Error> { | |||||||
| } | } | ||||||
|  |  | ||||||
| async fn has_http_access() -> bool { | async fn has_http_access() -> bool { | ||||||
|     let req = match make_http_request(Method::HEAD, "https://github.com/dani-garcia/vaultwarden") { |     let Ok(req) = make_http_request(Method::HEAD, "https://github.com/dani-garcia/vaultwarden") else { | ||||||
|         Ok(r) => r, |         return false; | ||||||
|         Err(_) => return false, |  | ||||||
|     }; |     }; | ||||||
|     match req.send().await { |     match req.send().await { | ||||||
|         Ok(r) => r.status().is_success(), |         Ok(r) => r.status().is_success(), | ||||||
|   | |||||||
| @@ -574,9 +574,8 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn, | |||||||
|         // Skip `null` folder id entries. |         // Skip `null` folder id entries. | ||||||
|         // See: https://github.com/bitwarden/clients/issues/8453 |         // See: https://github.com/bitwarden/clients/issues/8453 | ||||||
|         if let Some(folder_id) = folder_data.id { |         if let Some(folder_id) = folder_data.id { | ||||||
|             let saved_folder = match existing_folders.iter_mut().find(|f| f.uuid == folder_id) { |             let Some(saved_folder) = existing_folders.iter_mut().find(|f| f.uuid == folder_id) else { | ||||||
|                 Some(folder) => folder, |                 err!("Folder doesn't exist") | ||||||
|                 None => err!("Folder doesn't exist"), |  | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             saved_folder.name = folder_data.name; |             saved_folder.name = folder_data.name; | ||||||
| @@ -586,11 +585,11 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn, | |||||||
|  |  | ||||||
|     // Update emergency access data |     // Update emergency access data | ||||||
|     for emergency_access_data in data.emergency_access_keys { |     for emergency_access_data in data.emergency_access_keys { | ||||||
|         let saved_emergency_access = |         let Some(saved_emergency_access) = | ||||||
|             match existing_emergency_access.iter_mut().find(|ea| ea.uuid == emergency_access_data.id) { |             existing_emergency_access.iter_mut().find(|ea| ea.uuid == emergency_access_data.id) | ||||||
|                 Some(emergency_access) => emergency_access, |         else { | ||||||
|                 None => err!("Emergency access doesn't exist or is not owned by the user"), |             err!("Emergency access doesn't exist or is not owned by the user") | ||||||
|             }; |         }; | ||||||
|  |  | ||||||
|         saved_emergency_access.key_encrypted = Some(emergency_access_data.key_encrypted); |         saved_emergency_access.key_encrypted = Some(emergency_access_data.key_encrypted); | ||||||
|         saved_emergency_access.save(&mut conn).await? |         saved_emergency_access.save(&mut conn).await? | ||||||
| @@ -598,10 +597,10 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn, | |||||||
|  |  | ||||||
|     // Update reset password data |     // Update reset password data | ||||||
|     for reset_password_data in data.reset_password_keys { |     for reset_password_data in data.reset_password_keys { | ||||||
|         let user_org = match existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id) |         let Some(user_org) = | ||||||
|         { |             existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id) | ||||||
|             Some(reset_password) => reset_password, |         else { | ||||||
|             None => err!("Reset password doesn't exist"), |             err!("Reset password doesn't exist") | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         user_org.reset_password_key = Some(reset_password_data.reset_password_key); |         user_org.reset_password_key = Some(reset_password_data.reset_password_key); | ||||||
| @@ -610,9 +609,8 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn, | |||||||
|  |  | ||||||
|     // Update send data |     // Update send data | ||||||
|     for send_data in data.sends { |     for send_data in data.sends { | ||||||
|         let send = match existing_sends.iter_mut().find(|s| &s.uuid == send_data.id.as_ref().unwrap()) { |         let Some(send) = existing_sends.iter_mut().find(|s| &s.uuid == send_data.id.as_ref().unwrap()) else { | ||||||
|             Some(send) => send, |             err!("Send doesn't exist") | ||||||
|             None => err!("Send doesn't exist"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         update_send_from_data(send, send_data, &headers, &mut conn, &nt, UpdateType::None).await?; |         update_send_from_data(send, send_data, &headers, &mut conn, &nt, UpdateType::None).await?; | ||||||
| @@ -623,9 +621,9 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn, | |||||||
|  |  | ||||||
|     for cipher_data in data.ciphers { |     for cipher_data in data.ciphers { | ||||||
|         if cipher_data.organization_id.is_none() { |         if cipher_data.organization_id.is_none() { | ||||||
|             let saved_cipher = match existing_ciphers.iter_mut().find(|c| &c.uuid == cipher_data.id.as_ref().unwrap()) { |             let Some(saved_cipher) = existing_ciphers.iter_mut().find(|c| &c.uuid == cipher_data.id.as_ref().unwrap()) | ||||||
|                 Some(cipher) => cipher, |             else { | ||||||
|                 None => err!("Cipher doesn't exist"), |                 err!("Cipher doesn't exist") | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             // Prevent triggering cipher updates via WebSockets by settings UpdateType::None |             // Prevent triggering cipher updates via WebSockets by settings UpdateType::None | ||||||
| @@ -802,14 +800,12 @@ struct VerifyEmailTokenData { | |||||||
| async fn post_verify_email_token(data: Json<VerifyEmailTokenData>, mut conn: DbConn) -> EmptyResult { | async fn post_verify_email_token(data: Json<VerifyEmailTokenData>, mut conn: DbConn) -> EmptyResult { | ||||||
|     let data: VerifyEmailTokenData = data.into_inner(); |     let data: VerifyEmailTokenData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut user = match User::find_by_uuid(&data.user_id, &mut conn).await { |     let Some(mut user) = User::find_by_uuid(&data.user_id, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("User doesn't exist") | ||||||
|         None => err!("User doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let claims = match decode_verify_email(&data.token) { |     let Ok(claims) = decode_verify_email(&data.token) else { | ||||||
|         Ok(claims) => claims, |         err!("Invalid claim") | ||||||
|         Err(_) => err!("Invalid claim"), |  | ||||||
|     }; |     }; | ||||||
|     if claims.sub != user.uuid { |     if claims.sub != user.uuid { | ||||||
|         err!("Invalid claim"); |         err!("Invalid claim"); | ||||||
| @@ -861,15 +857,14 @@ struct DeleteRecoverTokenData { | |||||||
| async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, mut conn: DbConn) -> EmptyResult { | async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, mut conn: DbConn) -> EmptyResult { | ||||||
|     let data: DeleteRecoverTokenData = data.into_inner(); |     let data: DeleteRecoverTokenData = data.into_inner(); | ||||||
|  |  | ||||||
|     let user = match User::find_by_uuid(&data.user_id, &mut conn).await { |     let Ok(claims) = decode_delete(&data.token) else { | ||||||
|         Some(user) => user, |         err!("Invalid claim") | ||||||
|         None => err!("User doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let claims = match decode_delete(&data.token) { |     let Some(user) = User::find_by_uuid(&data.user_id, &mut conn).await else { | ||||||
|         Ok(claims) => claims, |         err!("User doesn't exist") | ||||||
|         Err(_) => err!("Invalid claim"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if claims.sub != user.uuid { |     if claims.sub != user.uuid { | ||||||
|         err!("Invalid claim"); |         err!("Invalid claim"); | ||||||
|     } |     } | ||||||
| @@ -1041,11 +1036,8 @@ impl<'r> FromRequest<'r> for KnownDevice { | |||||||
|  |  | ||||||
|     async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> { |     async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> { | ||||||
|         let email = if let Some(email_b64) = req.headers().get_one("X-Request-Email") { |         let email = if let Some(email_b64) = req.headers().get_one("X-Request-Email") { | ||||||
|             let email_bytes = match data_encoding::BASE64URL_NOPAD.decode(email_b64.as_bytes()) { |             let Ok(email_bytes) = data_encoding::BASE64URL_NOPAD.decode(email_b64.as_bytes()) else { | ||||||
|                 Ok(bytes) => bytes, |                 return Outcome::Error((Status::BadRequest, "X-Request-Email value failed to decode as base64url")); | ||||||
|                 Err(_) => { |  | ||||||
|                     return Outcome::Error((Status::BadRequest, "X-Request-Email value failed to decode as base64url")); |  | ||||||
|                 } |  | ||||||
|             }; |             }; | ||||||
|             match String::from_utf8(email_bytes) { |             match String::from_utf8(email_bytes) { | ||||||
|                 Ok(email) => email, |                 Ok(email) => email, | ||||||
| @@ -1086,9 +1078,9 @@ async fn put_device_token(uuid: &str, data: Json<PushToken>, headers: Headers, m | |||||||
|     let data = data.into_inner(); |     let data = data.into_inner(); | ||||||
|     let token = data.push_token; |     let token = data.push_token; | ||||||
|  |  | ||||||
|     let mut device = match Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &mut conn).await { |     let Some(mut device) = Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &mut conn).await | ||||||
|         Some(device) => device, |     else { | ||||||
|         None => err!(format!("Error: device {uuid} should be present before a token can be assigned")), |         err!(format!("Error: device {uuid} should be present before a token can be assigned")) | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // if the device already has been registered |     // if the device already has been registered | ||||||
| @@ -1159,9 +1151,8 @@ async fn post_auth_request( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data = data.into_inner(); |     let data = data.into_inner(); | ||||||
|  |  | ||||||
|     let user = match User::find_by_mail(&data.email, &mut conn).await { |     let Some(user) = User::find_by_mail(&data.email, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("AuthRequest doesn't exist", "User not found") | ||||||
|         None => err!("AuthRequest doesn't exist", "User not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Validate device uuid and type |     // Validate device uuid and type | ||||||
| @@ -1199,15 +1190,10 @@ async fn post_auth_request( | |||||||
|  |  | ||||||
| #[get("/auth-requests/<uuid>")] | #[get("/auth-requests/<uuid>")] | ||||||
| async fn get_auth_request(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_auth_request(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { |     let Some(auth_request) = AuthRequest::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(auth_request) => auth_request, |         err!("AuthRequest doesn't exist", "Record not found or user uuid does not match") | ||||||
|         None => err!("AuthRequest doesn't exist", "Record not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if headers.user.uuid != auth_request.user_uuid { |  | ||||||
|         err!("AuthRequest doesn't exist", "User uuid's do not match") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); |     let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); | ||||||
|  |  | ||||||
|     Ok(Json(json!({ |     Ok(Json(json!({ | ||||||
| @@ -1244,15 +1230,10 @@ async fn put_auth_request( | |||||||
|     nt: Notify<'_>, |     nt: Notify<'_>, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data = data.into_inner(); |     let data = data.into_inner(); | ||||||
|     let mut auth_request: AuthRequest = match AuthRequest::find_by_uuid(uuid, &mut conn).await { |     let Some(mut auth_request) = AuthRequest::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(auth_request) => auth_request, |         err!("AuthRequest doesn't exist", "Record not found or user uuid does not match") | ||||||
|         None => err!("AuthRequest doesn't exist", "Record not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if headers.user.uuid != auth_request.user_uuid { |  | ||||||
|         err!("AuthRequest doesn't exist", "User uuid's do not match") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if auth_request.approved.is_some() { |     if auth_request.approved.is_some() { | ||||||
|         err!("An authentication request with the same device already exists") |         err!("An authentication request with the same device already exists") | ||||||
|     } |     } | ||||||
| @@ -1297,9 +1278,8 @@ async fn get_auth_request_response( | |||||||
|     client_headers: ClientHeaders, |     client_headers: ClientHeaders, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { |     let Some(auth_request) = AuthRequest::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(auth_request) => auth_request, |         err!("AuthRequest doesn't exist", "User not found") | ||||||
|         None => err!("AuthRequest doesn't exist", "User not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if auth_request.device_type != client_headers.device_type |     if auth_request.device_type != client_headers.device_type | ||||||
|   | |||||||
| @@ -193,9 +193,8 @@ async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json<Value> { | |||||||
|  |  | ||||||
| #[get("/ciphers/<uuid>")] | #[get("/ciphers/<uuid>")] | ||||||
| async fn get_cipher(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_cipher(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -429,14 +428,9 @@ pub async fn update_cipher_from_data( | |||||||
|         cipher.user_uuid = Some(headers.user.uuid.clone()); |         cipher.user_uuid = Some(headers.user.uuid.clone()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if let Some(ref folder_id) = data.folder_id { |     if let Some(ref folder_uuid) = data.folder_id { | ||||||
|         match Folder::find_by_uuid(folder_id, conn).await { |         if Folder::find_by_uuid_and_user(folder_uuid, &headers.user.uuid, conn).await.is_none() { | ||||||
|             Some(folder) => { |             err!("Invalid folder", "Folder does not exist or belongs to another user"); | ||||||
|                 if folder.user_uuid != headers.user.uuid { |  | ||||||
|                     err!("Folder is not owned by user") |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             None => err!("Folder doesn't exist"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -661,9 +655,8 @@ async fn put_cipher( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: CipherData = data.into_inner(); |     let data: CipherData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(mut cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // TODO: Check if only the folder ID or favorite status is being changed. |     // TODO: Check if only the folder ID or favorite status is being changed. | ||||||
| @@ -695,19 +688,13 @@ async fn put_cipher_partial( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: PartialCipherData = data.into_inner(); |     let data: PartialCipherData = data.into_inner(); | ||||||
|  |  | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if let Some(ref folder_id) = data.folder_id { |     if let Some(ref folder_uuid) = data.folder_id { | ||||||
|         match Folder::find_by_uuid(folder_id, &mut conn).await { |         if Folder::find_by_uuid_and_user(folder_uuid, &headers.user.uuid, &mut conn).await.is_none() { | ||||||
|             Some(folder) => { |             err!("Invalid folder", "Folder does not exist or belongs to another user"); | ||||||
|                 if folder.user_uuid != headers.user.uuid { |  | ||||||
|                     err!("Folder is not owned by user") |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             None => err!("Folder doesn't exist"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -774,9 +761,8 @@ async fn post_collections_update( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: CollectionsAdminData = data.into_inner(); |     let data: CollectionsAdminData = data.into_inner(); | ||||||
|  |  | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -788,7 +774,8 @@ async fn post_collections_update( | |||||||
|         HashSet::<String>::from_iter(cipher.get_collections(headers.user.uuid.clone(), &mut conn).await); |         HashSet::<String>::from_iter(cipher.get_collections(headers.user.uuid.clone(), &mut conn).await); | ||||||
|  |  | ||||||
|     for collection in posted_collections.symmetric_difference(¤t_collections) { |     for collection in posted_collections.symmetric_difference(¤t_collections) { | ||||||
|         match Collection::find_by_uuid(collection, &mut conn).await { |         match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await | ||||||
|  |         { | ||||||
|             None => err!("Invalid collection ID provided"), |             None => err!("Invalid collection ID provided"), | ||||||
|             Some(collection) => { |             Some(collection) => { | ||||||
|                 if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { |                 if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -851,9 +838,8 @@ async fn post_collections_admin( | |||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let data: CollectionsAdminData = data.into_inner(); |     let data: CollectionsAdminData = data.into_inner(); | ||||||
|  |  | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -865,7 +851,8 @@ async fn post_collections_admin( | |||||||
|         HashSet::<String>::from_iter(cipher.get_admin_collections(headers.user.uuid.clone(), &mut conn).await); |         HashSet::<String>::from_iter(cipher.get_admin_collections(headers.user.uuid.clone(), &mut conn).await); | ||||||
|  |  | ||||||
|     for collection in posted_collections.symmetric_difference(¤t_collections) { |     for collection in posted_collections.symmetric_difference(¤t_collections) { | ||||||
|         match Collection::find_by_uuid(collection, &mut conn).await { |         match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await | ||||||
|  |         { | ||||||
|             None => err!("Invalid collection ID provided"), |             None => err!("Invalid collection ID provided"), | ||||||
|             Some(collection) => { |             Some(collection) => { | ||||||
|                 if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { |                 if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -1043,9 +1030,8 @@ async fn share_cipher_by_uuid( | |||||||
| /// redirects to the same location as before the v2 API. | /// redirects to the same location as before the v2 API. | ||||||
| #[get("/ciphers/<uuid>/attachment/<attachment_id>")] | #[get("/ciphers/<uuid>/attachment/<attachment_id>")] | ||||||
| async fn get_attachment(uuid: &str, attachment_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_attachment(uuid: &str, attachment_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -1084,9 +1070,8 @@ async fn post_attachment_v2( | |||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -1150,9 +1135,8 @@ async fn save_attachment( | |||||||
|         err!("Attachment size can't be negative") |         err!("Attachment size can't be negative") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let cipher = match Cipher::find_by_uuid(cipher_uuid, &mut conn).await { |     let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &mut conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { | ||||||
| @@ -1545,21 +1529,15 @@ async fn move_cipher_selected( | |||||||
|     let data = data.into_inner(); |     let data = data.into_inner(); | ||||||
|     let user_uuid = headers.user.uuid; |     let user_uuid = headers.user.uuid; | ||||||
|  |  | ||||||
|     if let Some(ref folder_id) = data.folder_id { |     if let Some(ref folder_uuid) = data.folder_id { | ||||||
|         match Folder::find_by_uuid(folder_id, &mut conn).await { |         if Folder::find_by_uuid_and_user(folder_uuid, &user_uuid, &mut conn).await.is_none() { | ||||||
|             Some(folder) => { |             err!("Invalid folder", "Folder does not exist or belongs to another user"); | ||||||
|                 if folder.user_uuid != user_uuid { |  | ||||||
|                     err!("Folder is not owned by user") |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             None => err!("Folder doesn't exist"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for uuid in data.ids { |     for uuid in data.ids { | ||||||
|         let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { |         let Some(cipher) = Cipher::find_by_uuid(&uuid, &mut conn).await else { | ||||||
|             Some(cipher) => cipher, |             err!("Cipher doesn't exist") | ||||||
|             None => err!("Cipher doesn't exist"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if !cipher.is_accessible_to_user(&user_uuid, &mut conn).await { |         if !cipher.is_accessible_to_user(&user_uuid, &mut conn).await { | ||||||
| @@ -1667,9 +1645,8 @@ async fn _delete_cipher_by_uuid( | |||||||
|     soft_delete: bool, |     soft_delete: bool, | ||||||
|     nt: &Notify<'_>, |     nt: &Notify<'_>, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { |     let Some(mut cipher) = Cipher::find_by_uuid(uuid, conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { | ||||||
| @@ -1739,9 +1716,8 @@ async fn _delete_multiple_ciphers( | |||||||
| } | } | ||||||
|  |  | ||||||
| async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { | async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { | ||||||
|     let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { |     let Some(mut cipher) = Cipher::find_by_uuid(uuid, conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { | ||||||
| @@ -1807,18 +1783,16 @@ async fn _delete_cipher_attachment_by_id( | |||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
|     nt: &Notify<'_>, |     nt: &Notify<'_>, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let attachment = match Attachment::find_by_id(attachment_id, conn).await { |     let Some(attachment) = Attachment::find_by_id(attachment_id, conn).await else { | ||||||
|         Some(attachment) => attachment, |         err!("Attachment doesn't exist") | ||||||
|         None => err!("Attachment doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if attachment.cipher_uuid != uuid { |     if attachment.cipher_uuid != uuid { | ||||||
|         err!("Attachment from other cipher") |         err!("Attachment from other cipher") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let cipher = match Cipher::find_by_uuid(uuid, conn).await { |     let Some(cipher) = Cipher::find_by_uuid(uuid, conn).await else { | ||||||
|         Some(cipher) => cipher, |         err!("Cipher doesn't exist") | ||||||
|         None => err!("Cipher doesn't exist"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { |     if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { | ||||||
|   | |||||||
| @@ -137,11 +137,11 @@ async fn post_emergency_access( | |||||||
|  |  | ||||||
|     let data: EmergencyAccessUpdateData = data.into_inner(); |     let data: EmergencyAccessUpdateData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await | ||||||
|             Some(emergency_access) => emergency_access, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     let new_type = match EmergencyAccessType::from_str(&data.r#type.into_string()) { |     let new_type = match EmergencyAccessType::from_str(&data.r#type.into_string()) { | ||||||
|         Some(new_type) => new_type as i32, |         Some(new_type) => new_type as i32, | ||||||
| @@ -284,24 +284,22 @@ async fn send_invite(data: Json<EmergencyAccessInviteData>, headers: Headers, mu | |||||||
| async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { | async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { | ||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if emergency_access.status != EmergencyAccessStatus::Invited as i32 { |     if emergency_access.status != EmergencyAccessStatus::Invited as i32 { | ||||||
|         err!("The grantee user is already accepted or confirmed to the organization"); |         err!("The grantee user is already accepted or confirmed to the organization"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let email = match emergency_access.email.clone() { |     let Some(email) = emergency_access.email.clone() else { | ||||||
|         Some(email) => email, |         err!("Email not valid.") | ||||||
|         None => err!("Email not valid."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let grantee_user = match User::find_by_mail(&email, &mut conn).await { |     let Some(grantee_user) = User::find_by_mail(&email, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantee user not found.") | ||||||
|         None => err!("Grantee user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let grantor_user = headers.user; |     let grantor_user = headers.user; | ||||||
| @@ -356,16 +354,15 @@ async fn accept_invite(emer_id: &str, data: Json<AcceptData>, headers: Headers, | |||||||
|  |  | ||||||
|     // We need to search for the uuid in combination with the email, since we do not yet store the uuid of the grantee in the database. |     // We need to search for the uuid in combination with the email, since we do not yet store the uuid of the grantee in the database. | ||||||
|     // The uuid of the grantee gets stored once accepted. |     // The uuid of the grantee gets stored once accepted. | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_email(emer_id, &headers.user.email, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_email(emer_id, &headers.user.email, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     // get grantor user to send Accepted email |     // get grantor user to send Accepted email | ||||||
|     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if emer_id == claims.emer_id |     if emer_id == claims.emer_id | ||||||
| @@ -403,11 +400,11 @@ async fn confirm_emergency_access( | |||||||
|     let data: ConfirmData = data.into_inner(); |     let data: ConfirmData = data.into_inner(); | ||||||
|     let key = data.key; |     let key = data.key; | ||||||
|  |  | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &confirming_user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &confirming_user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if emergency_access.status != EmergencyAccessStatus::Accepted as i32 |     if emergency_access.status != EmergencyAccessStatus::Accepted as i32 | ||||||
|         || emergency_access.grantor_uuid != confirming_user.uuid |         || emergency_access.grantor_uuid != confirming_user.uuid | ||||||
| @@ -415,15 +412,13 @@ async fn confirm_emergency_access( | |||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&confirming_user.uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { |     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { | ||||||
|         let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { |         let Some(grantee_user) = User::find_by_uuid(grantee_uuid, &mut conn).await else { | ||||||
|             Some(user) => user, |             err!("Grantee user not found.") | ||||||
|             None => err!("Grantee user not found."), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         emergency_access.status = EmergencyAccessStatus::Confirmed as i32; |         emergency_access.status = EmergencyAccessStatus::Confirmed as i32; | ||||||
| @@ -450,19 +445,18 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db | |||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let initiating_user = headers.user; |     let initiating_user = headers.user; | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &initiating_user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &initiating_user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if emergency_access.status != EmergencyAccessStatus::Confirmed as i32 { |     if emergency_access.status != EmergencyAccessStatus::Confirmed as i32 { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let now = Utc::now().naive_utc(); |     let now = Utc::now().naive_utc(); | ||||||
| @@ -488,25 +482,23 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db | |||||||
| async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32 { |     if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32 { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&headers.user.uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { |     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { | ||||||
|         let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { |         let Some(grantee_user) = User::find_by_uuid(grantee_uuid, &mut conn).await else { | ||||||
|             Some(user) => user, |             err!("Grantee user not found.") | ||||||
|             None => err!("Grantee user not found."), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32; |         emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32; | ||||||
| @@ -525,11 +517,11 @@ async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbC | |||||||
| async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let mut emergency_access = |     let Some(mut emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32 |     if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32 | ||||||
|         && emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32 |         && emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32 | ||||||
| @@ -538,9 +530,8 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { |     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { | ||||||
|         let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { |         let Some(grantee_user) = User::find_by_uuid(grantee_uuid, &mut conn).await else { | ||||||
|             Some(user) => user, |             err!("Grantee user not found.") | ||||||
|             None => err!("Grantee user not found."), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         emergency_access.status = EmergencyAccessStatus::Confirmed as i32; |         emergency_access.status = EmergencyAccessStatus::Confirmed as i32; | ||||||
| @@ -563,11 +554,11 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo | |||||||
| async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let emergency_access = |     let Some(emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &headers.user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &headers.user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if !is_valid_request(&emergency_access, &headers.user.uuid, EmergencyAccessType::View) { |     if !is_valid_request(&emergency_access, &headers.user.uuid, EmergencyAccessType::View) { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
| @@ -602,19 +593,18 @@ async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: Db | |||||||
|     check_emergency_access_enabled()?; |     check_emergency_access_enabled()?; | ||||||
|  |  | ||||||
|     let requesting_user = headers.user; |     let requesting_user = headers.user; | ||||||
|     let emergency_access = |     let Some(emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { |     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let result = json!({ |     let result = json!({ | ||||||
| @@ -650,19 +640,18 @@ async fn password_emergency_access( | |||||||
|     //let key = &data.Key; |     //let key = &data.Key; | ||||||
|  |  | ||||||
|     let requesting_user = headers.user; |     let requesting_user = headers.user; | ||||||
|     let emergency_access = |     let Some(emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { |     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { |     let Some(mut grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // change grantor_user password |     // change grantor_user password | ||||||
| @@ -686,19 +675,18 @@ async fn password_emergency_access( | |||||||
| #[get("/emergency-access/<emer_id>/policies")] | #[get("/emergency-access/<emer_id>/policies")] | ||||||
| async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let requesting_user = headers.user; |     let requesting_user = headers.user; | ||||||
|     let emergency_access = |     let Some(emergency_access) = | ||||||
|         match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await { |         EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await | ||||||
|             Some(emer) => emer, |     else { | ||||||
|             None => err!("Emergency access not valid."), |         err!("Emergency access not valid.") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { |     if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) { | ||||||
|         err!("Emergency access not valid.") |         err!("Emergency access not valid.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { |     let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Grantor user not found.") | ||||||
|         None => err!("Grantor user not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &mut conn); |     let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &mut conn); | ||||||
|   | |||||||
| @@ -25,16 +25,10 @@ async fn get_folders(headers: Headers, mut conn: DbConn) -> Json<Value> { | |||||||
|  |  | ||||||
| #[get("/folders/<uuid>")] | #[get("/folders/<uuid>")] | ||||||
| async fn get_folder(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_folder(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let folder = match Folder::find_by_uuid(uuid, &mut conn).await { |     match Folder::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await { | ||||||
|         Some(folder) => folder, |         Some(folder) => Ok(Json(folder.to_json())), | ||||||
|         _ => err!("Invalid folder"), |         _ => err!("Invalid folder", "Folder does not exist or belongs to another user"), | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if folder.user_uuid != headers.user.uuid { |  | ||||||
|         err!("Folder belongs to another user") |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(Json(folder.to_json())) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Deserialize)] | #[derive(Deserialize)] | ||||||
| @@ -71,15 +65,10 @@ async fn put_folder( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: FolderData = data.into_inner(); |     let data: FolderData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut folder = match Folder::find_by_uuid(uuid, &mut conn).await { |     let Some(mut folder) = Folder::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(folder) => folder, |         err!("Invalid folder", "Folder does not exist or belongs to another user") | ||||||
|         _ => err!("Invalid folder"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if folder.user_uuid != headers.user.uuid { |  | ||||||
|         err!("Folder belongs to another user") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     folder.name = data.name; |     folder.name = data.name; | ||||||
|  |  | ||||||
|     folder.save(&mut conn).await?; |     folder.save(&mut conn).await?; | ||||||
| @@ -95,15 +84,10 @@ async fn delete_folder_post(uuid: &str, headers: Headers, conn: DbConn, nt: Noti | |||||||
|  |  | ||||||
| #[delete("/folders/<uuid>")] | #[delete("/folders/<uuid>")] | ||||||
| async fn delete_folder(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { | async fn delete_folder(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { | ||||||
|     let folder = match Folder::find_by_uuid(uuid, &mut conn).await { |     let Some(folder) = Folder::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(folder) => folder, |         err!("Invalid folder", "Folder does not exist or belongs to another user") | ||||||
|         _ => err!("Invalid folder"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if folder.user_uuid != headers.user.uuid { |  | ||||||
|         err!("Folder belongs to another user") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Delete the actual folder entry |     // Delete the actual folder entry | ||||||
|     folder.delete(&mut conn).await?; |     folder.delete(&mut conn).await?; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -267,9 +267,8 @@ async fn post_organization( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: OrganizationUpdateData = data.into_inner(); |     let data: OrganizationUpdateData = data.into_inner(); | ||||||
|  |  | ||||||
|     let mut org = match Organization::find_by_uuid(org_id, &mut conn).await { |     let Some(mut org) = Organization::find_by_uuid(org_id, &mut conn).await else { | ||||||
|         Some(organization) => organization, |         err!("Can't find organization details") | ||||||
|         None => err!("Can't find organization details"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     org.name = data.name; |     org.name = data.name; | ||||||
| @@ -318,9 +317,8 @@ async fn get_org_collections(org_id: &str, _headers: ManagerHeadersLoose, mut co | |||||||
| async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { | async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { | ||||||
|     let mut data = Vec::new(); |     let mut data = Vec::new(); | ||||||
|  |  | ||||||
|     let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { |     let Some(user_org) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { | ||||||
|         Some(u) => u, |         err!("User is not part of organization") | ||||||
|         None => err!("User is not part of organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // get all collection memberships for the current organization |     // get all collection memberships for the current organization | ||||||
| @@ -387,9 +385,8 @@ async fn post_organization_collections( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: NewCollectionData = data.into_inner(); |     let data: NewCollectionData = data.into_inner(); | ||||||
|  |  | ||||||
|     let org = match Organization::find_by_uuid(org_id, &mut conn).await { |     let Some(org) = Organization::find_by_uuid(org_id, &mut conn).await else { | ||||||
|         Some(organization) => organization, |         err!("Can't find organization details") | ||||||
|         None => err!("Can't find organization details"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let collection = Collection::new(org.uuid, data.name, data.external_id); |     let collection = Collection::new(org.uuid, data.name, data.external_id); | ||||||
| @@ -413,9 +410,8 @@ async fn post_organization_collections( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for user in data.users { |     for user in data.users { | ||||||
|         let org_user = match UserOrganization::find_by_uuid(&user.id, &mut conn).await { |         let Some(org_user) = UserOrganization::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { | ||||||
|             Some(u) => u, |             err!("User is not part of organization") | ||||||
|             None => err!("User is not part of organization"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if org_user.access_all { |         if org_user.access_all { | ||||||
| @@ -454,20 +450,14 @@ async fn post_organization_collection_update( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: NewCollectionData = data.into_inner(); |     let data: NewCollectionData = data.into_inner(); | ||||||
|  |  | ||||||
|     let org = match Organization::find_by_uuid(org_id, &mut conn).await { |     if Organization::find_by_uuid(org_id, &mut conn).await.is_none() { | ||||||
|         Some(organization) => organization, |         err!("Can't find organization details") | ||||||
|         None => err!("Can't find organization details"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let mut collection = match Collection::find_by_uuid(col_id, &mut conn).await { |     let Some(mut collection) = Collection::find_by_uuid_and_org(col_id, org_id, &mut conn).await else { | ||||||
|         Some(collection) => collection, |         err!("Collection not found") | ||||||
|         None => err!("Collection not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if collection.org_uuid != org.uuid { |  | ||||||
|         err!("Collection is not owned by organization"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     collection.name = data.name; |     collection.name = data.name; | ||||||
|     collection.external_id = match data.external_id { |     collection.external_id = match data.external_id { | ||||||
|         Some(external_id) if !external_id.trim().is_empty() => Some(external_id), |         Some(external_id) if !external_id.trim().is_empty() => Some(external_id), | ||||||
| @@ -498,9 +488,8 @@ async fn post_organization_collection_update( | |||||||
|     CollectionUser::delete_all_by_collection(col_id, &mut conn).await?; |     CollectionUser::delete_all_by_collection(col_id, &mut conn).await?; | ||||||
|  |  | ||||||
|     for user in data.users { |     for user in data.users { | ||||||
|         let org_user = match UserOrganization::find_by_uuid(&user.id, &mut conn).await { |         let Some(org_user) = UserOrganization::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { | ||||||
|             Some(u) => u, |             err!("User is not part of organization") | ||||||
|             None => err!("User is not part of organization"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if org_user.access_all { |         if org_user.access_all { | ||||||
| @@ -521,15 +510,8 @@ async fn delete_organization_collection_user( | |||||||
|     _headers: AdminHeaders, |     _headers: AdminHeaders, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let collection = match Collection::find_by_uuid(col_id, &mut conn).await { |     let Some(collection) = Collection::find_by_uuid_and_org(col_id, org_id, &mut conn).await else { | ||||||
|         None => err!("Collection not found"), |         err!("Collection not found", "Collection does not exist or does not belong to this organization") | ||||||
|         Some(collection) => { |  | ||||||
|             if collection.org_uuid == org_id { |  | ||||||
|                 collection |  | ||||||
|             } else { |  | ||||||
|                 err!("Collection and Organization id do not match") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { |     match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { | ||||||
| @@ -560,26 +542,20 @@ async fn _delete_organization_collection( | |||||||
|     headers: &ManagerHeaders, |     headers: &ManagerHeaders, | ||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     match Collection::find_by_uuid(col_id, conn).await { |     let Some(collection) = Collection::find_by_uuid_and_org(col_id, org_id, conn).await else { | ||||||
|         None => err!("Collection not found"), |         err!("Collection not found", "Collection does not exist or does not belong to this organization") | ||||||
|         Some(collection) => { |     }; | ||||||
|             if collection.org_uuid == org_id { |     log_event( | ||||||
|                 log_event( |         EventType::CollectionDeleted as i32, | ||||||
|                     EventType::CollectionDeleted as i32, |         &collection.uuid, | ||||||
|                     &collection.uuid, |         org_id, | ||||||
|                     org_id, |         &headers.user.uuid, | ||||||
|                     &headers.user.uuid, |         headers.device.atype, | ||||||
|                     headers.device.atype, |         &headers.ip.ip, | ||||||
|                     &headers.ip.ip, |         conn, | ||||||
|                     conn, |     ) | ||||||
|                 ) |     .await; | ||||||
|                 .await; |     collection.delete(conn).await | ||||||
|                 collection.delete(conn).await |  | ||||||
|             } else { |  | ||||||
|                 err!("Collection and Organization id do not match") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[delete("/organizations/<org_id>/collections/<col_id>")] | #[delete("/organizations/<org_id>/collections/<col_id>")] | ||||||
| @@ -601,12 +577,11 @@ struct DeleteCollectionData { | |||||||
|     org_id: String, |     org_id: String, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[post("/organizations/<org_id>/collections/<col_id>/delete", data = "<_data>")] | #[post("/organizations/<org_id>/collections/<col_id>/delete")] | ||||||
| async fn post_organization_collection_delete( | async fn post_organization_collection_delete( | ||||||
|     org_id: &str, |     org_id: &str, | ||||||
|     col_id: &str, |     col_id: &str, | ||||||
|     headers: ManagerHeaders, |     headers: ManagerHeaders, | ||||||
|     _data: Json<DeleteCollectionData>, |  | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     _delete_organization_collection(org_id, col_id, &headers, &mut conn).await |     _delete_organization_collection(org_id, col_id, &headers, &mut conn).await | ||||||
| @@ -651,9 +626,9 @@ async fn get_org_collection_detail( | |||||||
|                 err!("Collection is not owned by organization") |                 err!("Collection is not owned by organization") | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { |             let Some(user_org) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await | ||||||
|                 Some(u) => u, |             else { | ||||||
|                 None => err!("User is not part of organization"), |                 err!("User is not part of organization") | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             let groups: Vec<Value> = if CONFIG.org_groups_enabled() { |             let groups: Vec<Value> = if CONFIG.org_groups_enabled() { | ||||||
| @@ -695,9 +670,8 @@ async fn get_org_collection_detail( | |||||||
| #[get("/organizations/<org_id>/collections/<coll_id>/users")] | #[get("/organizations/<org_id>/collections/<coll_id>/users")] | ||||||
| async fn get_collection_users(org_id: &str, coll_id: &str, _headers: ManagerHeaders, mut conn: DbConn) -> JsonResult { | async fn get_collection_users(org_id: &str, coll_id: &str, _headers: ManagerHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     // Get org and collection, check that collection is from org |     // Get org and collection, check that collection is from org | ||||||
|     let collection = match Collection::find_by_uuid_and_org(coll_id, org_id, &mut conn).await { |     let Some(collection) = Collection::find_by_uuid_and_org(coll_id, org_id, &mut conn).await else { | ||||||
|         None => err!("Collection not found in Organization"), |         err!("Collection not found in Organization") | ||||||
|         Some(collection) => collection, |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let mut user_list = Vec::new(); |     let mut user_list = Vec::new(); | ||||||
| @@ -731,9 +705,8 @@ async fn put_collection_users( | |||||||
|  |  | ||||||
|     // And then add all the received ones (except if the user has access_all) |     // And then add all the received ones (except if the user has access_all) | ||||||
|     for d in data.iter() { |     for d in data.iter() { | ||||||
|         let user = match UserOrganization::find_by_uuid(&d.id, &mut conn).await { |         let Some(user) = UserOrganization::find_by_uuid_and_org(&d.id, org_id, &mut conn).await else { | ||||||
|             Some(u) => u, |             err!("User is not part of organization") | ||||||
|             None => err!("User is not part of organization"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if user.access_all { |         if user.access_all { | ||||||
| @@ -1007,18 +980,16 @@ async fn reinvite_user(org_id: &str, user_org: &str, headers: AdminHeaders, mut | |||||||
| } | } | ||||||
|  |  | ||||||
| async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { | async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { | ||||||
|     let user_org = match UserOrganization::find_by_uuid(user_org, conn).await { |     let Some(user_org) = UserOrganization::find_by_uuid_and_org(user_org, org_id, conn).await else { | ||||||
|         Some(user_org) => user_org, |         err!("The user hasn't been invited to the organization.") | ||||||
|         None => err!("The user hasn't been invited to the organization."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if user_org.status != UserOrgStatus::Invited as i32 { |     if user_org.status != UserOrgStatus::Invited as i32 { | ||||||
|         err!("The user is already accepted or confirmed to the organization") |         err!("The user is already accepted or confirmed to the organization") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let user = match User::find_by_uuid(&user_org.user_uuid, conn).await { |     let Some(user) = User::find_by_uuid(&user_org.user_uuid, conn).await else { | ||||||
|         Some(user) => user, |         err!("User not found.") | ||||||
|         None => err!("User not found."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !CONFIG.invitations_allowed() && user.password_hash.is_empty() { |     if !CONFIG.invitations_allowed() && user.password_hash.is_empty() { | ||||||
| @@ -1059,20 +1030,25 @@ struct AcceptData { | |||||||
|     reset_password_key: Option<String>, |     reset_password_key: Option<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[post("/organizations/<org_id>/users/<_org_user_id>/accept", data = "<data>")] | #[post("/organizations/<org_id>/users/<org_user_id>/accept", data = "<data>")] | ||||||
| async fn accept_invite(org_id: &str, _org_user_id: &str, data: Json<AcceptData>, mut conn: DbConn) -> EmptyResult { | async fn accept_invite(org_id: &str, org_user_id: &str, data: Json<AcceptData>, mut conn: DbConn) -> EmptyResult { | ||||||
|     // The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead |     // The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead | ||||||
|     let data: AcceptData = data.into_inner(); |     let data: AcceptData = data.into_inner(); | ||||||
|     let claims = decode_invite(&data.token)?; |     let claims = decode_invite(&data.token)?; | ||||||
|  |  | ||||||
|  |     // If a claim does not have a user_org_id or it does not match the one in from the URI, something is wrong. | ||||||
|  |     match &claims.user_org_id { | ||||||
|  |         Some(ou_id) if ou_id.eq(org_user_id) => {} | ||||||
|  |         _ => err!("Error accepting the invitation", "Claim does not match the org_user_id"), | ||||||
|  |     } | ||||||
|  |  | ||||||
|     match User::find_by_mail(&claims.email, &mut conn).await { |     match User::find_by_mail(&claims.email, &mut conn).await { | ||||||
|         Some(user) => { |         Some(user) => { | ||||||
|             Invitation::take(&claims.email, &mut conn).await; |             Invitation::take(&claims.email, &mut conn).await; | ||||||
|  |  | ||||||
|             if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) { |             if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) { | ||||||
|                 let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &mut conn).await { |                 let Some(mut user_org) = UserOrganization::find_by_uuid_and_org(user_org, org, &mut conn).await else { | ||||||
|                     Some(user_org) => user_org, |                     err!("Error accepting the invitation") | ||||||
|                     None => err!("Error accepting the invitation"), |  | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 if user_org.status != UserOrgStatus::Invited as i32 { |                 if user_org.status != UserOrgStatus::Invited as i32 { | ||||||
| @@ -1213,9 +1189,8 @@ async fn _confirm_invite( | |||||||
|         err!("Key or UserId is not set, unable to process request"); |         err!("Key or UserId is not set, unable to process request"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let mut user_to_confirm = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { |     let Some(mut user_to_confirm) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { | ||||||
|         Some(user) => user, |         err!("The specified user isn't a member of the organization") | ||||||
|         None => err!("The specified user isn't a member of the organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if user_to_confirm.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { |     if user_to_confirm.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { | ||||||
| @@ -1287,9 +1262,8 @@ async fn get_user( | |||||||
|     _headers: AdminHeaders, |     _headers: AdminHeaders, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { |     let Some(user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("The specified user isn't a member of the organization") | ||||||
|         None => err!("The specified user isn't a member of the organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // In this case, when groups are requested we also need to include collections. |     // In this case, when groups are requested we also need to include collections. | ||||||
| @@ -1331,14 +1305,12 @@ async fn edit_user( | |||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let data: EditUserData = data.into_inner(); |     let data: EditUserData = data.into_inner(); | ||||||
|  |  | ||||||
|     let new_type = match UserOrgType::from_str(&data.r#type.into_string()) { |     let Some(new_type) = UserOrgType::from_str(&data.r#type.into_string()) else { | ||||||
|         Some(new_type) => new_type, |         err!("Invalid type") | ||||||
|         None => err!("Invalid type"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { |     let Some(mut user_to_edit) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("The specified user isn't member of the organization") | ||||||
|         None => err!("The specified user isn't member of the organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if new_type != user_to_edit.atype |     if new_type != user_to_edit.atype | ||||||
| @@ -1490,9 +1462,8 @@ async fn _delete_user( | |||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
|     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 Some(user_to_delete) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { | ||||||
|         Some(user) => user, |         err!("User to delete isn't member of the organization") | ||||||
|         None => err!("User to delete isn't member of the organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if user_to_delete.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { |     if user_to_delete.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { | ||||||
| @@ -1725,9 +1696,8 @@ async fn list_policies_token(org_id: &str, token: &str, mut conn: DbConn) -> Jso | |||||||
|  |  | ||||||
|     let invite = decode_invite(token)?; |     let invite = decode_invite(token)?; | ||||||
|  |  | ||||||
|     let invite_org_id = match invite.org_id { |     let Some(invite_org_id) = invite.org_id else { | ||||||
|         Some(invite_org_id) => invite_org_id, |         err!("Invalid token") | ||||||
|         None => err!("Invalid token"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if invite_org_id != org_id { |     if invite_org_id != org_id { | ||||||
| @@ -1747,9 +1717,8 @@ async fn list_policies_token(org_id: &str, token: &str, mut conn: DbConn) -> Jso | |||||||
|  |  | ||||||
| #[get("/organizations/<org_id>/policies/<pol_type>")] | #[get("/organizations/<org_id>/policies/<pol_type>")] | ||||||
| async fn get_policy(org_id: &str, pol_type: i32, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | async fn get_policy(org_id: &str, pol_type: i32, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     let pol_type_enum = match OrgPolicyType::from_i32(pol_type) { |     let Some(pol_type_enum) = OrgPolicyType::from_i32(pol_type) else { | ||||||
|         Some(pt) => pt, |         err!("Invalid or unsupported policy type") | ||||||
|         None => err!("Invalid or unsupported policy type"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &mut conn).await { |     let policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &mut conn).await { | ||||||
| @@ -1778,9 +1747,8 @@ async fn put_policy( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let data: PolicyData = data.into_inner(); |     let data: PolicyData = data.into_inner(); | ||||||
|  |  | ||||||
|     let pol_type_enum = match OrgPolicyType::from_i32(pol_type) { |     let Some(pol_type_enum) = OrgPolicyType::from_i32(pol_type) else { | ||||||
|         Some(pt) => pt, |         err!("Invalid or unsupported policy type") | ||||||
|         None => err!("Invalid or unsupported policy type"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Bitwarden only allows the Reset Password policy when Single Org policy is enabled |     // Bitwarden only allows the Reset Password policy when Single Org policy is enabled | ||||||
| @@ -2437,9 +2405,8 @@ async fn put_group( | |||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let group = match Group::find_by_uuid(group_id, &mut conn).await { |     let Some(group) = Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await else { | ||||||
|         Some(group) => group, |         err!("Group not found", "Group uuid is invalid or does not belong to the organization") | ||||||
|         None => err!("Group not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let group_request = data.into_inner(); |     let group_request = data.into_inner(); | ||||||
| @@ -2502,15 +2469,14 @@ async fn add_update_group( | |||||||
|     }))) |     }))) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[get("/organizations/<_org_id>/groups/<group_id>/details")] | #[get("/organizations/<org_id>/groups/<group_id>/details")] | ||||||
| async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | async fn get_group_details(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     if !CONFIG.org_groups_enabled() { |     if !CONFIG.org_groups_enabled() { | ||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let group = match Group::find_by_uuid(group_id, &mut conn).await { |     let Some(group) = Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await else { | ||||||
|         Some(group) => group, |         err!("Group not found", "Group uuid is invalid or does not belong to the organization") | ||||||
|         _ => err!("Group could not be found!"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     Ok(Json(group.to_json_details(&mut conn).await)) |     Ok(Json(group.to_json_details(&mut conn).await)) | ||||||
| @@ -2531,9 +2497,8 @@ async fn _delete_group(org_id: &str, group_id: &str, headers: &AdminHeaders, con | |||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let group = match Group::find_by_uuid(group_id, conn).await { |     let Some(group) = Group::find_by_uuid_and_org(group_id, org_id, conn).await else { | ||||||
|         Some(group) => group, |         err!("Group not found", "Group uuid is invalid or does not belong to the organization") | ||||||
|         _ => err!("Group not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     log_event( |     log_event( | ||||||
| @@ -2569,29 +2534,27 @@ async fn bulk_delete_groups( | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[get("/organizations/<_org_id>/groups/<group_id>")] | #[get("/organizations/<org_id>/groups/<group_id>")] | ||||||
| async fn get_group(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | async fn get_group(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     if !CONFIG.org_groups_enabled() { |     if !CONFIG.org_groups_enabled() { | ||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let group = match Group::find_by_uuid(group_id, &mut conn).await { |     let Some(group) = Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await else { | ||||||
|         Some(group) => group, |         err!("Group not found", "Group uuid is invalid or does not belong to the organization") | ||||||
|         _ => err!("Group not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     Ok(Json(group.to_json())) |     Ok(Json(group.to_json())) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[get("/organizations/<_org_id>/groups/<group_id>/users")] | #[get("/organizations/<org_id>/groups/<group_id>/users")] | ||||||
| async fn get_group_users(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | async fn get_group_users(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     if !CONFIG.org_groups_enabled() { |     if !CONFIG.org_groups_enabled() { | ||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     match Group::find_by_uuid(group_id, &mut conn).await { |     if Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(_) => { /* Do nothing */ } |         err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization") | ||||||
|         _ => err!("Group could not be found!"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let group_users: Vec<String> = GroupUser::find_by_group(group_id, &mut conn) |     let group_users: Vec<String> = GroupUser::find_by_group(group_id, &mut conn) | ||||||
| @@ -2615,9 +2578,8 @@ async fn put_group_users( | |||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     match Group::find_by_uuid(group_id, &mut conn).await { |     if Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(_) => { /* Do nothing */ } |         err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization") | ||||||
|         _ => err!("Group could not be found!"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     GroupUser::delete_all_by_group(group_id, &mut conn).await?; |     GroupUser::delete_all_by_group(group_id, &mut conn).await?; | ||||||
| @@ -2642,15 +2604,14 @@ async fn put_group_users( | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[get("/organizations/<_org_id>/users/<user_id>/groups")] | #[get("/organizations/<org_id>/users/<user_id>/groups")] | ||||||
| async fn get_user_groups(_org_id: &str, user_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | async fn get_user_groups(org_id: &str, user_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { | ||||||
|     if !CONFIG.org_groups_enabled() { |     if !CONFIG.org_groups_enabled() { | ||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     match UserOrganization::find_by_uuid(user_id, &mut conn).await { |     if UserOrganization::find_by_uuid_and_org(user_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(_) => { /* Do nothing */ } |         err!("User could not be found!") | ||||||
|         _ => err!("User could not be found!"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let user_groups: Vec<String> = |     let user_groups: Vec<String> = | ||||||
| @@ -2688,13 +2649,8 @@ async fn put_user_groups( | |||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let user_org = match UserOrganization::find_by_uuid(org_user_id, &mut conn).await { |     if UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(uo) => uo, |         err!("User could not be found or does not belong to the organization."); | ||||||
|         _ => err!("User could not be found!"), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if user_org.org_uuid != org_id { |  | ||||||
|         err!("Group doesn't belong to organization"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     GroupUser::delete_all_by_user(org_user_id, &mut conn).await?; |     GroupUser::delete_all_by_user(org_user_id, &mut conn).await?; | ||||||
| @@ -2742,22 +2698,12 @@ async fn delete_group_user( | |||||||
|         err!("Group support is disabled"); |         err!("Group support is disabled"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let user_org = match UserOrganization::find_by_uuid(org_user_id, &mut conn).await { |     if UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(uo) => uo, |         err!("User could not be found or does not belong to the organization."); | ||||||
|         _ => err!("User could not be found!"), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if user_org.org_uuid != org_id { |  | ||||||
|         err!("User doesn't belong to organization"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let group = match Group::find_by_uuid(group_id, &mut conn).await { |     if Group::find_by_uuid_and_org(group_id, org_id, &mut conn).await.is_none() { | ||||||
|         Some(g) => g, |         err!("Group could not be found or does not belong to the organization."); | ||||||
|         _ => err!("Group could not be found!"), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if group.organizations_uuid != org_id { |  | ||||||
|         err!("Group doesn't belong to organization"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     log_event( |     log_event( | ||||||
| @@ -2794,9 +2740,8 @@ struct OrganizationUserResetPasswordRequest { | |||||||
| // Just add it here in case they will | // Just add it here in case they will | ||||||
| #[get("/organizations/<org_id>/public-key")] | #[get("/organizations/<org_id>/public-key")] | ||||||
| async fn get_organization_public_key(org_id: &str, _headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_organization_public_key(org_id: &str, _headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let org = match Organization::find_by_uuid(org_id, &mut conn).await { |     let Some(org) = Organization::find_by_uuid(org_id, &mut conn).await else { | ||||||
|         Some(organization) => organization, |         err!("Organization not found") | ||||||
|         None => err!("Organization not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     Ok(Json(json!({ |     Ok(Json(json!({ | ||||||
| @@ -2821,19 +2766,16 @@ async fn put_reset_password( | |||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
|     nt: Notify<'_>, |     nt: Notify<'_>, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let org = match Organization::find_by_uuid(org_id, &mut conn).await { |     let Some(org) = Organization::find_by_uuid(org_id, &mut conn).await else { | ||||||
|         Some(org) => org, |         err!("Required organization not found") | ||||||
|         None => err!("Required organization not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, &org.uuid, &mut conn).await { |     let Some(org_user) = UserOrganization::find_by_uuid_and_org(org_user_id, &org.uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("User to reset isn't member of required organization") | ||||||
|         None => err!("User to reset isn't member of required organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { |     let Some(user) = User::find_by_uuid(&org_user.user_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("User not found") | ||||||
|         None => err!("User not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; |     check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; | ||||||
| @@ -2880,19 +2822,16 @@ async fn get_reset_password_details( | |||||||
|     headers: AdminHeaders, |     headers: AdminHeaders, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let org = match Organization::find_by_uuid(org_id, &mut conn).await { |     let Some(org) = Organization::find_by_uuid(org_id, &mut conn).await else { | ||||||
|         Some(org) => org, |         err!("Required organization not found") | ||||||
|         None => err!("Required organization not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { |     let Some(org_user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("User to reset isn't member of required organization") | ||||||
|         None => err!("User to reset isn't member of required organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { |     let Some(user) = User::find_by_uuid(&org_user.user_uuid, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("User not found") | ||||||
|         None => err!("User not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; |     check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; | ||||||
| @@ -2918,9 +2857,8 @@ async fn check_reset_password_applicable_and_permissions( | |||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     check_reset_password_applicable(org_id, conn).await?; |     check_reset_password_applicable(org_id, conn).await?; | ||||||
|  |  | ||||||
|     let target_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { |     let Some(target_user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { | ||||||
|         Some(user) => user, |         err!("Reset target user not found") | ||||||
|         None => err!("Reset target user not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Resetting user must be higher/equal to user to reset |     // Resetting user must be higher/equal to user to reset | ||||||
| @@ -2936,9 +2874,8 @@ async fn check_reset_password_applicable(org_id: &str, conn: &mut DbConn) -> Emp | |||||||
|         err!("Password reset is not supported on an email-disabled instance."); |         err!("Password reset is not supported on an email-disabled instance."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let policy = match OrgPolicy::find_by_org_and_type(org_id, OrgPolicyType::ResetPassword, conn).await { |     let Some(policy) = OrgPolicy::find_by_org_and_type(org_id, OrgPolicyType::ResetPassword, conn).await else { | ||||||
|         Some(p) => p, |         err!("Policy not found") | ||||||
|         None => err!("Policy not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !policy.enabled { |     if !policy.enabled { | ||||||
| @@ -2956,9 +2893,8 @@ async fn put_reset_password_enrollment( | |||||||
|     data: Json<OrganizationUserResetPasswordEnrollmentRequest>, |     data: Json<OrganizationUserResetPasswordEnrollmentRequest>, | ||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { |     let Some(mut org_user) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { | ||||||
|         Some(u) => u, |         err!("User to enroll isn't member of required organization") | ||||||
|         None => err!("User to enroll isn't member of required organization"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     check_reset_password_applicable(org_id, &mut conn).await?; |     check_reset_password_applicable(org_id, &mut conn).await?; | ||||||
|   | |||||||
| @@ -203,9 +203,8 @@ impl<'r> FromRequest<'r> for PublicToken { | |||||||
|             None => err_handler!("No access token provided"), |             None => err_handler!("No access token provided"), | ||||||
|         }; |         }; | ||||||
|         // Check JWT token is valid and get device and user from it |         // Check JWT token is valid and get device and user from it | ||||||
|         let claims = match auth::decode_api_org(access_token) { |         let Ok(claims) = auth::decode_api_org(access_token) else { | ||||||
|             Ok(claims) => claims, |             err_handler!("Invalid claim") | ||||||
|             Err(_) => err_handler!("Invalid claim"), |  | ||||||
|         }; |         }; | ||||||
|         // Check if time is between claims.nbf and claims.exp |         // Check if time is between claims.nbf and claims.exp | ||||||
|         let time_now = Utc::now().timestamp(); |         let time_now = Utc::now().timestamp(); | ||||||
| @@ -227,13 +226,11 @@ impl<'r> FromRequest<'r> for PublicToken { | |||||||
|             Outcome::Success(conn) => conn, |             Outcome::Success(conn) => conn, | ||||||
|             _ => err_handler!("Error getting DB"), |             _ => err_handler!("Error getting DB"), | ||||||
|         }; |         }; | ||||||
|         let org_uuid = match claims.client_id.strip_prefix("organization.") { |         let Some(org_uuid) = claims.client_id.strip_prefix("organization.") else { | ||||||
|             Some(uuid) => uuid, |             err_handler!("Malformed client_id") | ||||||
|             None => err_handler!("Malformed client_id"), |  | ||||||
|         }; |         }; | ||||||
|         let org_api_key = match OrganizationApiKey::find_by_org_uuid(org_uuid, &conn).await { |         let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(org_uuid, &conn).await else { | ||||||
|             Some(org_api_key) => org_api_key, |             err_handler!("Invalid client_id") | ||||||
|             None => err_handler!("Invalid client_id"), |  | ||||||
|         }; |         }; | ||||||
|         if org_api_key.org_uuid != claims.client_sub { |         if org_api_key.org_uuid != claims.client_sub { | ||||||
|             err_handler!("Token not issued for this org"); |             err_handler!("Token not issued for this org"); | ||||||
|   | |||||||
| @@ -159,16 +159,10 @@ async fn get_sends(headers: Headers, mut conn: DbConn) -> Json<Value> { | |||||||
|  |  | ||||||
| #[get("/sends/<uuid>")] | #[get("/sends/<uuid>")] | ||||||
| async fn get_send(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | async fn get_send(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { | ||||||
|     let send = match Send::find_by_uuid(uuid, &mut conn).await { |     match Send::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await { | ||||||
|         Some(send) => send, |         Some(send) => Ok(Json(send.to_json())), | ||||||
|         None => err!("Send not found"), |         None => err!("Send not found", "Invalid uuid or does not belong to user"), | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if send.user_uuid.as_ref() != Some(&headers.user.uuid) { |  | ||||||
|         err!("Send is not owned by user") |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(Json(send.to_json())) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[post("/sends", data = "<data>")] | #[post("/sends", data = "<data>")] | ||||||
| @@ -371,22 +365,14 @@ async fn post_send_file_v2_data( | |||||||
|  |  | ||||||
|     let mut data = data.into_inner(); |     let mut data = data.into_inner(); | ||||||
|  |  | ||||||
|     let Some(send) = Send::find_by_uuid(send_uuid, &mut conn).await else { |     let Some(send) = Send::find_by_uuid_and_user(send_uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         err!("Send not found. Unable to save the file.") |         err!("Send not found. Unable to save the file.", "Invalid uuid or does not belong to user.") | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if send.atype != SendType::File as i32 { |     if send.atype != SendType::File as i32 { | ||||||
|         err!("Send is not a file type send."); |         err!("Send is not a file type send."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let Some(send_user_id) = &send.user_uuid else { |  | ||||||
|         err!("Sends are only supported for users at the moment.") |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if send_user_id != &headers.user.uuid { |  | ||||||
|         err!("Send doesn't belong to user."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let Ok(send_data) = serde_json::from_str::<SendFileData>(&send.data) else { |     let Ok(send_data) = serde_json::from_str::<SendFileData>(&send.data) else { | ||||||
|         err!("Unable to decode send data as json.") |         err!("Unable to decode send data as json.") | ||||||
|     }; |     }; | ||||||
| @@ -456,9 +442,8 @@ async fn post_access( | |||||||
|     ip: ClientIp, |     ip: ClientIp, | ||||||
|     nt: Notify<'_>, |     nt: Notify<'_>, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let mut send = match Send::find_by_access_id(access_id, &mut conn).await { |     let Some(mut send) = Send::find_by_access_id(access_id, &mut conn).await else { | ||||||
|         Some(s) => s, |         err_code!(SEND_INACCESSIBLE_MSG, 404) | ||||||
|         None => err_code!(SEND_INACCESSIBLE_MSG, 404), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if let Some(max_access_count) = send.max_access_count { |     if let Some(max_access_count) = send.max_access_count { | ||||||
| @@ -517,9 +502,8 @@ async fn post_access_file( | |||||||
|     mut conn: DbConn, |     mut conn: DbConn, | ||||||
|     nt: Notify<'_>, |     nt: Notify<'_>, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     let mut send = match Send::find_by_uuid(send_id, &mut conn).await { |     let Some(mut send) = Send::find_by_uuid(send_id, &mut conn).await else { | ||||||
|         Some(s) => s, |         err_code!(SEND_INACCESSIBLE_MSG, 404) | ||||||
|         None => err_code!(SEND_INACCESSIBLE_MSG, 404), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if let Some(max_access_count) = send.max_access_count { |     if let Some(max_access_count) = send.max_access_count { | ||||||
| @@ -582,16 +566,15 @@ async fn download_send(send_id: SafeString, file_id: SafeString, t: &str) -> Opt | |||||||
|     None |     None | ||||||
| } | } | ||||||
|  |  | ||||||
| #[put("/sends/<id>", data = "<data>")] | #[put("/sends/<uuid>", data = "<data>")] | ||||||
| async fn put_send(id: &str, data: Json<SendData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { | async fn put_send(uuid: &str, data: Json<SendData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { | ||||||
|     enforce_disable_send_policy(&headers, &mut conn).await?; |     enforce_disable_send_policy(&headers, &mut conn).await?; | ||||||
|  |  | ||||||
|     let data: SendData = data.into_inner(); |     let data: SendData = data.into_inner(); | ||||||
|     enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; |     enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; | ||||||
|  |  | ||||||
|     let mut send = match Send::find_by_uuid(id, &mut conn).await { |     let Some(mut send) = Send::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(s) => s, |         err!("Send not found", "Send uuid is invalid or does not belong to user") | ||||||
|         None => err!("Send not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     update_send_from_data(&mut send, data, &headers, &mut conn, &nt, UpdateType::SyncSendUpdate).await?; |     update_send_from_data(&mut send, data, &headers, &mut conn, &nt, UpdateType::SyncSendUpdate).await?; | ||||||
| @@ -657,17 +640,12 @@ pub async fn update_send_from_data( | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[delete("/sends/<id>")] | #[delete("/sends/<uuid>")] | ||||||
| async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { | async fn delete_send(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { | ||||||
|     let send = match Send::find_by_uuid(id, &mut conn).await { |     let Some(send) = Send::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(s) => s, |         err!("Send not found", "Invalid send uuid, or does not belong to user") | ||||||
|         None => err!("Send not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if send.user_uuid.as_ref() != Some(&headers.user.uuid) { |  | ||||||
|         err!("Send is not owned by user") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     send.delete(&mut conn).await?; |     send.delete(&mut conn).await?; | ||||||
|     nt.send_send_update( |     nt.send_send_update( | ||||||
|         UpdateType::SyncSendDelete, |         UpdateType::SyncSendDelete, | ||||||
| @@ -681,19 +659,14 @@ async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_ | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[put("/sends/<id>/remove-password")] | #[put("/sends/<uuid>/remove-password")] | ||||||
| async fn put_remove_password(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { | async fn put_remove_password(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { | ||||||
|     enforce_disable_send_policy(&headers, &mut conn).await?; |     enforce_disable_send_policy(&headers, &mut conn).await?; | ||||||
|  |  | ||||||
|     let mut send = match Send::find_by_uuid(id, &mut conn).await { |     let Some(mut send) = Send::find_by_uuid_and_user(uuid, &headers.user.uuid, &mut conn).await else { | ||||||
|         Some(s) => s, |         err!("Send not found", "Invalid send uuid, or does not belong to user") | ||||||
|         None => err!("Send not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if send.user_uuid.as_ref() != Some(&headers.user.uuid) { |  | ||||||
|         err!("Send is not owned by user") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     send.set_password(None); |     send.set_password(None); | ||||||
|     send.save(&mut conn).await?; |     send.save(&mut conn).await?; | ||||||
|     nt.send_send_update( |     nt.send_send_update( | ||||||
|   | |||||||
| @@ -117,9 +117,8 @@ pub async fn validate_totp_code( | |||||||
| ) -> EmptyResult { | ) -> EmptyResult { | ||||||
|     use totp_lite::{totp_custom, Sha1}; |     use totp_lite::{totp_custom, Sha1}; | ||||||
|  |  | ||||||
|     let decoded_secret = match BASE32.decode(secret.as_bytes()) { |     let Ok(decoded_secret) = BASE32.decode(secret.as_bytes()) else { | ||||||
|         Ok(s) => s, |         err!("Invalid TOTP secret") | ||||||
|         Err(_) => err!("Invalid TOTP secret"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let mut twofactor = |     let mut twofactor = | ||||||
|   | |||||||
| @@ -232,9 +232,8 @@ async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus { | |||||||
|     let type_ = TwoFactorType::Duo as i32; |     let type_ = TwoFactorType::Duo as i32; | ||||||
|  |  | ||||||
|     // If the user doesn't have an entry, disabled |     // If the user doesn't have an entry, disabled | ||||||
|     let twofactor = match TwoFactor::find_by_user_and_type(uuid, type_, conn).await { |     let Some(twofactor) = TwoFactor::find_by_user_and_type(uuid, type_, conn).await else { | ||||||
|         Some(t) => t, |         return DuoStatus::Disabled(DuoData::global().is_some()); | ||||||
|         None => return DuoStatus::Disabled(DuoData::global().is_some()), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // If the user has the required values, we use those |     // If the user has the required values, we use those | ||||||
| @@ -333,14 +332,12 @@ fn parse_duo_values(key: &str, val: &str, ikey: &str, prefix: &str, time: i64) - | |||||||
|         err!("Prefixes don't match") |         err!("Prefixes don't match") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let cookie_vec = match BASE64.decode(u_b64.as_bytes()) { |     let Ok(cookie_vec) = BASE64.decode(u_b64.as_bytes()) else { | ||||||
|         Ok(c) => c, |         err!("Invalid Duo cookie encoding") | ||||||
|         Err(_) => err!("Invalid Duo cookie encoding"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let cookie = match String::from_utf8(cookie_vec) { |     let Ok(cookie) = String::from_utf8(cookie_vec) else { | ||||||
|         Ok(c) => c, |         err!("Invalid Duo cookie encoding") | ||||||
|         Err(_) => err!("Invalid Duo cookie encoding"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let cookie_split: Vec<&str> = cookie.split('|').collect(); |     let cookie_split: Vec<&str> = cookie.split('|').collect(); | ||||||
|   | |||||||
| @@ -40,9 +40,8 @@ async fn send_email_login(data: Json<SendEmailLoginData>, mut conn: DbConn) -> E | |||||||
|     use crate::db::models::User; |     use crate::db::models::User; | ||||||
|  |  | ||||||
|     // Get the user |     // Get the user | ||||||
|     let user = match User::find_by_mail(&data.email, &mut conn).await { |     let Some(user) = User::find_by_mail(&data.email, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Username or password is incorrect. Try again.") | ||||||
|         None => err!("Username or password is incorrect. Try again."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Check password |     // Check password | ||||||
| @@ -174,9 +173,8 @@ async fn email(data: Json<EmailData>, headers: Headers, mut conn: DbConn) -> Jso | |||||||
|  |  | ||||||
|     let mut email_data = EmailTokenData::from_json(&twofactor.data)?; |     let mut email_data = EmailTokenData::from_json(&twofactor.data)?; | ||||||
|  |  | ||||||
|     let issued_token = match &email_data.last_token { |     let Some(issued_token) = &email_data.last_token else { | ||||||
|         Some(t) => t, |         err!("No token available") | ||||||
|         _ => err!("No token available"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !crypto::ct_eq(issued_token, data.token) { |     if !crypto::ct_eq(issued_token, data.token) { | ||||||
| @@ -205,14 +203,13 @@ pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, c | |||||||
|     let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn) |     let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn) | ||||||
|         .await |         .await | ||||||
|         .map_res("Two factor not found")?; |         .map_res("Two factor not found")?; | ||||||
|     let issued_token = match &email_data.last_token { |     let Some(issued_token) = &email_data.last_token else { | ||||||
|         Some(t) => t, |         err!( | ||||||
|         _ => err!( |  | ||||||
|             "No token available", |             "No token available", | ||||||
|             ErrorEvent { |             ErrorEvent { | ||||||
|                 event: EventType::UserFailedLogIn2fa |                 event: EventType::UserFailedLogIn2fa | ||||||
|             } |             } | ||||||
|         ), |         ) | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if !crypto::ct_eq(issued_token, token) { |     if !crypto::ct_eq(issued_token, token) { | ||||||
|   | |||||||
| @@ -85,9 +85,8 @@ async fn recover(data: Json<RecoverTwoFactor>, client_headers: ClientHeaders, mu | |||||||
|     use crate::db::models::User; |     use crate::db::models::User; | ||||||
|  |  | ||||||
|     // Get the user |     // Get the user | ||||||
|     let mut user = match User::find_by_mail(&data.email, &mut conn).await { |     let Some(mut user) = User::find_by_mail(&data.email, &mut conn).await else { | ||||||
|         Some(user) => user, |         err!("Username or password is incorrect. Try again.") | ||||||
|         None => err!("Username or password is incorrect. Try again."), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Check password |     // Check password | ||||||
|   | |||||||
| @@ -309,17 +309,16 @@ async fn delete_webauthn(data: Json<DeleteU2FData>, headers: Headers, mut conn: | |||||||
|         err!("Invalid password"); |         err!("Invalid password"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let mut tf = |     let Some(mut tf) = | ||||||
|         match TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &mut conn).await { |         TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &mut conn).await | ||||||
|             Some(tf) => tf, |     else { | ||||||
|             None => err!("Webauthn data not found!"), |         err!("Webauthn data not found!") | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     let mut data: Vec<WebauthnRegistration> = serde_json::from_str(&tf.data)?; |     let mut data: Vec<WebauthnRegistration> = serde_json::from_str(&tf.data)?; | ||||||
|  |  | ||||||
|     let item_pos = match data.iter().position(|r| r.id == id) { |     let Some(item_pos) = data.iter().position(|r| r.id == id) else { | ||||||
|         Some(p) => p, |         err!("Webauthn entry not found") | ||||||
|         None => err!("Webauthn entry not found"), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let removed_item = data.remove(item_pos); |     let removed_item = data.remove(item_pos); | ||||||
|   | |||||||
| @@ -157,9 +157,8 @@ async fn _password_login( | |||||||
|  |  | ||||||
|     // Get the user |     // Get the user | ||||||
|     let username = data.username.as_ref().unwrap().trim(); |     let username = data.username.as_ref().unwrap().trim(); | ||||||
|     let mut user = match User::find_by_mail(username, conn).await { |     let Some(mut user) = User::find_by_mail(username, conn).await else { | ||||||
|         Some(user) => user, |         err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)) | ||||||
|         None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Set the user_uuid here to be passed back used for event logging. |     // Set the user_uuid here to be passed back used for event logging. | ||||||
| @@ -180,7 +179,8 @@ async fn _password_login( | |||||||
|  |  | ||||||
|     // If we get an auth request, we don't check the user's password, but the access code of the auth request |     // If we get an auth request, we don't check the user's password, but the access code of the auth request | ||||||
|     if let Some(ref auth_request_uuid) = data.auth_request { |     if let Some(ref auth_request_uuid) = data.auth_request { | ||||||
|         let Some(auth_request) = AuthRequest::find_by_uuid(auth_request_uuid.as_str(), conn).await else { |         let Some(auth_request) = AuthRequest::find_by_uuid_and_user(auth_request_uuid.as_str(), &user.uuid, conn).await | ||||||
|  |         else { | ||||||
|             err!( |             err!( | ||||||
|                 "Auth request not found. Try again.", |                 "Auth request not found. Try again.", | ||||||
|                 format!("IP: {}. Username: {}.", ip.ip, username), |                 format!("IP: {}. Username: {}.", ip.ip, username), | ||||||
| @@ -382,13 +382,11 @@ async fn _user_api_key_login( | |||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     // Get the user via the client_id |     // Get the user via the client_id | ||||||
|     let client_id = data.client_id.as_ref().unwrap(); |     let client_id = data.client_id.as_ref().unwrap(); | ||||||
|     let client_user_uuid = match client_id.strip_prefix("user.") { |     let Some(client_user_uuid) = client_id.strip_prefix("user.") else { | ||||||
|         Some(uuid) => uuid, |         err!("Malformed client_id", format!("IP: {}.", ip.ip)) | ||||||
|         None => err!("Malformed client_id", format!("IP: {}.", ip.ip)), |  | ||||||
|     }; |     }; | ||||||
|     let user = match User::find_by_uuid(client_user_uuid, conn).await { |     let Some(user) = User::find_by_uuid(client_user_uuid, conn).await else { | ||||||
|         Some(user) => user, |         err!("Invalid client_id", format!("IP: {}.", ip.ip)) | ||||||
|         None => err!("Invalid client_id", format!("IP: {}.", ip.ip)), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Set the user_uuid here to be passed back used for event logging. |     // Set the user_uuid here to be passed back used for event logging. | ||||||
| @@ -471,13 +469,11 @@ async fn _user_api_key_login( | |||||||
| async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &ClientIp) -> JsonResult { | async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &ClientIp) -> JsonResult { | ||||||
|     // Get the org via the client_id |     // Get the org via the client_id | ||||||
|     let client_id = data.client_id.as_ref().unwrap(); |     let client_id = data.client_id.as_ref().unwrap(); | ||||||
|     let org_uuid = match client_id.strip_prefix("organization.") { |     let Some(org_uuid) = client_id.strip_prefix("organization.") else { | ||||||
|         Some(uuid) => uuid, |         err!("Malformed client_id", format!("IP: {}.", ip.ip)) | ||||||
|         None => err!("Malformed client_id", format!("IP: {}.", ip.ip)), |  | ||||||
|     }; |     }; | ||||||
|     let org_api_key = match OrganizationApiKey::find_by_org_uuid(org_uuid, conn).await { |     let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(org_uuid, conn).await else { | ||||||
|         Some(org_api_key) => org_api_key, |         err!("Invalid client_id", format!("IP: {}.", ip.ip)) | ||||||
|         None => err!("Invalid client_id", format!("IP: {}.", ip.ip)), |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Check API key. |     // Check API key. | ||||||
| @@ -676,9 +672,8 @@ async fn _json_err_twofactor( | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             Some(tf_type @ TwoFactorType::YubiKey) => { |             Some(tf_type @ TwoFactorType::YubiKey) => { | ||||||
|                 let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await { |                 let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await else { | ||||||
|                     Some(tf) => tf, |                     err!("No YubiKey devices registered") | ||||||
|                     None => err!("No YubiKey devices registered"), |  | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let yubikey_metadata: yubikey::YubikeyMetadata = serde_json::from_str(&twofactor.data)?; |                 let yubikey_metadata: yubikey::YubikeyMetadata = serde_json::from_str(&twofactor.data)?; | ||||||
| @@ -689,9 +684,8 @@ async fn _json_err_twofactor( | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             Some(tf_type @ TwoFactorType::Email) => { |             Some(tf_type @ TwoFactorType::Email) => { | ||||||
|                 let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await { |                 let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await else { | ||||||
|                     Some(tf) => tf, |                     err!("No twofactor email registered") | ||||||
|                     None => err!("No twofactor email registered"), |  | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 // Send email immediately if email is the only 2FA option |                 // Send email immediately if email is the only 2FA option | ||||||
|   | |||||||
| @@ -155,12 +155,9 @@ pub async fn push_cipher_update( | |||||||
|     if cipher.organization_uuid.is_some() { |     if cipher.organization_uuid.is_some() { | ||||||
|         return; |         return; | ||||||
|     }; |     }; | ||||||
|     let user_uuid = match &cipher.user_uuid { |     let Some(user_uuid) = &cipher.user_uuid else { | ||||||
|         Some(c) => c, |         debug!("Cipher has no uuid"); | ||||||
|         None => { |         return; | ||||||
|             debug!("Cipher has no uuid"); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if Device::check_user_has_push_device(user_uuid, conn).await { |     if Device::check_user_has_push_device(user_uuid, conn).await { | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/auth.rs
									
									
									
									
									
								
							| @@ -471,9 +471,8 @@ impl<'r> FromRequest<'r> for Headers { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         // Check JWT token is valid and get device and user from it |         // Check JWT token is valid and get device and user from it | ||||||
|         let claims = match decode_login(access_token) { |         let Ok(claims) = decode_login(access_token) else { | ||||||
|             Ok(claims) => claims, |             err_handler!("Invalid claim") | ||||||
|             Err(_) => err_handler!("Invalid claim"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let device_uuid = claims.device; |         let device_uuid = claims.device; | ||||||
| @@ -484,23 +483,20 @@ impl<'r> FromRequest<'r> for Headers { | |||||||
|             _ => err_handler!("Error getting DB"), |             _ => err_handler!("Error getting DB"), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let device = match Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &mut conn).await { |         let Some(device) = Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &mut conn).await else { | ||||||
|             Some(device) => device, |             err_handler!("Invalid device id") | ||||||
|             None => err_handler!("Invalid device id"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let user = match User::find_by_uuid(&user_uuid, &mut conn).await { |         let Some(user) = User::find_by_uuid(&user_uuid, &mut conn).await else { | ||||||
|             Some(user) => user, |             err_handler!("Device has no user associated") | ||||||
|             None => err_handler!("Device has no user associated"), |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if user.security_stamp != claims.sstamp { |         if user.security_stamp != claims.sstamp { | ||||||
|             if let Some(stamp_exception) = |             if let Some(stamp_exception) = | ||||||
|                 user.stamp_exception.as_deref().and_then(|s| serde_json::from_str::<UserStampException>(s).ok()) |                 user.stamp_exception.as_deref().and_then(|s| serde_json::from_str::<UserStampException>(s).ok()) | ||||||
|             { |             { | ||||||
|                 let current_route = match request.route().and_then(|r| r.name.as_deref()) { |                 let Some(current_route) = request.route().and_then(|r| r.name.as_deref()) else { | ||||||
|                     Some(name) => name, |                     err_handler!("Error getting current route for stamp exception") | ||||||
|                     _ => err_handler!("Error getting current route for stamp exception"), |  | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 // Check if the stamp exception has expired first. |                 // Check if the stamp exception has expired first. | ||||||
|   | |||||||
| @@ -111,6 +111,17 @@ impl AuthRequest { | |||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> { | ||||||
|  |         db_run! {conn: { | ||||||
|  |             auth_requests::table | ||||||
|  |                 .filter(auth_requests::uuid.eq(uuid)) | ||||||
|  |                 .filter(auth_requests::user_uuid.eq(user_uuid)) | ||||||
|  |                 .first::<AuthRequestDb>(conn) | ||||||
|  |                 .ok() | ||||||
|  |                 .from_db() | ||||||
|  |         }} | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> { |     pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> { | ||||||
|         db_run! {conn: { |         db_run! {conn: { | ||||||
|             auth_requests::table |             auth_requests::table | ||||||
|   | |||||||
| @@ -120,10 +120,11 @@ impl Folder { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> { |     pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> { | ||||||
|         db_run! { conn: { |         db_run! { conn: { | ||||||
|             folders::table |             folders::table | ||||||
|                 .filter(folders::uuid.eq(uuid)) |                 .filter(folders::uuid.eq(uuid)) | ||||||
|  |                 .filter(folders::user_uuid.eq(user_uuid)) | ||||||
|                 .first::<FolderDb>(conn) |                 .first::<FolderDb>(conn) | ||||||
|                 .ok() |                 .ok() | ||||||
|                 .from_db() |                 .from_db() | ||||||
|   | |||||||
| @@ -191,10 +191,11 @@ impl Group { | |||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> { |     pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> { | ||||||
|         db_run! { conn: { |         db_run! { conn: { | ||||||
|             groups::table |             groups::table | ||||||
|                 .filter(groups::uuid.eq(uuid)) |                 .filter(groups::uuid.eq(uuid)) | ||||||
|  |                 .filter(groups::organizations_uuid.eq(org_uuid)) | ||||||
|                 .first::<GroupDb>(conn) |                 .first::<GroupDb>(conn) | ||||||
|                 .ok() |                 .ok() | ||||||
|                 .from_db() |                 .from_db() | ||||||
|   | |||||||
| @@ -142,16 +142,6 @@ impl OrgPolicy { | |||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> { |  | ||||||
|         db_run! { conn: { |  | ||||||
|             org_policies::table |  | ||||||
|                 .filter(org_policies::uuid.eq(uuid)) |  | ||||||
|                 .first::<OrgPolicyDb>(conn) |  | ||||||
|                 .ok() |  | ||||||
|                 .from_db() |  | ||||||
|         }} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> { |     pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> { | ||||||
|         db_run! { conn: { |         db_run! { conn: { | ||||||
|             org_policies::table |             org_policies::table | ||||||
|   | |||||||
| @@ -268,9 +268,8 @@ impl Send { | |||||||
|         use data_encoding::BASE64URL_NOPAD; |         use data_encoding::BASE64URL_NOPAD; | ||||||
|         use uuid::Uuid; |         use uuid::Uuid; | ||||||
|  |  | ||||||
|         let uuid_vec = match BASE64URL_NOPAD.decode(access_id.as_bytes()) { |         let Ok(uuid_vec) = BASE64URL_NOPAD.decode(access_id.as_bytes()) else { | ||||||
|             Ok(v) => v, |             return None; | ||||||
|             Err(_) => return None, |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let uuid = match Uuid::from_slice(&uuid_vec) { |         let uuid = match Uuid::from_slice(&uuid_vec) { | ||||||
| @@ -291,6 +290,17 @@ impl Send { | |||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> { | ||||||
|  |         db_run! {conn: { | ||||||
|  |             sends::table | ||||||
|  |                 .filter(sends::uuid.eq(uuid)) | ||||||
|  |                 .filter(sends::user_uuid.eq(user_uuid)) | ||||||
|  |                 .first::<SendDb>(conn) | ||||||
|  |                 .ok() | ||||||
|  |                 .from_db() | ||||||
|  |         }} | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> { |     pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> { | ||||||
|         db_run! {conn: { |         db_run! {conn: { | ||||||
|             sends::table |             sends::table | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/mail.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/mail.rs
									
									
									
									
									
								
							| @@ -286,9 +286,8 @@ pub async fn send_invite( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let query_string = match query.query() { |     let Some(query_string) = query.query() else { | ||||||
|         None => err!("Failed to build invite URL query parameters"), |         err!("Failed to build invite URL query parameters") | ||||||
|         Some(query) => query, |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let (subject, body_html, body_text) = get_text( |     let (subject, body_html, body_text) = get_text( | ||||||
| @@ -330,9 +329,8 @@ pub async fn send_emergency_access_invite( | |||||||
|             .append_pair("token", &encode_jwt(&claims)); |             .append_pair("token", &encode_jwt(&claims)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let query_string = match query.query() { |     let Some(query_string) = query.query() else { | ||||||
|         None => err!("Failed to build emergency invite URL query parameters"), |         err!("Failed to build emergency invite URL query parameters") | ||||||
|         Some(query) => query, |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let (subject, body_html, body_text) = get_text( |     let (subject, body_html, body_text) = get_text( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user