@ -61,6 +61,10 @@ pub fn routes() -> Vec<Route> {
import ,
import ,
post_org_keys ,
post_org_keys ,
bulk_public_keys ,
bulk_public_keys ,
deactivate_organization_user ,
bulk_deactivate_organization_user ,
activate_organization_user ,
bulk_activate_organization_user
]
]
}
}
@ -107,7 +111,7 @@ async fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn:
if ! CONFIG . is_org_creation_allowed ( & headers . user . email ) {
if ! CONFIG . is_org_creation_allowed ( & headers . user . email ) {
err ! ( "User not allowed to create organizations" )
err ! ( "User not allowed to create organizations" )
}
}
if OrgPolicy ::is_applicable_to_user ( & headers . user . uuid , OrgPolicyType ::SingleOrg , & conn ) . await {
if OrgPolicy ::is_applicable_to_user ( & headers . user . uuid , OrgPolicyType ::SingleOrg , None , & conn ) . await {
err ! (
err ! (
"You may not create an organization. You belong to an organization which has a policy that prohibits you from being a member of any other organization."
"You may not create an organization. You belong to an organization which has a policy that prohibits you from being a member of any other organization."
)
)
@ -172,13 +176,10 @@ async fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> E
match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , & conn ) . await {
match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , & conn ) . await {
None = > err ! ( "User not part of organization" ) ,
None = > err ! ( "User not part of organization" ) ,
Some ( user_org ) = > {
Some ( user_org ) = > {
if user_org . atype = = UserOrgType ::Owner {
if user_org . atype = = UserOrgType ::Owner
let num_owners =
& & UserOrganization ::count_confirmed_by_org_and_type ( & org_id , UserOrgType ::Owner , & conn ) . await < = 1
UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::Owner as i32 , & conn ) . await . len ( ) ;
{
err ! ( "The last owner can't leave" )
if num_owners < = 1 {
err ! ( "The last owner can't leave" )
}
}
}
user_org . delete ( & conn ) . await
user_org . delete ( & conn ) . await
@ -749,17 +750,16 @@ struct AcceptData {
Token : String ,
Token : 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 (
async fn accept_invite (
_ org_id: String ,
org_id: String ,
_org_user_id : String ,
_org_user_id : String ,
data : JsonUpcase < AcceptData > ,
data : JsonUpcase < AcceptData > ,
conn : DbConn ,
conn : DbConn ,
) -> EmptyResult {
) -> 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 ( ) . data ;
let data : AcceptData = data . into_inner ( ) . data ;
let token = & data . Token ;
let claims = decode_invite ( & data . Token ) ? ;
let claims = decode_invite ( token ) ? ;
match User ::find_by_mail ( & claims . email , & conn ) . await {
match User ::find_by_mail ( & claims . email , & conn ) . await {
Some ( _ ) = > {
Some ( _ ) = > {
@ -775,46 +775,20 @@ async fn accept_invite(
err ! ( "User already accepted the invitation" )
err ! ( "User already accepted the invitation" )
}
}
let user_twofactor_disabled = TwoFactor ::find_by_user ( & user_org . user_uuid , & conn ) . await . is_empty ( ) ;
// This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
// It returns different error messages per function.
let policy = OrgPolicyType ::TwoFactorAuthentication as i32 ;
if user_org . atype < UserOrgType ::Admin {
let org_twofactor_policy_enabled =
match OrgPolicy ::is_user_allowed ( & user_org . user_uuid , & org_id , false , & conn ) . await {
match OrgPolicy ::find_by_org_and_type ( & user_org . org_uuid , policy , & conn ) . await {
Ok ( _ ) = > { }
Some ( p ) = > p . enabled ,
Err ( OrgPolicyErr ::TwoFactorMissing ) = > {
None = > false ,
err ! ( "You cannot join this organization until you enable two-step login on your user account" ) ;
} ;
}
Err ( OrgPolicyErr ::SingleOrgEnforced ) = > {
if org_twofactor_policy_enabled & & user_twofactor_disabled {
err ! ( "You cannot join this organization because you are a member of an organization which forbids it" ) ;
err ! ( "You cannot join this organization until you enable two-step login on your user account." )
}
}
// Enforce Single Organization Policy of organization user is trying to join
let single_org_policy_enabled =
match OrgPolicy ::find_by_org_and_type ( & user_org . org_uuid , OrgPolicyType ::SingleOrg as i32 , & conn )
. await
{
Some ( p ) = > p . enabled ,
None = > false ,
} ;
if single_org_policy_enabled & & user_org . atype < UserOrgType ::Admin {
let is_member_of_another_org = UserOrganization ::find_any_state_by_user ( & user_org . user_uuid , & conn )
. await
. into_iter ( )
. filter ( | uo | uo . org_uuid ! = user_org . org_uuid )
. count ( )
> 1 ;
if is_member_of_another_org {
err ! ( "You may not join this organization until you leave or remove all other organizations." )
}
}
}
}
// Enforce Single Organization Policy of other organizations user is a member of
if OrgPolicy ::is_applicable_to_user ( & user_org . user_uuid , OrgPolicyType ::SingleOrg , & conn ) . await {
err ! (
"You cannot join this organization because you are a member of an organization which forbids it"
)
}
user_org . status = UserOrgStatus ::Accepted as i32 ;
user_org . status = UserOrgStatus ::Accepted as i32 ;
user_org . save ( & conn ) . await ? ;
user_org . save ( & conn ) . await ? ;
}
}
@ -918,6 +892,20 @@ async fn _confirm_invite(
err ! ( "User in invalid state" )
err ! ( "User in invalid state" )
}
}
// This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
// It returns different error messages per function.
if user_to_confirm . atype < UserOrgType ::Admin {
match OrgPolicy ::is_user_allowed ( & user_to_confirm . user_uuid , org_id , true , conn ) . await {
Ok ( _ ) = > { }
Err ( OrgPolicyErr ::TwoFactorMissing ) = > {
err ! ( "You cannot confirm this user because it has no two-step login method activated" ) ;
}
Err ( OrgPolicyErr ::SingleOrgEnforced ) = > {
err ! ( "You cannot confirm this user because it is a member of an organization which forbids it" ) ;
}
}
}
user_to_confirm . status = UserOrgStatus ::Confirmed as i32 ;
user_to_confirm . status = UserOrgStatus ::Confirmed as i32 ;
user_to_confirm . akey = key . to_string ( ) ;
user_to_confirm . akey = key . to_string ( ) ;
@ -997,14 +985,26 @@ async fn edit_user(
}
}
if user_to_edit . atype = = UserOrgType ::Owner & & new_type ! = UserOrgType ::Owner {
if user_to_edit . atype = = UserOrgType ::Owner & & new_type ! = UserOrgType ::Owner {
// Removing owner permmission, check that there are at least another owner
// Removing owner permmission, check that there is at least one other confirmed owner
let num_owners = UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::Owner as i32 , & conn ) . await . len ( ) ;
if UserOrganization ::count_confirmed_by_org_and_type ( & org_id , UserOrgType ::Owner , & conn ) . await < = 1 {
if num_owners < = 1 {
err ! ( "Can't delete the last owner" )
err ! ( "Can't delete the last owner" )
}
}
}
}
// This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
// It returns different error messages per function.
if new_type < UserOrgType ::Admin {
match OrgPolicy ::is_user_allowed ( & user_to_edit . user_uuid , & org_id , true , & conn ) . await {
Ok ( _ ) = > { }
Err ( OrgPolicyErr ::TwoFactorMissing ) = > {
err ! ( "You cannot modify this user to this type because it has no two-step login method activated" ) ;
}
Err ( OrgPolicyErr ::SingleOrgEnforced ) = > {
err ! ( "You cannot modify this user to this type because it is a member of an organization which forbids it" ) ;
}
}
}
user_to_edit . access_all = data . AccessAll ;
user_to_edit . access_all = data . AccessAll ;
user_to_edit . atype = new_type as i32 ;
user_to_edit . atype = new_type as i32 ;
@ -1083,10 +1083,8 @@ async fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, c
}
}
if user_to_delete . atype = = UserOrgType ::Owner {
if user_to_delete . atype = = UserOrgType ::Owner {
// Removing owner, check that there are at least another owner
// Removing owner, check that there is at least one other confirmed owner
let num_owners = UserOrganization ::find_by_org_and_type ( org_id , UserOrgType ::Owner as i32 , conn ) . await . len ( ) ;
if UserOrganization ::count_confirmed_by_org_and_type ( org_id , UserOrgType ::Owner , conn ) . await < = 1 {
if num_owners < = 1 {
err ! ( "Can't delete the last owner" )
err ! ( "Can't delete the last owner" )
}
}
}
}
@ -1255,7 +1253,7 @@ async fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn:
None = > 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 , & conn ) . await {
let policy = match OrgPolicy ::find_by_org_and_type ( & org_id , pol_type _enum , & conn ) . await {
Some ( p ) = > p ,
Some ( p ) = > p ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , "{}" . to_string ( ) ) ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , "{}" . to_string ( ) ) ,
} ;
} ;
@ -1283,15 +1281,16 @@ async fn put_policy(
let pol_type_enum = match OrgPolicyType ::from_i32 ( pol_type ) {
let pol_type_enum = match OrgPolicyType ::from_i32 ( pol_type ) {
Some ( pt ) = > pt ,
Some ( pt ) = > pt ,
None = > err ! ( "Invalid policy type") ,
None = > err ! ( "Invalid or unsupported policy type") ,
} ;
} ;
// If enabling the TwoFactorAuthentication policy, remove this org's members that do have 2FA
// When enabling the TwoFactorAuthentication policy, remove this org's members that do have 2FA
if pol_type_enum = = OrgPolicyType ::TwoFactorAuthentication & & data . enabled {
if pol_type_enum = = OrgPolicyType ::TwoFactorAuthentication & & data . enabled {
for member in UserOrganization ::find_by_org ( & org_id , & conn ) . await . into_iter ( ) {
for member in UserOrganization ::find_by_org ( & org_id , & conn ) . await . into_iter ( ) {
let user_twofactor_disabled = TwoFactor ::find_by_user ( & member . user_uuid , & conn ) . await . is_empty ( ) ;
let user_twofactor_disabled = TwoFactor ::find_by_user ( & member . user_uuid , & conn ) . await . is_empty ( ) ;
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
// Invited users still need to accept the invite and will get an error when they try to accept the invite.
if user_twofactor_disabled
if user_twofactor_disabled
& & member . atype < UserOrgType ::Admin
& & member . atype < UserOrgType ::Admin
& & member . status ! = UserOrgStatus ::Invited as i32
& & member . status ! = UserOrgStatus ::Invited as i32
@ -1307,33 +1306,29 @@ async fn put_policy(
}
}
}
}
// If enabling the SingleOrg policy, remove this org's members that are members of other orgs
// When enabling the SingleOrg policy, remove this org's members that are members of other orgs
if pol_type_enum = = OrgPolicyType ::SingleOrg & & data . enabled {
if pol_type_enum = = OrgPolicyType ::SingleOrg & & data . enabled {
for member in UserOrganization ::find_by_org ( & org_id , & conn ) . await . into_iter ( ) {
for member in UserOrganization ::find_by_org ( & org_id , & conn ) . await . into_iter ( ) {
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
if member . atype < UserOrgType ::Admin & & member . status ! = UserOrgStatus ::Invited as i32 {
// Exclude invited and revoked users when checking for this policy.
let is_member_of_another_org = UserOrganization ::find_any_state_by_user ( & member . user_uuid , & conn )
// Those users will not be allowed to accept or be activated because of the policy checks done there.
. await
// We check if the count is larger then 1, because it includes this organization also.
. into_iter ( )
if member . atype < UserOrgType ::Admin
// Other UserOrganization's where they have accepted being a member of
& & member . status ! = UserOrgStatus ::Invited as i32
. filter ( | uo | uo . uuid ! = member . uuid & & uo . status ! = UserOrgStatus ::Invited as i32 )
& & UserOrganization ::count_accepted_and_confirmed_by_user ( & member . user_uuid , & conn ) . await > 1
. count ( )
{
> 1 ;
if CONFIG . mail_enabled ( ) {
let org = Organization ::find_by_uuid ( & member . org_uuid , & conn ) . await . unwrap ( ) ;
if is_member_of_another_org {
let user = User ::find_by_uuid ( & member . user_uuid , & conn ) . await . unwrap ( ) ;
if CONFIG . mail_enabled ( ) {
let org = Organization ::find_by_uuid ( & member . org_uuid , & conn ) . await . unwrap ( ) ;
mail ::send_single_org_removed_from_org ( & user . email , & org . name ) . await ? ;
let user = User ::find_by_uuid ( & member . user_uuid , & conn ) . await . unwrap ( ) ;
mail ::send_single_org_removed_from_org ( & user . email , & org . name ) . await ? ;
}
member . delete ( & conn ) . await ? ;
}
}
member . delete ( & conn ) . await ? ;
}
}
}
}
}
}
let mut policy = match OrgPolicy ::find_by_org_and_type ( & org_id , pol_type , & conn ) . await {
let mut policy = match OrgPolicy ::find_by_org_and_type ( & org_id , pol_type _enum , & conn ) . await {
Some ( p ) = > p ,
Some ( p ) = > p ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , "{}" . to_string ( ) ) ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , "{}" . to_string ( ) ) ,
} ;
} ;
@ -1473,7 +1468,7 @@ async fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Header
// If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
// If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
if data . OverwriteExisting {
if data . OverwriteExisting {
for user_org in UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::User as i32 , & conn ) . await {
for user_org in UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::User , & conn ) . await {
if let Some ( user_email ) = User ::find_by_uuid ( & user_org . user_uuid , & conn ) . await . map ( | u | u . email ) {
if let Some ( user_email ) = User ::find_by_uuid ( & user_org . user_uuid , & conn ) . await . map ( | u | u . email ) {
if ! data . Users . iter ( ) . any ( | u | u . Email = = user_email ) {
if ! data . Users . iter ( ) . any ( | u | u . Email = = user_email ) {
user_org . delete ( & conn ) . await ? ;
user_org . delete ( & conn ) . await ? ;
@ -1484,3 +1479,166 @@ async fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Header
Ok ( ( ) )
Ok ( ( ) )
}
}
#[ put( " /organizations/<org_id>/users/<org_user_id>/deactivate " ) ]
async fn deactivate_organization_user (
org_id : String ,
org_user_id : String ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
_deactivate_organization_user ( & org_id , & org_user_id , & headers , & conn ) . await
}
#[ put( " /organizations/<org_id>/users/deactivate " , data = " <data> " ) ]
async fn bulk_deactivate_organization_user (
org_id : String ,
data : JsonUpcase < Value > ,
headers : AdminHeaders ,
conn : DbConn ,
) -> Json < Value > {
let data = data . into_inner ( ) . data ;
let mut bulk_response = Vec ::new ( ) ;
match data [ "Ids" ] . as_array ( ) {
Some ( org_users ) = > {
for org_user_id in org_users {
let org_user_id = org_user_id . as_str ( ) . unwrap_or_default ( ) ;
let err_msg = match _deactivate_organization_user ( & org_id , org_user_id , & headers , & conn ) . await {
Ok ( _ ) = > String ::from ( "" ) ,
Err ( e ) = > format! ( "{:?}" , e ) ,
} ;
bulk_response . push ( json ! (
{
"Object" : "OrganizationUserBulkResponseModel" ,
"Id" : org_user_id ,
"Error" : err_msg
}
) ) ;
}
}
None = > error ! ( "No users to revoke" ) ,
}
Json ( json ! ( {
"Data" : bulk_response ,
"Object" : "list" ,
"ContinuationToken" : null
} ) )
}
async fn _deactivate_organization_user (
org_id : & str ,
org_user_id : & str ,
headers : & AdminHeaders ,
conn : & DbConn ,
) -> EmptyResult {
match UserOrganization ::find_by_uuid_and_org ( org_user_id , org_id , conn ) . await {
Some ( mut user_org ) if user_org . status > UserOrgStatus ::Revoked as i32 = > {
if user_org . user_uuid = = headers . user . uuid {
err ! ( "You cannot revoke yourself" )
}
if user_org . atype = = UserOrgType ::Owner & & headers . org_user_type ! = UserOrgType ::Owner {
err ! ( "Only owners can revoke other owners" )
}
if user_org . atype = = UserOrgType ::Owner
& & UserOrganization ::count_confirmed_by_org_and_type ( org_id , UserOrgType ::Owner , conn ) . await < = 1
{
err ! ( "Organization must have at least one confirmed owner" )
}
user_org . revoke ( ) ;
user_org . save ( conn ) . await ? ;
}
Some ( _ ) = > err ! ( "User is already revoked" ) ,
None = > err ! ( "User not found in organization" ) ,
}
Ok ( ( ) )
}
#[ put( " /organizations/<org_id>/users/<org_user_id>/activate " ) ]
async fn activate_organization_user (
org_id : String ,
org_user_id : String ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
_activate_organization_user ( & org_id , & org_user_id , & headers , & conn ) . await
}
#[ put( " /organizations/<org_id>/users/activate " , data = " <data> " ) ]
async fn bulk_activate_organization_user (
org_id : String ,
data : JsonUpcase < Value > ,
headers : AdminHeaders ,
conn : DbConn ,
) -> Json < Value > {
let data = data . into_inner ( ) . data ;
let mut bulk_response = Vec ::new ( ) ;
match data [ "Ids" ] . as_array ( ) {
Some ( org_users ) = > {
for org_user_id in org_users {
let org_user_id = org_user_id . as_str ( ) . unwrap_or_default ( ) ;
let err_msg = match _activate_organization_user ( & org_id , org_user_id , & headers , & conn ) . await {
Ok ( _ ) = > String ::from ( "" ) ,
Err ( e ) = > format! ( "{:?}" , e ) ,
} ;
bulk_response . push ( json ! (
{
"Object" : "OrganizationUserBulkResponseModel" ,
"Id" : org_user_id ,
"Error" : err_msg
}
) ) ;
}
}
None = > error ! ( "No users to restore" ) ,
}
Json ( json ! ( {
"Data" : bulk_response ,
"Object" : "list" ,
"ContinuationToken" : null
} ) )
}
async fn _activate_organization_user (
org_id : & str ,
org_user_id : & str ,
headers : & AdminHeaders ,
conn : & DbConn ,
) -> EmptyResult {
match UserOrganization ::find_by_uuid_and_org ( org_user_id , org_id , conn ) . await {
Some ( mut user_org ) if user_org . status < UserOrgStatus ::Accepted as i32 = > {
if user_org . user_uuid = = headers . user . uuid {
err ! ( "You cannot restore yourself" )
}
if user_org . atype = = UserOrgType ::Owner & & headers . org_user_type ! = UserOrgType ::Owner {
err ! ( "Only owners can restore other owners" )
}
// This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
// It returns different error messages per function.
if user_org . atype < UserOrgType ::Admin {
match OrgPolicy ::is_user_allowed ( & user_org . user_uuid , org_id , false , conn ) . await {
Ok ( _ ) = > { }
Err ( OrgPolicyErr ::TwoFactorMissing ) = > {
err ! ( "You cannot restore this user because it has no two-step login method activated" ) ;
}
Err ( OrgPolicyErr ::SingleOrgEnforced ) = > {
err ! ( "You cannot restore this user because it is a member of an organization which forbids it" ) ;
}
}
}
user_org . activate ( ) ;
user_org . save ( conn ) . await ? ;
}
Some ( _ ) = > err ! ( "User is already active" ) ,
None = > err ! ( "User not found in organization" ) ,
}
Ok ( ( ) )
}