From 967104b1033e1a2b98754e982f70b8b838b635af Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 21 Aug 2019 11:54:36 +0200 Subject: [PATCH] Added email account recovery API --- conf/emails.conf | 15 +++++ src/misc/email.php | 15 +++++ src/xmlrpc/accounts.php | 105 +++++++++++++++++++++++++++++++--- src/xmlrpc/results_values.php | 1 + src/xmlrpc/user_info.php | 4 ++ src/xmlrpc/xmlrpc.php | 18 +++--- 6 files changed, 143 insertions(+), 15 deletions(-) diff --git a/conf/emails.conf b/conf/emails.conf index edb9341..bab180a 100644 --- a/conf/emails.conf +++ b/conf/emails.conf @@ -53,4 +53,19 @@ define("EMAIL_ACTIVATION_BODY", "Hello,\nActivation pending for using your Linph */ define("EMAIL_ACTIVATION_BODY_HTML", 'Start your sip.linphone.org service

Hello,

Activation pending for using your Linphone account.
Please use the link bellow to activate your account :

%link%

 

Regards,
The Linphone team.

'); +/* + * The subject of the account recovery email. + */ +define("EMAIL_RECOVERY_SUBJECT", "Recover your sip.linphone.org account"); + +/* + * The body (as text) of the account recovery email. + */ +define("EMAIL_RECOVERY_BODY", "Hello,\nHere is your recovery code: %key%\n\nRegards,\nThe Linphone team.\n"); + +/* + * The body (as html) of the account recovery email. + */ +define("EMAIL_RECOVERY_BODY_HTML", 'Recover your sip.linphone.org account

Hello,

Here is your recovery code: %key%

Regards,
The Linphone team.

'); + ?> \ No newline at end of file diff --git a/src/misc/email.php b/src/misc/email.php index b16e144..31737db 100644 --- a/src/misc/email.php +++ b/src/misc/email.php @@ -87,4 +87,19 @@ function send_email_with_activation_link($email, $key) { Logger::getInstance()->message("[EMAIL] Email sent to email " . $email . " to activate the account"); } +function send_email_with_recover_key($email, $key) { + if( !EMAIL_ENABLED ){ + Logger::getInstance()->warning("[EMAIL] Emails are disabled"); + return "WARNING_EMAILS_DISABLED"; + } + + $body = str_replace("%key%", $key, EMAIL_RECOVERY_BODY); + Logger::getInstance()->debug("[EMAIL] Recovery body is " . $body); + $body_html = str_replace("%key%", $key, EMAIL_RECOVERY_BODY_HTML); + Logger::getInstance()->debug("[EMAIL] Recovery html body is " . $body_html); + + send_email($email, EMAIL_ACTIVATION_SUBJECT, $body, $body_html); + Logger::getInstance()->message("[EMAIL] Email sent to email " . $email . " to recover the account"); +} + ?> \ No newline at end of file diff --git a/src/xmlrpc/accounts.php b/src/xmlrpc/accounts.php index 13b8e60..593de39 100644 --- a/src/xmlrpc/accounts.php +++ b/src/xmlrpc/accounts.php @@ -28,6 +28,8 @@ include_once __DIR__ . '/../misc/utilities.php'; include_once __DIR__ . '/results_values.php'; +define ("INVALID_CONFIRMATION_KEY", "ERROR"); + // args = [user, pwd, [domain], [algo]] // /!\ This method must be used for tests purposes only /!\ function xmlrpc_get_confirmation_key($method, $args) { @@ -257,9 +259,9 @@ function xmlrpc_activate_phone_account($method, $args) { } $key_db = $account->confirmation_key; - if ($key == "ERROR" || $key != $key_db) { - if ($key_db != "ERROR") { - $account->confirmation_key = "ERROR"; + if ($key == INVALID_CONFIRMATION_KEY || $key != $key_db) { + if ($key_db != INVALID_CONFIRMATION_KEY) { + $account->confirmation_key = INVALID_CONFIRMATION_KEY; $account->update(); } @@ -267,6 +269,10 @@ function xmlrpc_activate_phone_account($method, $args) { return KEY_DOESNT_MATCH; } + // Key is one time only + $account->confirmation_key = INVALID_CONFIRMATION_KEY; + $account->update(); + // If this is a recovery, account is already activated, don't go through the following again if (!is_activated($account->activated)) { $expiration = NULL; @@ -439,9 +445,9 @@ function xmlrpc_activate_email_account($method, $args) { } $key_db = $account->confirmation_key; - if ($key == "ERROR" || $key != $key_db) { - if ($key_db != "ERROR") { - $account->confirmation_key = "ERROR"; + if ($key == INVALID_CONFIRMATION_KEY || $key != $key_db) { + if ($key_db != INVALID_CONFIRMATION_KEY) { + $account->confirmation_key = INVALID_CONFIRMATION_KEY; $account->update(); } Logger::getInstance()->error("Key doesn't match"); @@ -450,6 +456,8 @@ function xmlrpc_activate_email_account($method, $args) { $expiration = NULL; $account->activated = "1"; + // Key is one time only + $account->confirmation_key = INVALID_CONFIRMATION_KEY; $account->update(); // TODO @@ -607,6 +615,7 @@ function xmlrpc_recover_phone_account($method, $args) { $database = new Database(); $db = $database->getConnection(); + $account = new Account($db); $account->username = $phone; $account->domain = $domain; @@ -644,6 +653,84 @@ function xmlrpc_recover_phone_account($method, $args) { return $account->username; } +// args = [username, email, [domain]] +function xmlrpc_recover_email_account($method, $args) { + $username = $args[0]; + $email = $args[1]; + $domain = get_domain($args[2]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_email_account(" . $username . ", " . $email . ", " . $domain . ")"); + + $database = new Database(); + $db = $database->getConnection(); + + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + if ($email != $account->email) { + return EMAIL_DOESNT_MATCH; + } + + $account->confirmation_key = uniqid(); + $account->update(); + + if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { + send_email_with_recover_key($email, $account->confirmation_key); + } + + return OK; +} + +// args = [username, key, [domain], [algo]] +function xmlrpc_recover_account_from_confirmation_key($method, $args) { + $username = $args[0]; + $key = $args[1]; + $domain = get_domain($args[2]); + $algo = get_algo($args[3]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_account_from_confirmation_key(" . $username . ", " . $domain . ", " . $key . ", " . $algo . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $key_db = $account->confirmation_key; + if ($key == INVALID_CONFIRMATION_KEY || $key != $key_db) { + if ($key_db != INVALID_CONFIRMATION_KEY) { + $account->confirmation_key = INVALID_CONFIRMATION_KEY; + $account->update(); + } + + Logger::getInstance()->error("Key doesn't match"); + return KEY_DOESNT_MATCH; + } + + // Key is one time only + $account->confirmation_key = INVALID_CONFIRMATION_KEY; + $account->update(); + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if ($password->getOne()) { + return $password->password; + } + + return PASSWORD_NOT_FOUND; +} + // args = [username, old password, new password, [domain], [algo]] function xmlrpc_update_password($method, $args) { $user = $args[0]; @@ -781,12 +868,16 @@ function xmlrpc_accounts_register_methods($server) { xmlrpc_server_register_method($server, 'is_account_used', 'xmlrpc_is_account_used');// args = [username, [domain]], return OK or NOK xmlrpc_server_register_method($server, 'is_account_activated', 'xmlrpc_is_account_activated');// args = [username, [domain]], return OK or NOK xmlrpc_server_register_method($server, 'is_phone_number_used', 'xmlrpc_is_phone_number_used');// args = [phone], return OK_ACCOUNT, OK_ALIAS or NOK + xmlrpc_server_register_method($server, 'get_phone_number_for_account', 'xmlrpc_get_phone_number_for_account');// args = [username, [domain]], return a phone number or an error + xmlrpc_server_register_method($server, 'activate_phone_account', 'xmlrpc_activate_phone_account');// args = [phone, username, key, [domain]], return ha1_password xmlrpc_server_register_method($server, 'create_phone_account', 'xmlrpc_create_phone_account');// args = [phone, [username], [password], useragent, [domain], [lang]], return OK xmlrpc_server_register_method($server, 'activate_email_account', 'xmlrpc_activate_email_account');// args = [username, key, [domain]], return ha1_password xmlrpc_server_register_method($server, 'create_email_account', 'xmlrpc_create_email_account');// args = [username, email, [hash], useragent, [domain]], return OK - xmlrpc_server_register_method($server, 'get_phone_number_for_account', 'xmlrpc_get_phone_number_for_account');// args = [username, [domain]], return a phone number or an error + xmlrpc_server_register_method($server, 'recover_phone_account', 'xmlrpc_recover_phone_account');// args = [phone, [domain], [lang]], return username + xmlrpc_server_register_method($server, 'recover_email_account', 'xmlrpc_recover_email_account');// args = [username, email, [domain]], return OK + xmlrpc_server_register_method($server, 'recover_account_from_confirmation_key', 'xmlrpc_recover_account_from_confirmation_key');// args = [username, key, [domain], [algo]] xmlrpc_server_register_method($server, 'update_password', 'xmlrpc_update_password');// args = [username, old password, new password, [domain]], return OK xmlrpc_server_register_method($server, 'update_hash', 'xmlrpc_update_hash');// args = [username, old hash, new hash, [domain]], return OK diff --git a/src/xmlrpc/results_values.php b/src/xmlrpc/results_values.php index 4519859..0d70b54 100644 --- a/src/xmlrpc/results_values.php +++ b/src/xmlrpc/results_values.php @@ -47,6 +47,7 @@ define ("USERINFO_NOT_FOUND", "ERROR_USERINFO_NOT_FOUND"); define ("KEY_DOESNT_MATCH", "ERROR_KEY_DOESNT_MATCH"); define ("PASSWORD_DOESNT_MATCH", "ERROR_PASSWORD_DOESNT_MATCH"); +define ("EMAIL_DOESNT_MATCH", "ERROR_EMAIL_DOESNT_MATCH"); /* Disabled features */ diff --git a/src/xmlrpc/user_info.php b/src/xmlrpc/user_info.php index 380a633..c7835bc 100644 --- a/src/xmlrpc/user_info.php +++ b/src/xmlrpc/user_info.php @@ -149,6 +149,10 @@ function xmlrpc_get_account_by_confirmation_key($method, $args) { Logger::getInstance()->message("[XMLRPC] xmlrpc_get_account_by_confirmation_key(" . $confirmation_key . ")"); + if ($confirmation_key == "ERROR") { + return KEY_DOESNT_MATCH; + } + $database = new Database(); $db = $database->getConnection(); diff --git a/src/xmlrpc/xmlrpc.php b/src/xmlrpc/xmlrpc.php index 14acac2..a4e4f8b 100644 --- a/src/xmlrpc/xmlrpc.php +++ b/src/xmlrpc/xmlrpc.php @@ -48,24 +48,26 @@ if (USE_DIGEST_AUTH) { 3 => 'activate_email_account', 4 => 'activate_phone_account', 5 => 'recover_phone_account', - 6 => 'get_phone_number_for_account', - 7 => 'is_account_activated', + 6 => 'recover_email_account', + 7 => 'recover_account_from_confirmation_key', + 8 => 'get_phone_number_for_account', + 9 => 'is_account_activated', // aliases - 8 => 'is_alias_used', + 10 => 'is_alias_used', // inapp - 9 => 'check_payload_signature', + 11 => 'check_payload_signature', // misc - 10 => 'add_ec_calibration_result', + 12 => 'add_ec_calibration_result', // compatibility - 11 => 'create_account', - 12 => 'create_account_with_useragent', + 13 => 'create_account', + 14 => 'create_account_with_useragent', // user_info - 13 => 'get_account_by_confirmation_key', + 15 => 'get_account_by_confirmation_key', ); // Get authentication header if there is one