@ -5,7 +5,10 @@ use rocket::Route;
use serde_json ::Value ;
use crate ::{
api ::{ core ::log_user_event , JsonResult , JsonUpcase , NumberOrString , PasswordOrOtpData } ,
api ::{
core ::{ log_event , log_user_event } ,
EmptyResult , JsonResult , JsonUpcase , NumberOrString , PasswordOrOtpData ,
} ,
auth ::{ ClientHeaders , Headers } ,
crypto ,
db ::{ models ::* , DbConn , DbPool } ,
@ -96,6 +99,7 @@ async fn recover(data: JsonUpcase<RecoverTwoFactor>, client_headers: ClientHeade
// Remove all twofactors from the user
TwoFactor ::delete_all_by_user ( & user . uuid , & mut conn ) . await ? ;
enforce_2fa_policy ( & user , user . uuid . clone ( ) , client_headers . device_type , & client_headers . ip . ip , & mut conn ) . await ? ;
log_user_event (
EventType ::UserRecovered2fa as i32 ,
@ -149,22 +153,8 @@ async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Head
. await ;
}
let twofactor_disabled = TwoFactor ::find_by_user ( & user . uuid , & mut conn ) . await . is_empty ( ) ;
if twofactor_disabled {
for user_org in
UserOrganization ::find_by_user_and_policy ( & user . uuid , OrgPolicyType ::TwoFactorAuthentication , & mut conn )
. await
. into_iter ( )
{
if user_org . atype < UserOrgType ::Admin {
if CONFIG . mail_enabled ( ) {
let org = Organization ::find_by_uuid ( & user_org . org_uuid , & mut conn ) . await . unwrap ( ) ;
mail ::send_2fa_removed_from_org ( & user . email , & org . name ) . await ? ;
}
user_org . delete ( & mut conn ) . await ? ;
}
}
if TwoFactor ::find_by_user ( & user . uuid , & mut conn ) . await . is_empty ( ) {
enforce_2fa_policy ( & user , user . uuid . clone ( ) , headers . device . atype , & headers . ip . ip , & mut conn ) . await ? ;
}
Ok ( Json ( json ! ( {
@ -179,6 +169,78 @@ async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers:
disable_twofactor ( data , headers , conn ) . await
}
pub async fn enforce_2fa_policy (
user : & User ,
act_uuid : String ,
device_type : i32 ,
ip : & std ::net ::IpAddr ,
conn : & mut DbConn ,
) -> EmptyResult {
for member in UserOrganization ::find_by_user_and_policy ( & user . uuid , OrgPolicyType ::TwoFactorAuthentication , conn )
. await
. into_iter ( )
{
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
if member . atype < UserOrgType ::Admin {
if CONFIG . mail_enabled ( ) {
let org = Organization ::find_by_uuid ( & member . org_uuid , conn ) . await . unwrap ( ) ;
mail ::send_2fa_removed_from_org ( & user . email , & org . name ) . await ? ;
}
let mut member = member ;
member . revoke ( ) ;
member . save ( conn ) . await ? ;
log_event (
EventType ::OrganizationUserRevoked as i32 ,
& member . uuid ,
& member . org_uuid ,
act_uuid . clone ( ) ,
device_type ,
ip ,
conn ,
)
. await ;
}
}
Ok ( ( ) )
}
pub async fn enforce_2fa_policy_for_org (
org_uuid : & str ,
act_uuid : String ,
device_type : i32 ,
ip : & std ::net ::IpAddr ,
conn : & mut DbConn ,
) -> EmptyResult {
let org = Organization ::find_by_uuid ( org_uuid , conn ) . await . unwrap ( ) ;
for member in UserOrganization ::find_confirmed_by_org ( org_uuid , conn ) . await . into_iter ( ) {
// Don't enforce the policy for Admins and Owners.
if member . atype < UserOrgType ::Admin & & TwoFactor ::find_by_user ( & member . user_uuid , conn ) . await . is_empty ( ) {
if CONFIG . mail_enabled ( ) {
let user = User ::find_by_uuid ( & member . user_uuid , conn ) . await . unwrap ( ) ;
mail ::send_2fa_removed_from_org ( & user . email , & org . name ) . await ? ;
}
let mut member = member ;
member . revoke ( ) ;
member . save ( conn ) . await ? ;
log_event (
EventType ::OrganizationUserRevoked as i32 ,
& member . uuid ,
org_uuid ,
act_uuid . clone ( ) ,
device_type ,
ip ,
conn ,
)
. await ;
}
}
Ok ( ( ) )
}
pub async fn send_incomplete_2fa_notifications ( pool : DbPool ) {
debug ! ( "Sending notifications for incomplete 2FA logins" ) ;