Commit e9cdc983 authored by Arnau Vàzquez's avatar Arnau Vàzquez
Browse files

Merge branch 'ldap' into 'nc22'

Check if user exists using LDAP backend method

See merge request e/infra/ecloud/nextcloud-apps/ecloud-drop-account!14
parents f1c81430 6f5c7bd0
......@@ -10,7 +10,7 @@
<description><![CDATA[in /e/ cloud self-hosting setup, nextcloud accounts are linked to mail accounts and some other things. This app sets the mail, quota and storage of the user upon creation.
It also completes the account deletion by cleaning other parts of the /e/ cloud setup to ensure no more data is retained when a user requests an account deletion.
This app uses the UserDeletedEvent to invoke scripts in the docker-welcome container of /e/ cloud setup]]></description>
<version>1.2.0</version>
<version>2.0.0</version>
<licence>agpl</licence>
<author mail="dev@e.email" homepage="https://gitlab.e.foundation/">Akhil Potukuchi</author>
<namespace>EcloudAccounts</namespace>
......
......@@ -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();
});
}
}
<?php
declare(strict_types=1);
namespace OCA\EcloudAccounts\Listeners;
use Exception;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\ILogger;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCA\EcloudAccounts\Service\LDAPConnectionService;
class BeforeUserDeletedListener implements IEventListener
{
private $logger;
private $LDAPConnectionService;
public function __construct(ILogger $logger, LDAPConnectionService $LDAPConnectionService)
{
$this->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;
}
}
......@@ -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);
}
}
}
......@@ -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'
);
......
<?php
declare(strict_types=1);
namespace OCA\EcloudAccounts\Service;
use Exception;
use OCP\IUserManager;
class LDAPConnectionService
{
/** @var IUserManager */
private $userManager;
private $configuration;
private $ldapEnabled;
private $access;
public function __construct(IUserManager $userManager)
{
$this->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);
}
}
......@@ -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;
}
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment