@@ -18,6 +18,7 @@ use ulid::Ulid;
18
18
use url:: Url ;
19
19
use zeroize:: Zeroizing ;
20
20
21
+ use super :: verify_password_if_needed;
21
22
use crate :: graphql:: {
22
23
UserId ,
23
24
model:: { NodeType , User } ,
@@ -383,6 +384,61 @@ impl ResendRecoveryEmailPayload {
383
384
}
384
385
}
385
386
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
+
386
442
fn valid_username_character ( c : char ) -> bool {
387
443
c. is_ascii_lowercase ( )
388
444
|| c. is_ascii_digit ( )
@@ -868,4 +924,64 @@ impl UserMutations {
868
924
recovery_session_id : recovery_session. id ,
869
925
} )
870
926
}
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
+ }
871
987
}
0 commit comments