mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35:55 +08:00
ChangePasswordPlugin also use Have I Been Pwned
This commit is contained in:
parent
6739ec21c8
commit
b4dbf249ee
|
@ -7,9 +7,9 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
const
|
||||
NAME = 'Change Password',
|
||||
VERSION = '2.37',
|
||||
RELEASE = '2024-03-29',
|
||||
REQUIRED = '2.36.0',
|
||||
VERSION = '2.38',
|
||||
RELEASE = '2024-04-22',
|
||||
REQUIRED = '2.36.1',
|
||||
CATEGORY = 'Security',
|
||||
DESCRIPTION = 'Extension to allow users to change their passwords';
|
||||
|
||||
|
@ -18,7 +18,8 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
CouldNotSaveNewPassword = 130,
|
||||
CurrentPasswordIncorrect = 131,
|
||||
NewPasswordShort = 132,
|
||||
NewPasswordWeak = 133;
|
||||
NewPasswordWeak = 133,
|
||||
NewPasswordHibp = 134;
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
|
@ -103,18 +104,23 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
public function configMapping() : array
|
||||
{
|
||||
$result = [
|
||||
\RainLoop\Plugins\Property::NewInstance("pass_min_length")
|
||||
\RainLoop\Plugins\Property::NewInstance('pass_min_length')
|
||||
->SetLabel('Password minimum length')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::INT)
|
||||
->SetDescription('Minimum length of the password')
|
||||
->SetDefaultValue(10)
|
||||
->SetAllowedInJs(true),
|
||||
\RainLoop\Plugins\Property::NewInstance("pass_min_strength")
|
||||
\RainLoop\Plugins\Property::NewInstance('pass_min_strength')
|
||||
->SetLabel('Password minimum strength')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::INT)
|
||||
->SetDescription('Minimum strength of the password in %')
|
||||
->SetDefaultValue(70)
|
||||
->SetAllowedInJs(true),
|
||||
\RainLoop\Plugins\Property::NewInstance('check_hibp')
|
||||
->SetLabel('Check Have I Been Pwned')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDescription('Check if new passphrase is in a data breach')
|
||||
->SetDefaultValue(false),
|
||||
];
|
||||
foreach ($this->getSupportedDrivers(true) as $name => $class) {
|
||||
$group = new \RainLoop\Plugins\PropertyCollection($name);
|
||||
|
@ -160,6 +166,9 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
throw new ClientException(static::NewPasswordWeak, null, $oActions->StaticI18N('NOTIFICATIONS/NEW_PASSWORD_WEAK'));
|
||||
}
|
||||
$oNewPassword = new \SnappyMail\SensitiveString($sNewPassword);
|
||||
if ($this->Config()->Get('plugin', 'check_hibp', false) && \SnappyMail\Hibp::password($oNewPassword)) {
|
||||
throw new ClientException(static::NewPasswordHibp, null, $oActions->StaticI18N('NOTIFICATIONS/NEW_PASSWORD_HIBP'));
|
||||
}
|
||||
|
||||
$bResult = false;
|
||||
$oConfig = $this->Config();
|
||||
|
|
|
@ -10,3 +10,4 @@ CURRENT_PASSWORD_INCORRECT = "Aktuelles Passwort falsch"
|
|||
CURRENT_PASSWORD_INCORRECT = "Aktuelles Passwort falsch"
|
||||
NEW_PASSWORD_SHORT = "Passwort ist zu kurz"
|
||||
NEW_PASSWORD_WEAK = "Passwort ist zu einfach"
|
||||
NEW_PASSWORD_HIBP = "Passwort gefunden in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Could not save new password"
|
|||
CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT = "Password is too short"
|
||||
NEW_PASSWORD_WEAK = "Password is too easy"
|
||||
NEW_PASSWORD_HIBP = "Password found in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Could not save new password"
|
|||
CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT = "Password is too short"
|
||||
NEW_PASSWORD_WEAK = "Password is too easy"
|
||||
NEW_PASSWORD_HIBP = "Password found in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Could not save new password"
|
|||
CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT = "Password is too short"
|
||||
NEW_PASSWORD_WEAK = "Password is too easy"
|
||||
NEW_PASSWORD_HIBP = "Password found in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "No se puede guardar la nueva contraseña"
|
|||
CURRENT_PASSWORD_INCORRECT = "La contraseña actual es incorrecta"
|
||||
NEW_PASSWORD_SHORT = "La contraseña es muy corta"
|
||||
NEW_PASSWORD_WEAK = "La contraseña es muy fácil"
|
||||
NEW_PASSWORD_HIBP = "Contraseña encontrada en Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Impossible d'enregistrer le nouveau mot de passe"
|
|||
CURRENT_PASSWORD_INCORRECT = "Le mot de passe actuel est incorrect"
|
||||
NEW_PASSWORD_SHORT = "Le mot de passe est trop court"
|
||||
NEW_PASSWORD_WEAK = "Le mot de passe n'est pas assez fort"
|
||||
NEW_PASSWORD_HIBP = "Mot de passe trouvé dans Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Non è stato possibile salvare la nuova password"
|
|||
CURRENT_PASSWORD_INCORRECT = "La password attuale non è corretta"
|
||||
NEW_PASSWORD_SHORT = "La password scelta è troppo breve"
|
||||
NEW_PASSWORD_WEAK = "La password scelta non è abbastanza complessa"
|
||||
NEW_PASSWORD_HIBP = "Password trovata in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "Nieuwe wachtwoord kon niet opgeslagen worden"
|
|||
CURRENT_PASSWORD_INCORRECT = "Huidig wachtwoord onjuist"
|
||||
NEW_PASSWORD_SHORT = "Wachtwoord is te kort"
|
||||
NEW_PASSWORD_WEAK = "Wachtwoord is te makkelijk"
|
||||
NEW_PASSWORD_HIBP = "Wachtwoord gevonden in Have I Been Pwned"
|
||||
|
|
|
@ -10,3 +10,4 @@ COULD_NOT_SAVE_NEW_PASSWORD = "无法保存新密码"
|
|||
CURRENT_PASSWORD_INCORRECT = "当前密码不正确"
|
||||
NEW_PASSWORD_SHORT = "密码太短"
|
||||
NEW_PASSWORD_WEAK = "密码过于简单"
|
||||
NEW_PASSWORD_HIBP = "在 Have I Been Pwned 中找到密码"
|
||||
|
|
|
@ -3,14 +3,8 @@
|
|||
* https://haveibeenpwned.com/API/v3
|
||||
*/
|
||||
|
||||
use RainLoop\Model\Account;
|
||||
use MailSo\Imap\ImapClient;
|
||||
use MailSo\Imap\Settings as ImapSettings;
|
||||
use MailSo\Sieve\SieveClient;
|
||||
use MailSo\Sieve\Settings as SieveSettings;
|
||||
use MailSo\Smtp\SmtpClient;
|
||||
use MailSo\Smtp\Settings as SmtpSettings;
|
||||
use MailSo\Mime\Message as MimeMessage;
|
||||
use SnappyMail\Hibp;
|
||||
use SnappyMail\SensitiveString;
|
||||
|
||||
class HaveibeenpwnedPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
|
@ -22,7 +16,7 @@ class HaveibeenpwnedPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '0.1',
|
||||
RELEASE = '2024-04-22',
|
||||
REQUIRED = '2.14.0',
|
||||
REQUIRED = '2.36.1',
|
||||
CATEGORY = 'General',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = 'Check if your passphrase or email address is in a data breach';
|
||||
|
@ -40,37 +34,14 @@ class HaveibeenpwnedPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$oAccount = $this->Manager()->Actions()->getAccountFromToken();
|
||||
// $oAccount = \RainLoop\Api::Actions()->getAccountFromToken();
|
||||
|
||||
$HTTP = \SnappyMail\HTTP\Request::factory();
|
||||
|
||||
$breached = null;
|
||||
$api_key = \trim($this->Config()->Get('plugin', 'hibp-api-key', ''));
|
||||
if ($api_key) {
|
||||
$breached = $HTTP->doRequest('GET', "https://haveibeenpwned.com/api/v3/breachedaccount/{$oAccount->Email()}", null, [
|
||||
'hibp-api-key' => $api_key
|
||||
]);
|
||||
}
|
||||
$breaches = $api_key ? Hibp::account($api_key, $oAccount->Email()) : null;
|
||||
|
||||
$pass = \sha1($oAccount->ImapPass());
|
||||
$prefix = \substr($pass, 0, 5);
|
||||
$suffix = \substr($pass, 5);
|
||||
$response = $HTTP->doRequest('GET', "https://api.pwnedpasswords.com/range/{$prefix}");
|
||||
$passwords = [];
|
||||
foreach (\preg_split('/\\R/', $response->body) as $entry) {
|
||||
if ($entry) {
|
||||
$entry = \explode(':', $entry);
|
||||
$passwords[$entry[0]] = (int) $entry[1];
|
||||
}
|
||||
}
|
||||
$pwned = Hibp::password(new SensitiveString($oAccount->ImapPass()));
|
||||
|
||||
return $this->jsonResponse(__FUNCTION__, array(
|
||||
'pwned' => isset($passwords[$suffix]) ? $passwords[$suffix] : 0,
|
||||
'breached' => $breached ? [
|
||||
'request_uri' => $breached->request_uri,
|
||||
'final_uri' => $breached->final_uri,
|
||||
'status' => $breached->status,
|
||||
'headers' => $breached->headers,
|
||||
'body' => $breached->body
|
||||
] : []
|
||||
'pwned' => $pwned,
|
||||
'breaches' => $breaches
|
||||
));
|
||||
}
|
||||
|
||||
|
|
45
snappymail/v/0.0.0/app/libraries/snappymail/hibp.php
Normal file
45
snappymail/v/0.0.0/app/libraries/snappymail/hibp.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/**
|
||||
* https://haveibeenpwned.com/API/v3
|
||||
* https://haveibeenpwned.com/API/Key
|
||||
*/
|
||||
|
||||
namespace SnappyMail;
|
||||
|
||||
class Hibp
|
||||
{
|
||||
public static function password(SensitiveString $password): int
|
||||
{
|
||||
$pass = \strtoupper(\sha1($password));
|
||||
$prefix = \substr($pass, 0, 5);
|
||||
$suffix = \substr($pass, 5);
|
||||
$response = HTTP\Request::factory()->doRequest('GET', "https://api.pwnedpasswords.com/range/{$prefix}");
|
||||
if (200 !== $response->status) {
|
||||
throw new HTTP\Exception('Hibp', $response->status);
|
||||
}
|
||||
foreach (\preg_split('/\\R/', $response->body) as $entry) {
|
||||
if ($entry) {
|
||||
$entry = \explode(':', $entry);
|
||||
if ($entry[0] === $suffix) {
|
||||
return (int) $entry[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function account(string $api_key, string $email): ?array
|
||||
{
|
||||
if ($api_key) {
|
||||
$email = \rawurlencode(IDN::emailToAscii($email));
|
||||
$response = HTTP\Request::factory()->doRequest('GET', "https://haveibeenpwned.com/api/v3/breachedaccount/{$email}", null, [
|
||||
'hibp-api-key' => $api_key
|
||||
]);
|
||||
if (200 !== $response->status) {
|
||||
throw new HTTP\Exception('Hibp', $response->status);
|
||||
}
|
||||
return \json_decode($response->body, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue