@@ -18,6 +18,7 @@ use ulid::Ulid;
1818use url:: Url ;
1919use zeroize:: Zeroizing ;
2020
21+ use super :: verify_password_if_needed;
2122use crate :: graphql:: {
2223 UserId ,
2324 model:: { NodeType , User } ,
@@ -383,6 +384,61 @@ impl ResendRecoveryEmailPayload {
383384 }
384385}
385386
387+ /// The input for the `deactivateUser` mutation.
388+ #[ derive( InputObject ) ]
389+ pub struct DeactivateUserInput {
390+ /// Whether to ask the homeserver to GDPR-erase the user
391+ ///
392+ /// This is equivalent to the `erase` parameter on the
393+ /// `/_matrix/client/v3/account/deactivate` C-S API, which is
394+ /// implementation-specific.
395+ ///
396+ /// What Synapse does is documented here:
397+ /// <https://element-hq.github.io/synapse/latest/admin_api/user_admin_api.html#deactivate-account>
398+ hs_erase : bool ,
399+
400+ /// The password of the user to deactivate.
401+ password : Option < String > ,
402+ }
403+
404+ /// The payload for the `deactivateUser` mutation.
405+ #[ derive( Description ) ]
406+ pub enum DeactivateUserPayload {
407+ /// The user was deactivated.
408+ Deactivated ( mas_data_model:: User ) ,
409+
410+ /// The password was wrong or missing.
411+ IncorrectPassword ,
412+ }
413+
414+ /// The status of the `deactivateUser` mutation.
415+ #[ derive( Enum , Copy , Clone , Eq , PartialEq ) ]
416+ pub enum DeactivateUserStatus {
417+ /// The user was deactivated.
418+ Deactivated ,
419+
420+ /// The password was wrong.
421+ IncorrectPassword ,
422+ }
423+
424+ #[ Object ( use_type_description) ]
425+ impl DeactivateUserPayload {
426+ /// Status of the operation
427+ async fn status ( & self ) -> DeactivateUserStatus {
428+ match self {
429+ Self :: Deactivated ( _) => DeactivateUserStatus :: Deactivated ,
430+ Self :: IncorrectPassword => DeactivateUserStatus :: IncorrectPassword ,
431+ }
432+ }
433+
434+ async fn user ( & self ) -> Option < User > {
435+ match self {
436+ Self :: Deactivated ( user) => Some ( User ( user. clone ( ) ) ) ,
437+ Self :: IncorrectPassword => None ,
438+ }
439+ }
440+ }
441+
386442fn valid_username_character ( c : char ) -> bool {
387443 c. is_ascii_lowercase ( )
388444 || c. is_ascii_digit ( )
@@ -868,4 +924,64 @@ impl UserMutations {
868924 recovery_session_id : recovery_session. id ,
869925 } )
870926 }
927+
928+ /// Deactivate the current user account
929+ ///
930+ /// If the user has a password, it *must* be supplied in the `password`
931+ /// field.
932+ async fn deactivate_user (
933+ & self ,
934+ ctx : & Context < ' _ > ,
935+ input : DeactivateUserInput ,
936+ ) -> Result < DeactivateUserPayload , async_graphql:: Error > {
937+ let state = ctx. state ( ) ;
938+ let mut rng = state. rng ( ) ;
939+ let clock = state. clock ( ) ;
940+ let requester = ctx. requester ( ) ;
941+ let site_config = state. site_config ( ) ;
942+
943+ // Only allow calling this if the requester is a browser session
944+ let Some ( browser_session) = requester. browser_session ( ) else {
945+ return Err ( async_graphql:: Error :: new ( "Unauthorized" ) ) ;
946+ } ;
947+
948+ if !site_config. account_deactivation_allowed {
949+ return Err ( async_graphql:: Error :: new (
950+ "Account deactivation is not allowed on this server" ,
951+ ) ) ;
952+ }
953+
954+ let mut repo = state. repository ( ) . await ?;
955+ if !verify_password_if_needed (
956+ requester,
957+ site_config,
958+ & state. password_manager ( ) ,
959+ input. password ,
960+ & browser_session. user ,
961+ & mut repo,
962+ )
963+ . await ?
964+ {
965+ return Ok ( DeactivateUserPayload :: IncorrectPassword ) ;
966+ }
967+
968+ // Deactivate the user right away
969+ let user = repo
970+ . user ( )
971+ . deactivate ( & state. clock ( ) , browser_session. user . clone ( ) )
972+ . await ?;
973+
974+ // and then schedule a job to deactivate it fully
975+ repo. queue_job ( )
976+ . schedule_job (
977+ & mut rng,
978+ & clock,
979+ DeactivateUserJob :: new ( & user, input. hs_erase ) ,
980+ )
981+ . await ?;
982+
983+ repo. save ( ) . await ?;
984+
985+ Ok ( DeactivateUserPayload :: Deactivated ( user) )
986+ }
871987}
0 commit comments