diff --git a/appinfo/info.xml b/appinfo/info.xml index e9d62deb4b5b81294fb0f8b75f61970037ae3846..2803cce99977bfc705dac0e84e01551395f02448 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -10,7 +10,7 @@ - 1.2.0 + 2.0.0 agpl Akhil Potukuchi EcloudAccounts diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 799c6b78458b6dc80670397773466cee3d54d662..cc21683232c52632782abfcb5c60ba2f66c3dba4 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -32,10 +32,14 @@ use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\User\Events\UserDeletedEvent; -use OCP\User\Events\UserChangedEvent; use OCA\EcloudAccounts\Listeners\UserDeletedListener; +use OCA\EcloudAccounts\Listeners\BeforeUserDeletedListener; +use OCA\EcloudAccounts\Service\LDAPConnectionService; +use OCP\User\Events\BeforeUserDeletedEvent; +use OCP\User\Events\UserChangedEvent; use OCA\EcloudAccounts\Listeners\UserChangedListener; + class Application extends App implements IBootstrap { @@ -49,10 +53,15 @@ class Application extends App implements IBootstrap public function register(IRegistrationContext $context): void { $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); + $context->registerEventListener(BeforeUserDeletedEvent::class, BeforeUserDeletedListener::class); $context->registerEventListener(UserChangedEvent::class, UserChangedListener::class); } public function boot(IBootContext $context): void { + $serverContainer = $context->getServerContainer(); + $serverContainer->registerService('LDAPConnectionService', function($c) { + return new LDAPConnectionService(); + }); } } diff --git a/lib/Listeners/BeforeUserDeletedListener.php b/lib/Listeners/BeforeUserDeletedListener.php new file mode 100644 index 0000000000000000000000000000000000000000..0ca586cb320a7b5097cd7e11bb2eee0bb37d51d2 --- /dev/null +++ b/lib/Listeners/BeforeUserDeletedListener.php @@ -0,0 +1,76 @@ +logger = $logger; + $this->LDAPConnectionService = $LDAPConnectionService; + } + + + public function handle(Event $event): void + { + if (!($event instanceof BeforeUserDeletedEvent)) { + return; + } + + $user = $event->getUser(); + $email = $user->getEMailAddress(); + $uid = $user->getUID(); + + try { + if ($this->LDAPConnectionService->isLDAPEnabled() && $this->LDAPConnectionService->isUserOnLDAPBackend($user)) { + $conn = $this->LDAPConnectionService->getLDAPConnection($uid); + $this->deleteAliasEntries($conn, $email); + $this->LDAPConnectionService->closeLDAPConnection($conn); + } + } catch (Exception $e) { + $this->logger->error('Error deleting aliases for user '. $uid . ' :' . $e->getMessage()); + } + } + + private function deleteAliasEntries($conn, string $email) + { + $aliasBaseDn = getenv('LDAP_ALIASES_BASE_DN'); + $aliasDns = $this->getAliasEntries($conn, $aliasBaseDn, $email); + foreach ($aliasDns as $aliasDn) { + $deleted = ldap_delete($conn, $aliasDn); + if (!$deleted) { + $this->logger->error('Deleting alias ' . $aliasDn . ' for email ' . $email . ' failed'); + } + } + } + + private function getAliasEntries($conn, string $aliasBaseDn, string $email) : array + { + $filter = "(mailAddress=$email)"; + $aliasEntries = ldap_search($conn, $aliasBaseDn, $filter); + if (!$aliasEntries) { + return []; + } + + $aliasEntries = ldap_get_entries($conn, $aliasEntries); + $aliasEntries = array_filter($aliasEntries, fn ($entry) => is_array($entry)); + $aliasEntries = array_map( + fn ($entry) => $entry['dn'], + $aliasEntries + ); + + return $aliasEntries; + } +} diff --git a/lib/Listeners/UserChangedListener.php b/lib/Listeners/UserChangedListener.php index c62e254159a20e9f7d9cdf5e9303ca42b81977e8..40c782c251c74ca7f787e562f41a624b4fe9b5ff 100644 --- a/lib/Listeners/UserChangedListener.php +++ b/lib/Listeners/UserChangedListener.php @@ -4,25 +4,33 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\Listeners; +use Exception; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Util; +use OCP\ILogger; use OCP\User\Events\UserChangedEvent; use OCA\EcloudAccounts\Db\MailboxMapper; +use OCA\EcloudAccounts\Service\LDAPConnectionService; class UserChangedListener implements IEventListener { - private const QUOTA_FEATURE = 'quota'; private $util; + private $logger; + + private $ldapConnectionService; + private $mailboxMapper; - public function __construct(Util $util, MailboxMapper $mailboxMapper) + public function __construct(Util $util, LDAPConnectionService $LDAPConnectionService, ILogger $logger, MailboxMapper $mailboxMapper) { $this->util = $util; + $this->ldapConnectionService = $LDAPConnectionService; $this->mailboxMapper = $mailboxMapper; + $this->logger = $logger; } public function handle(Event $event): void @@ -36,12 +44,38 @@ class UserChangedListener implements IEventListener if ($feature !== self::QUOTA_FEATURE) { return; } - $user = $event->getUser(); $username = $user->getUID(); $updatedQuota = $event->getValue(); - $quotaInBytes = (int) $this->util->computerFileSize($updatedQuota); - $this->mailboxMapper->updateMailboxQuota($username, $quotaInBytes); + $backend = $user->getBackend()->getBackendName(); + + try { + if ($backend === 'SQL raw') { + $this->mailboxMapper->updateMailboxQuota($username, $quotaInBytes); + } + if ($backend === 'LDAP') { + $this->updateQuotaInLDAP($username, $quotaInBytes); + } + } catch (Exception $e) { + $this->logger->error("Error setting quota for user $username " . $e->getMessage()); + } + } + + private function updateQuotaInLDAP(string $username, int $quota) + { + if ($this->ldapConnectionService->isLDAPEnabled()) { + $conn = $this->ldapConnectionService->getLDAPConnection(); + $userDn = $this->ldapConnectionService->username2dn($username); + + $entry = [ + 'quota' => $quota + ]; + + if (!ldap_modify($conn, $userDn, $entry)) { + throw new Exception('Could not modify user entry at LDAP server!'); + } + $this->ldapConnectionService->closeLDAPConnection($conn); + } } } diff --git a/lib/Listeners/UserDeletedListener.php b/lib/Listeners/UserDeletedListener.php index 25e98359fec52d0c6c2326f914aa438fa0e872c6..6e63f56838fe24a6429e48589ec446014b05d5ea 100644 --- a/lib/Listeners/UserDeletedListener.php +++ b/lib/Listeners/UserDeletedListener.php @@ -5,39 +5,47 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\Listeners; use Curl; +use Exception; use OCA\EcloudAccounts\AppInfo\Application; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\IConfig; use OCP\ILogger; use OCP\User\Events\UserDeletedEvent; +use OCA\EcloudAccounts\Service\LDAPConnectionService; require_once 'curl.class.php'; class UserDeletedListener implements IEventListener { - private $logger; private $config; + private $ldapConnectionService; - public function __construct(ILogger $logger, IConfig $config) + public function __construct(ILogger $logger, IConfig $config, LDAPConnectionService $LDAPConnectionService) { $this->logger = $logger; $this->config = $config; + $this->ldapConnectionService = $LDAPConnectionService; } + public function handle(Event $event): void { if (!($event instanceof UserDeletedEvent)) { return; } - $uid = $event->getUser()->getUID(); + $user = $event->getUser(); + $uid = $user->getUID(); + $isUserOnLDAP = $this->ldapConnectionService->isUserOnLDAPBackend($user); + $this->logger->info("PostDelete user {user}", array('user' => $uid)); $this->ecloudDelete( $uid, $this->config->getSystemValue('e_welcome_domain'), - $this->config->getSystemValue('e_welcome_secret') + $this->config->getSystemValue('e_welcome_secret'), + $isUserOnLDAP ); } @@ -51,10 +59,13 @@ class UserDeletedListener implements IEventListener * @param $welcomeSecret string generated at ecloud selfhosting install and added as a custom var in NC's config * @return mixed response of the external endpoint */ - public function ecloudDelete(string $userID, string $welcomeDomain, string $welcomeSecret) + public function ecloudDelete(string $userID, string $welcomeDomain, string $welcomeSecret, bool $isUserOnLDAP = false) { - - $postDeleteUrl = "https://" . $welcomeDomain . "/postDelete.php"; + $endpoint = 'postDelete.php'; + if($isUserOnLDAP) { + $endpoint = 'postDeleteLDAP.php'; + } + $postDeleteUrl = "https://" . $welcomeDomain . $endpoint; $curl = new Curl(); /** @@ -62,7 +73,6 @@ class UserDeletedListener implements IEventListener * Handling the non NC part of deletion process */ try { - $headers = array( 'Content-Type: application/json' ); diff --git a/lib/Service/LDAPConnectionService.php b/lib/Service/LDAPConnectionService.php new file mode 100644 index 0000000000000000000000000000000000000000..dc0ee7b284c13ccc310a2011aec178e91510c679 --- /dev/null +++ b/lib/Service/LDAPConnectionService.php @@ -0,0 +1,96 @@ +userManager = $userManager; + $this->getConfigurationFromBackend(); + } + + + private function getConfigurationFromBackend() + { + // We don't actually need user id to get access from backend + $uid = ''; + $backends = $this->userManager->getBackends(); + foreach ($backends as $backend) { + if ($backend->getBackendName() === 'LDAP') { + $this->access = $backend->getLDAPAccess($uid); + $connection = $this->access->getConnection(); + $configuration = $connection->getConfiguration(); + + if ($configuration['ldap_configuration_active']) { + $this->ldapEnabled = true; + $this->configuration = $configuration; + break; + } + } + } + } + + public function isUserOnLDAPBackend($user) { + $backend = $user->getBackend(); + return $backend->getBackendName() === 'LDAP'; + } + + public function isLDAPEnabled() : bool { + return $this->ldapEnabled; + } + + public function username2dn(string $username) { + return $this->access->username2dn($username); + } + + + public function getUserBaseDn() : string + { + if (isset($this->configuration['ldap_base_users'])) { + return $this->configuration['ldap_base_users']; + } + throw new Exception('User Base Dn not set!'); + } + + public function getLDAPConnection() + { + if(!$this->ldapEnabled) { + throw new Exception('LDAP backend is not enabled'); + } + + $adminDn = $this->configuration['ldap_dn']; + $adminPassword = $this->configuration['ldap_agent_password']; + $host = $this->configuration['ldap_host']; + $port = intval($this->configuration['ldap_port']); + + $conn = ldap_connect($host, $port); + ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_bind($conn, $adminDn, $adminPassword); + + if (!$conn) { + throw new Exception('Could not connect to LDAP server!'); + } + return $conn; + } + + public function closeLDAPConnection($conn) : void + { + ldap_close($conn); + } +} diff --git a/lib/Service/UserService.php b/lib/Service/UserService.php index 0b8610a110b02cd5d5edbe868061fb5f2a826376..f32a1256a76475fb73350751e707d549f4bf3a20 100644 --- a/lib/Service/UserService.php +++ b/lib/Service/UserService.php @@ -40,7 +40,22 @@ class UserService public function userExists(string $uid): bool { - return $this->userManager->userExists($uid); + $exists = $this->userManager->userExists($uid); + if ($exists) { + return $exists; + } + + $backends = $this->userManager->getBackends(); + foreach ($backends as $backend) { + if ($backend->getBackendName() === 'LDAP') { + $access = $backend->getLDAPAccess($uid); + $users = $access->fetchUsersByLoginName($uid) ; + if (count($users) > 0) { + $exists = true; + } + } + } + return $exists; } public function getUser(string $uid): ?IUser @@ -75,8 +90,8 @@ class UserService try { $this->config->setUserValue($uid, 'hide-my-email', 'email-aliases', $aliases); return true; - } catch(UnexpectedValueException $e) { + } catch (UnexpectedValueException $e) { return false; - } + } } }