4444
4545% % commands
4646-export ([cleanup_expired /0 , delete_invite_by_token /2 , expire_invites /2 , expire_invite_by_token /2 , generate_invite /1 ,
47- generate_invite /2 , list_invites /1 ]).
47+ generate_invite /2 , generate_reset_token / 2 , list_invites /1 ]).
4848
4949% % helpers
5050-export ([create_account_allowed /2 , create_account_invite /4 , get_invite /2 , get_invites_tree_t /2 ,
6363-include (" ejabberd_web_admin.hrl" ).
6464
6565-ifdef (TEST ).
66- -export ([create_roster_invite /2 , find_invites_tree_root_t /4 , gen_invite /1 ,
66+ -export ([create_roster_invite /2 , create_reset_token / 2 , find_invites_tree_root_t /4 , gen_invite /1 ,
6767 gen_invite /2 , get_invites /2 , get_invites_tree_as_root_t /2 , is_token_valid /3 ]).
6868-endif .
6969
@@ -380,6 +380,7 @@ webadmin_page_host(_, Host, #request{path = [<<"invites">> | RPath], lang = Lang
380380 Set = [make_command (generate_invite , Request , BaseArguments , [{force_execution , false }]),
381381 make_command (generate_invite_with_username , Request , BaseArguments , [{force_execution , false }]),
382382 make_command (expire_invite_tokens , Request , BaseArguments , []),
383+ make_command (generate_reset_token , Request , BaseArguments , [{force_execution , false }]),
383384 make_command (cleanup_expired_invite_tokens , Request , [], [{style , danger }, {force_execution , false }])],
384385 ExpireInvites = action_button (expire_invite_by_token , Request , BaseArguments , []),
385386 DeleteInvites = action_button (delete_invite_by_token , Request , BaseArguments , [{style , danger }]),
@@ -480,6 +481,22 @@ get_commands_spec() ->
480481 {<<" xmpp:juliet@example.com?register;preauth=4bsdpwVrRDQYnF9aQQKXGbF7" >>,
481482 <<" https://example.com/invites/4bsdpwVrRDQYnF9aQQKXGbF7" >>},
482483 result = {invite , {tuple , [{invite_uri , string }, {landing_page , string }]}}},
484+ # ejabberd_commands {name = generate_reset_token ,
485+ tags = [accounts ],
486+ desc =
487+ " Create a password reset token for user with given name on given host." ,
488+ module = ? MODULE ,
489+ function = generate_reset_token ,
490+ note = " added in 26.05" ,
491+ args = [{username , binary }, {host , binary }],
492+ args_desc =
493+ [" Username" ,
494+ " Hostname" ],
495+ args_example = [<<" juliet" >>, <<" example.com" >>],
496+ result_example =
497+ {<<" xmpp:juliet@example.com?register;preauth=4bsdpwVrRDQYnF9aQQKXGbF7" >>,
498+ <<" https://example.com/invites/4bsdpwVrRDQYnF9aQQKXGbF7" >>},
499+ result = {invite , {tuple , [{invite_uri , string }, {landing_page , string }]}}},
483500 # ejabberd_commands {name = list_invites ,
484501 tags = [accounts ],
485502 desc = " List invite tokens" ,
@@ -532,11 +549,11 @@ expire_invites(User0, Server0) ->
532549expire_invite_by_token (Host , Token ) ->
533550 pretty_format_command_result (try_db_call (Host , expire_invite_by_token , [Host , Token ])).
534551
535- -spec generate_invite (binary ()) -> binary () | {error , any ()}.
552+ -spec generate_invite (binary ()) -> { binary (), binary ()} | {error , any ()}.
536553generate_invite (Host ) ->
537554 generate_invite (<<>>, Host ).
538555
539- -spec generate_invite (binary (), binary ()) -> binary () | {error , any ()}.
556+ -spec generate_invite (binary (), binary ()) -> { binary (), binary ()} | {error , any ()}.
540557generate_invite (AccountName , Host ) ->
541558 pretty_format_command_result (gen_invite (AccountName , Host )).
542559
@@ -548,7 +565,7 @@ gen_invite(Host) ->
548565
549566-endif .
550567
551- -spec gen_invite (binary (), binary ()) -> binary () | {error , any ()}.
568+ -spec gen_invite (binary (), binary ()) -> { binary (), binary ()} | {error , any ()}.
552569gen_invite (AccountName , Host0 ) ->
553570 Host = jid :nameprep (Host0 ),
554571 case create_account_invite (Host , {<<>>, Host }, AccountName , false ) of
@@ -558,6 +575,16 @@ gen_invite(AccountName, Host0) ->
558575 {token_uri (Invite ), landing_page (Host , Invite )}
559576 end .
560577
578+ -spec generate_reset_token (binary (), binary ()) -> {binary (), binary ()} | {error , any ()}.
579+ generate_reset_token (User , Host ) ->
580+ Res = case create_reset_token (User , Host ) of
581+ {error , _Reason } = Error ->
582+ Error ;
583+ Invite ->
584+ {token_uri (Invite ), landing_page (Host , Invite )}
585+ end ,
586+ pretty_format_command_result (Res ).
587+
561588list_invites (Host ) ->
562589 Res = maybe
563590 {ok , Invites } ?= try_db_call (Host , list_invites , [Host ]),
@@ -1111,27 +1138,44 @@ invite_token_t(Type, Host, Inviter, AccountName0) ->
11111138 account_name = AccountName },
11121139 mod_invites_opt :token_expire_seconds (Host )).
11131140
1114- token_uri (# invite_token {type = Type ,
1141+ -spec create_reset_token (binary (), binary ()) -> invite_token () | {error , any ()}.
1142+ create_reset_token (User , Host ) ->
1143+ maybe
1144+ # invite_token {} = ResetToken ?= reset_token (User , Host ),
1145+ F = fun () -> db_call (Host , create_invite_t , [ResetToken ]) end ,
1146+ transaction (Host , F )
1147+ end .
1148+
1149+ reset_token (User , Host ) ->
1150+ maybe
1151+ true ?= lists :member (Host , ejabberd_option :hosts ()) orelse {error , host_unknown },
1152+ true ?= ejabberd_auth :user_exists (User , Host ) orelse {error , user_not_exists },
1153+ set_token_expires (# invite_token {token = p1_rand :get_alphanum_string (? INVITE_TOKEN_LENGTH_DEFAULT ),
1154+ inviter = {<<>>, Host },
1155+ type = reset_token ,
1156+ account_name = User },
1157+ mod_invites_opt :token_expire_seconds (Host ))
1158+ end .
1159+
1160+ token_uri (# invite_token {type = roster_only ,
11151161 token = Token ,
1162+ inviter = {User , Host }}) ->
1163+ IBR = maybe_add_ibr_allowed (User , Host ),
1164+ Inviter =
1165+ jid :encode (
1166+ jid :make (User , Host )),
1167+ <<" xmpp:" , Inviter /binary , " ?roster;preauth=" , Token /binary , IBR /binary >>;
1168+ token_uri (# invite_token {token = Token ,
11161169 account_name = AccountName ,
1117- inviter = {_User , Host }})
1118- when Type =:= account_only ; Type =:= account_subscription ->
1170+ inviter = {_User , Host }}) ->
11191171 Invitee =
11201172 case AccountName of
11211173 <<>> ->
11221174 Host ;
11231175 _ ->
11241176 <<AccountName /binary , " @" , Host /binary >>
11251177 end ,
1126- <<" xmpp:" , Invitee /binary , " ?register;preauth=" , Token /binary >>;
1127- token_uri (# invite_token {type = roster_only ,
1128- token = Token ,
1129- inviter = {User , Host }}) ->
1130- IBR = maybe_add_ibr_allowed (User , Host ),
1131- Inviter =
1132- jid :encode (
1133- jid :make (User , Host )),
1134- <<" xmpp:" , Inviter /binary , " ?roster;preauth=" , Token /binary , IBR /binary >>.
1178+ <<" xmpp:" , Invitee /binary , " ?register;preauth=" , Token /binary >>.
11351179
11361180maybe_add_ibr_allowed (User , Host ) ->
11371181 case create_account_allowed (Host , jid :make (User , Host )) of
@@ -1295,6 +1339,8 @@ pretty_format_command_result({error, host_unknown}) ->
12951339 {error , " Virtual host not known" };
12961340pretty_format_command_result ({error , user_exists }) ->
12971341 {error , " Username already taken" };
1342+ pretty_format_command_result ({error , user_not_exists }) ->
1343+ {error , " User does not exist" };
12981344pretty_format_command_result ({ok , Result }) ->
12991345 Result ;
13001346pretty_format_command_result (Result ) ->
0 commit comments