diff --git a/plugins/ldap-mail-accounts/LdapMailAccounts.php b/plugins/ldap-mail-accounts/LdapMailAccounts.php index 2f93dbd14..5dc0c4eb6 100644 --- a/plugins/ldap-mail-accounts/LdapMailAccounts.php +++ b/plugins/ldap-mail-accounts/LdapMailAccounts.php @@ -3,7 +3,9 @@ use RainLoop\Enumerations\Capa; use MailSo\Log\Logger; use RainLoop\Actions; +use RainLoop\Model\AdditionalAccount; use RainLoop\Model\MainAccount; +use RainLoop\Providers\Storage\Enumerations\StorageType; class LdapMailAccounts { @@ -50,14 +52,13 @@ class LdapMailAccounts /** * @inheritDoc * - * AOverwrite the MainAccount mail address by looking up the new one in the ldap directory + * Overwrite the MainAccount mail address by looking up the new one in the ldap directory * * The ldap search string has to be configured in the plugin configuration of the extension (in the SnappyMail Admin Panel) * * @param string &$sEmail - * @param string &$sLogin */ - public function overwriteEmail(&$sEmail, &$sLogin) + public function overwriteEmail(&$sEmail) { try { $this->EnsureBound(); @@ -106,7 +107,7 @@ class LdapMailAccounts foreach($mailAddressResults as $mailAddressResult) { if($mailAddressResult->username === $username) { - //$sLogin is already set to be the same as $sEmail by function "resolveLoginCredentials" in /RainLoop/Actions/UserAuth.php + //$sImapUser and $sSmtpUser are already set to be the same as $sEmail by function "resolveLoginCredentials" in /RainLoop/Actions/UserAuth.php //that called this hook, so we just have to overwrite the mail address $sEmail = $mailAddressResult->mailMainAccount; } @@ -131,10 +132,19 @@ class LdapMailAccounts return false; // exceptions are only thrown from the handle error function that does logging already } - // Try to get account information. IncLogin() returns the username of the user - // and removes the domainname if this was configured inside the domain config. - $username = @ldap_escape($oAccount->IncLogin(), "", LDAP_ESCAPE_FILTER); + //Basing on https://github.com/the-djmaze/snappymail/issues/616 + $oActions = \RainLoop\Api::Actions(); + + //Check if SnappyMail is configured to allow additional accounts + if (!$oActions->GetCapa(Capa::ADDITIONAL_ACCOUNTS)) { + return $oActions->FalseResponse(__FUNCTION__); + } + + // Try to get account information. ImapUser() returns the username of the user + // and removes the domainname if this was configured inside the domain config. + $username = @ldap_escape($oAccount->ImapUser(), "", LDAP_ESCAPE_FILTER); + $searchString = $this->config->search_string; // Replace placeholders inside the ldap search string with actual values @@ -159,21 +169,10 @@ class LdapMailAccounts catch (LdapMailAccountsException $e) { return false; // exceptions are only thrown from the handle error function that does logging already } + if (count($mailAddressResults) < 1) { $this->logger->Write("Could not find user $username", \LOG_NOTICE, self::LOG_KEY); return false; - } else if (count($mailAddressResults) == 1) { - $this->logger->Write("Found only one match for user $username, no additional mail adresses found", \LOG_NOTICE, self::LOG_KEY); - return true; - } - - //Basing on https://github.com/the-djmaze/snappymail/issues/616 - - $oActions = \RainLoop\Api::Actions(); - - //Check if SnappyMail is configured to allow additional accounts - if (!$oActions->GetCapa(Capa::ADDITIONAL_ACCOUNTS)) { - return $oActions->FalseResponse(__FUNCTION__); } $aAccounts = $oActions->GetAccounts($oAccount); @@ -189,6 +188,25 @@ class LdapMailAccounts } } + //SnappyMail saves the passwords of the additional accounts by encrypting them using a cryptkey that is saved in the file .cryptkey + //When the password of the main account changes, SnappyMail asks the user for the old password to reencrypt the keys with the new userpassword. + //On a password change using ldap (or when the password has been forgotten by the user) this makes us some problems. Therefore overwrite + //the .cryptkey file in order to always accept the actual ldap password of the user. This has side effects on pgp keys! + //See https://github.com/the-djmaze/snappymail/issues/1570#issuecomment-2085528061 + if ($this->config->bool_overwrite_cryptkey) { + if (!$oActions->StorageProvider()->Put($oAccount, StorageType::ROOT, '.cryptkey', "")) { + $this->logger->Write("Could not overwrite the .cryptkey file!", \LOG_WARNING, self::LOG_KEY); + return $oActions->FalseResponse(__FUNCTION__); + } + } + + if (count($mailAddressResults) == 1) { + $this->logger->Write("Found only one match for user $username, no additional mail adresses found", \LOG_NOTICE, self::LOG_KEY); + //Write back the accounts even if no additional account was found. This ensures, that previous additional accounts are always removed + $oActions->SetAccounts($oAccount, $aAccounts); + return true; + } + foreach($mailAddressResults as $mailAddressResult) { $sUsername = $mailAddressResult->username; @@ -210,7 +228,18 @@ class LdapMailAccounts //if this fails the user will see the new mail addresses but will be asked for the correct password $sPass = new \SnappyMail\SensitiveString($oAccount->IncPassword()); //After creating the accounts here $sUsername is used as username to login to the IMAP server (see Account.php) - $oNewAccount = RainLoop\Model\AdditionalAccount::NewInstanceFromCredentials($oActions, $sEmail, $sUsername, $sPass); + //$oNewAccount = RainLoop\Model\AdditionalAccount::NewInstanceFromCredentials($oActions, $sEmail, $sUsername, $sPass); + + $oDomain = $oActions->DomainProvider()->Load($sDomain, false); + + $oNewAccount = new AdditionalAccount; + $oNewAccount->setCredentials( + $oDomain, + $sEmail, + $sUsername, + $sPass, + $sUsername + ); $aAccounts[$sEmail] = $oNewAccount->asTokenArray($oAccount); } diff --git a/plugins/ldap-mail-accounts/LdapMailAccountsConfig.php b/plugins/ldap-mail-accounts/LdapMailAccountsConfig.php index ab62ed58d..8b674e4f2 100644 --- a/plugins/ldap-mail-accounts/LdapMailAccountsConfig.php +++ b/plugins/ldap-mail-accounts/LdapMailAccountsConfig.php @@ -21,6 +21,7 @@ class LdapMailAccountsConfig public const CONFIG_FIELD_MAIL_ADDRESS_ADDITIONAL_ACCOUNT = "field_mail_address_additional_account"; public const CONFIG_BOOL_OVERWRITE_MAIL_ADDRESS_MAIN_ACCOUNT = "bool_overwrite_mail_address_main_account"; public const CONFIG_FIELD_MAIL_ADDRESS_MAIN_ACCOUNT = "field_mail_address_main_account"; + public const CONFIG_BOOL_OVERWRITE_CRYPTKEY = "bool_overwrite_cryptkey"; public $server; public $protocol; @@ -36,6 +37,7 @@ class LdapMailAccountsConfig public $field_mail_address_main_account; public $field_mail_address_additional_account; public $bool_overwrite_mail_address_main_account; + public $bool_overwrite_cryptkey; public static function MakeConfig(Plugin $config): LdapMailAccountsConfig { @@ -54,6 +56,7 @@ class LdapMailAccountsConfig $ldap->field_mail_address_additional_account = trim($config->Get("plugin", self::CONFIG_FIELD_MAIL_ADDRESS_ADDITIONAL_ACCOUNT)); $ldap->bool_overwrite_mail_address_main_account = $config->Get("plugin", self::CONFIG_BOOL_OVERWRITE_MAIL_ADDRESS_MAIN_ACCOUNT); $ldap->field_mail_address_main_account = trim($config->Get("plugin", self::CONFIG_FIELD_MAIL_ADDRESS_MAIN_ACCOUNT)); + $ldap->bool_overwrite_cryptkey = $config->Get("plugin", self::CONFIG_BOOL_OVERWRITE_CRYPTKEY); return $ldap; } diff --git a/plugins/ldap-mail-accounts/README.md b/plugins/ldap-mail-accounts/README.md index 8f7417e8b..d26d6302d 100644 --- a/plugins/ldap-mail-accounts/README.md +++ b/plugins/ldap-mail-accounts/README.md @@ -8,6 +8,8 @@ Version 2.0.0 changes the way additional mail accounts get their e-mail address: Now it is also possible to overwrite the mail address of the main account: if a user logs into SnappyMail with a username and SnappyMail added the configured default domain the mail address of the main account could have been some not existing address like "username@default-domain.com". This could have happend when using the Nextcloud SnappyMail integration that offers an automatic login using the Nextcloud username. The plugin now can be configured to overwrite the username or mail address used at login with a mail address found inside ldap. +Version 2.2.0 adds compatibility with SnappyMail 2.36.1 and later where some changes were introduced that made the plugin unusable. This version also adds the possibility to delete the .cryptkey file on evere login. For more information see the `Configuration` section below. + ### Configuration - Install and activate the plugin using the SnappyMail Admin Panel -> menu Extensions. - Click on the gear symbol beside the plugin to open the configration dialog. @@ -20,4 +22,8 @@ The plugin now can be configured to overwrite the username or mail address used Section `Overwrite mail address of main account` can be used to overwrite the username or mail address used at login with a value found in ldap. If activated, the username or mail address used at login will be looked up inside the `Username field` in ldap (for details see how a search for additional accounts is made). If the username is found, the value of the field `Mail address field for main account` will be used to overwrite the mail address of the main account. + `Overwrite user cryptkey` can be activated to prevent SnappyMail from asking the user for his old LDAP password when this password was changed or reseted. SnappyMail saves the passwords of the additional accounts by encrypting them using a cryptkey that is saved in the file `.cryptkey`. When the password of the main account changes, SnappyMail asks the user for the old password to reencrypt the keys with the new userpassword. + On a password change using ldap (or when the password has been forgotten by the user) this makes problems and asks the user to insert the old password. Therefore activating this option overwrites the .cryptkey file on login in order to always accept the actual ldap password of the user. + **ATTENTION:** This has side effects on pgp keys because these are also secured by the cryptkey and could therefore not be accessible anymore! See https://github.com/the-djmaze/snappymail/issues/1570#issuecomment-2085528061 . This has also an impact on additional mail accounts that aren't created by this plugin because without the cryptkey saved passwords of additional mail accounts can not be decrypted anymore. + **Important:** SnappyMail normally needs a mail address as username. This plugin handles some special circumstances (login with an ldap username, not a mail address) so that you can login to your IMAP server with the ldap username but send mails with a mail address connected to this ldap user. diff --git a/plugins/ldap-mail-accounts/index.php b/plugins/ldap-mail-accounts/index.php index a6063c640..68d802503 100644 --- a/plugins/ldap-mail-accounts/index.php +++ b/plugins/ldap-mail-accounts/index.php @@ -12,11 +12,11 @@ class LdapMailAccountsPlugin extends AbstractPlugin { const NAME = 'LDAP Mail Accounts', - VERSION = '2.1.1', + VERSION = '2.2.0', AUTHOR = 'cm-schl', URL = 'https://github.com/cm-sch', - RELEASE = '2024-03-16', - REQUIRED = '2.35.3', + RELEASE = '2024-05-28', + REQUIRED = '2.36.1', CATEGORY = 'Accounts', DESCRIPTION = 'Add additional mail accounts the SnappyMail user has access to by a LDAP query. Basing on the work of FWest98 (https://github.com/FWest98).'; @@ -40,11 +40,13 @@ class LdapMailAccountsPlugin extends AbstractPlugin * Overwrite the MainAccount mail address by looking up the new one in the ldap directory * * @param string &$sEmail - * @param string &$sLogin + * @param string &$sImapUser + * @param string &$sPassword + * @param string &$sSmtpUser */ - public function overwriteMainAccountEmail(&$sEmail, &$sLogin) + public function overwriteMainAccountEmail(&$sEmail, &$sImapUser, &$sPassword, &$sSmtpUser) { - $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); + $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sImapUser email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); // Set up config $config = LdapMailAccountsConfig::MakeConfig($this->Config()); @@ -52,10 +54,10 @@ class LdapMailAccountsPlugin extends AbstractPlugin if ($config->bool_overwrite_mail_address_main_account) { $oldapMailAccounts = new LdapMailAccounts($config, $this->Manager()->Actions()->Logger()); - $oldapMailAccounts->overwriteEmail($sEmail, $sLogin); + $oldapMailAccounts->overwriteEmail($sEmail); } - $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); + $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sImapUser email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); } // Function gets called by RainLoop/Actions/User.php @@ -92,6 +94,17 @@ class LdapMailAccountsPlugin extends AbstractPlugin ->SetDefaultValue("mail"), ]); + $groupAdditionalSettings = new \RainLoop\Plugins\PropertyCollection('Additional settings'); + $groupAdditionalSettings->exchangeArray([ + \RainLoop\Plugins\Property::NewInstance(LdapMailAccountsConfig::CONFIG_BOOL_OVERWRITE_CRYPTKEY)->SetLabel('Overwrite user cryptkey') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDescription("SnappyMail saves the passwords of the additional accounts by encrypting them using a cryptkey that is saved in the file \".cryptkey\". When the password of the main account changes, SnappyMail asks the user for the old password to reencrypt the keys with the new userpassword. + \nOn a password change using ldap (or when the password has been forgotten by the user) this makes problems and asks the user to insert the old password. Therefore activating this option overwrites the .cryptkey file on login in order to always accept the actual ldap password of the user. + \nATTENTION: This has side effects on pgp keys because these are also secured by the cryptkey and could therefore not be accessible anymore! + \nSee https://github.com/the-djmaze/snappymail/issues/1570#issuecomment-2085528061") + ->SetDefaultValue(false), + ]); + return [ \RainLoop\Plugins\Property::NewInstance(LdapMailAccountsConfig::CONFIG_SERVER) ->SetLabel("LDAP Server URL") @@ -171,8 +184,9 @@ class LdapMailAccountsPlugin extends AbstractPlugin ->SetDescription("The field containing the default sender name of the found additional mail account.") ->SetDefaultValue("displayName"), - $groupOverwriteMainAccount + $groupOverwriteMainAccount, + $groupAdditionalSettings ]; } }