From 0d57babf7ba46003e41125773348e3dbe5538f16 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 12:10:17 +0530 Subject: [PATCH 1/8] Fix users with no email attribute set --- README.md | 35 ++++++ appinfo/info.xml | 1 + lib/Command/FixMissingEmails.php | 186 +++++++++++++++++++++++++++++++ lib/Service/UserService.php | 17 +++ test-fix-emails.sh | 50 +++++++++ 5 files changed, 289 insertions(+) create mode 100644 lib/Command/FixMissingEmails.php create mode 100755 test-fix-emails.sh diff --git a/README.md b/README.md index 58d2499c..e10e0709 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,41 @@ - This plugin calls the postDelete.php script in the /e/ docker-welcome container - The e_welcome_secret is loaded in nextcloud's config file during ecloud-selfhosting installation. +## Fix Missing Email Addresses + +- This app provides a command to fix missing email addresses by querying LDAP and setting them in NextCloud +- The command can be used to resolve issues where TOTP sync to Keycloak fails due to missing email addresses + +### Usage + +```bash +# Fix all users without email addresses (dry run first) +occ ecloud-accounts:fix-missing-emails --dry-run + +# Fix all users without email addresses +occ ecloud-accounts:fix-missing-emails + +# Fix specific users +occ ecloud-accounts:fix-missing-emails --users=user1,user2,user3 + +# Fix specific users with dry run +occ ecloud-accounts:fix-missing-emails --users=user1,user2,user3 --dry-run +``` + +### What it does + +1. Queries the database to find users without email addresses set in NextCloud +2. For each user, checks if they are on the LDAP backend +3. Queries LDAP to get the `mailAddress` attribute +4. Sets only the email address in NextCloud (quota remains unchanged) +5. Provides detailed output of the process and any errors encountered + +### Prerequisites + +- LDAP backend must be properly configured and enabled +- Users must exist in both NextCloud and LDAP +- The `mailAddress` attribute must be set in LDAP for the users + ## Support Please open issues here : https://gitlab.e.foundation/e/backlog/issues diff --git a/appinfo/info.xml b/appinfo/info.xml index 3dbec5c8..2e1d4393 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -28,5 +28,6 @@ OCA\EcloudAccounts\Command\Migrate2FASecrets OCA\EcloudAccounts\Command\MigrateWebmailAddressbooks OCA\EcloudAccounts\Command\MapActiveAttributetoLDAP + OCA\EcloudAccounts\Command\FixMissingEmails diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php new file mode 100644 index 00000000..30dc1606 --- /dev/null +++ b/lib/Command/FixMissingEmails.php @@ -0,0 +1,186 @@ +ldapConnectionService = $ldapConnectionService; + $this->userService = $userService; + $this->dbConnection = $dbConnection; + $this->userManager = $userManager; + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName(Application::APP_ID . ':fix-missing-emails') + ->setDescription('Fixes missing email addresses by querying LDAP and setting them in NextCloud') + ->addOption( + 'users', + null, + InputOption::VALUE_OPTIONAL, + 'comma separated list of users to fix (if not provided, all users without email will be processed)', + '' + ) + ->addOption( + 'dry-run', + null, + InputOption::VALUE_NONE, + 'Show what would be done without making changes' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $this->commandOutput = $output; + $isDryRun = $input->getOption('dry-run'); + $usernameList = $input->getOption('users'); + + if ($isDryRun) { + $this->commandOutput->writeln('DRY RUN MODE - No changes will be made'); + } + + // Get users without email + $usersWithoutEmail = $this->getUsersWithoutEmail($usernameList); + + if (empty($usersWithoutEmail)) { + $this->commandOutput->writeln('No users found without email addresses.'); + return 0; + } + + $this->commandOutput->writeln(sprintf('Found %d users without email addresses.', count($usersWithoutEmail))); + + $successCount = 0; + $errorCount = 0; + + foreach ($usersWithoutEmail as $username) { + try { + $this->fixUserEmail($username, $isDryRun); + $successCount++; + } catch (\Exception $e) { + $this->commandOutput->writeln(sprintf('Error fixing email for user %s: %s', $username, $e->getMessage())); + $errorCount++; + } + } + + $this->commandOutput->writeln(sprintf('Processed %d users successfully, %d errors.', $successCount, $errorCount)); + return 0; + } catch (\Exception $e) { + $this->commandOutput->writeln(sprintf('Error: %s', $e->getMessage())); + return 1; + } + } + + /** + * Get users without email addresses + * + * @param string $usernameList Comma-separated list of usernames (optional) + * @return array Array of usernames without email + */ + private function getUsersWithoutEmail(string $usernameList = ''): array { + $query = $this->dbConnection->getQueryBuilder(); + $query->select('a.uid') + ->from('e_accounts', 'a') + ->leftJoin('a', 'e_preferences', 'p', $query->expr()->andX( + $query->expr()->eq('a.uid', 'p.userid'), + $query->expr()->eq('p.appid', $query->createNamedParameter('settings')), + $query->expr()->eq('p.configkey', $query->createNamedParameter('email')) + )) + ->where($query->expr()->isNull('p.userid')); + + if (!empty($usernameList)) { + $usernames = explode(',', $usernameList); + $usernames = array_map('trim', $usernames); + $placeholders = []; + foreach ($usernames as $i => $username) { + $placeholders[] = $query->createNamedParameter($username, \PDO::PARAM_STR, 'username' . $i); + } + $query->andWhere($query->expr()->in('a.uid', $placeholders)); + } + + $result = $query->execute(); + $users = []; + while ($row = $result->fetch()) { + $users[] = $row['uid']; + } + $result->closeCursor(); + + return $users; + } + + /** + * Fix email address for a specific user + * + * @param string $username The username to fix + * @param bool $isDryRun Whether this is a dry run + * @return void + */ + private function fixUserEmail(string $username, bool $isDryRun = false): void { + $this->commandOutput->writeln(sprintf('Processing user: %s', $username)); + + // Check if user exists and is on LDAP backend + $user = $this->userManager->get($username); + if (!$user) { + throw new \Exception("User not found in NextCloud"); + } + + if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { + throw new \Exception("User is not on LDAP backend"); + } + + // Get LDAP access and fetch user attributes + $ldapAccess = $this->ldapConnectionService->getLDAPAccess(); + $requiredAttributes = $ldapAccess->userManager->getAttributes(); + + // Add mailAddress to required attributes if not already present + if (!in_array('mailAddress', $requiredAttributes)) { + $requiredAttributes[] = 'mailAddress'; + } + + $users = $ldapAccess->fetchUsersByLoginName($username, $requiredAttributes); + + if (empty($users)) { + throw new \Exception("User not found in LDAP"); + } + + $userData = $users[0]; + $mailAddress = $userData['mailAddress'][0] ?? null; + + if (empty($mailAddress)) { + throw new \Exception("No mailAddress found in LDAP for user"); + } + + $this->commandOutput->writeln(sprintf(' Found email in LDAP: %s', $mailAddress)); + + if (!$isDryRun) { + // Set the email address in NextCloud (only email, not quota) + $this->userService->setUserEmailOnly($username, $mailAddress); + $this->commandOutput->writeln(sprintf(' ✓ Email set successfully for user: %s', $username)); + } else { + $this->commandOutput->writeln(sprintf(' Would set email to: %s', $mailAddress)); + } + } +} \ No newline at end of file diff --git a/lib/Service/UserService.php b/lib/Service/UserService.php index 7b8098a9..21464011 100644 --- a/lib/Service/UserService.php +++ b/lib/Service/UserService.php @@ -386,6 +386,23 @@ class UserService { $user->setQuota($quota); } + /** + * Set only the email address for a user without modifying quota. + * + * @param string $uid The unique identifier of the user. + * @param string $mailAddress The email address to set for the user. + * + * @return void + */ + public function setUserEmailOnly(string $uid, string $mailAddress): void { + $user = $this->getUser($uid); + if (is_null($user)) { + throw new Exception("User with username '$uid' not found."); + } + // Set only the email address for the user, leave quota unchanged + $user->setEMailAddress($mailAddress); + } + public function isUsernameTaken(string $username) : bool { $commonServicesURL = $this->apiConfig['commonServicesURL']; $commonApiVersion = $this->apiConfig['commonApiVersion']; diff --git a/test-fix-emails.sh b/test-fix-emails.sh new file mode 100755 index 00000000..0eda1641 --- /dev/null +++ b/test-fix-emails.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Test script for the FixMissingEmails command +# This script demonstrates how to use the new command to fix missing email addresses + +echo "=== Ecloud Accounts - Fix Missing Emails Test Script ===" +echo "" + +# Check if we're in a NextCloud environment +if [ ! -f "occ" ] && [ ! -f "../occ" ]; then + echo "Error: This script should be run from the NextCloud root directory" + echo "Please navigate to your NextCloud installation directory and run this script" + exit 1 +fi + +# Find the occ command +OCC_CMD="" +if [ -f "occ" ]; then + OCC_CMD="./occ" +elif [ -f "../occ" ]; then + OCC_CMD="../occ" +else + echo "Error: Could not find occ command" + exit 1 +fi + +echo "Using OCC command: $OCC_CMD" +echo "" + +# First, let's see what users are missing emails (dry run) +echo "1. Checking for users without email addresses (dry run)..." +echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails --dry-run" +echo "" + +$OCC_CMD ecloud-accounts:fix-missing-emails --dry-run + +echo "" +echo "2. If the dry run looks good, you can run the actual fix:" +echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails" +echo "" + +echo "3. To fix specific users only:" +echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails --users=username1,username2 --dry-run" +echo "" + +echo "4. To see all available commands for this app:" +echo "Command: $OCC_CMD list | grep ecloud-accounts" +echo "" + +echo "=== Test script completed ===" \ No newline at end of file -- GitLab From f2a1fbf069f5722317d57d449009710f281e7743 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 12:14:25 +0530 Subject: [PATCH 2/8] php lint issue resolved --- lib/Command/FixMissingEmails.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php index 30dc1606..230e0018 100644 --- a/lib/Command/FixMissingEmails.php +++ b/lib/Command/FixMissingEmails.php @@ -183,4 +183,4 @@ class FixMissingEmails extends Command { $this->commandOutput->writeln(sprintf(' Would set email to: %s', $mailAddress)); } } -} \ No newline at end of file +} -- GitLab From d133a5506080d1564693f67145243e802f51cfb2 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 12:46:49 +0530 Subject: [PATCH 3/8] updated readme files --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index e10e0709..9ddfa2d3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,6 @@ ## Fix Missing Email Addresses - This app provides a command to fix missing email addresses by querying LDAP and setting them in NextCloud -- The command can be used to resolve issues where TOTP sync to Keycloak fails due to missing email addresses ### Usage @@ -62,12 +61,6 @@ occ ecloud-accounts:fix-missing-emails --users=user1,user2,user3 --dry-run 4. Sets only the email address in NextCloud (quota remains unchanged) 5. Provides detailed output of the process and any errors encountered -### Prerequisites - -- LDAP backend must be properly configured and enabled -- Users must exist in both NextCloud and LDAP -- The `mailAddress` attribute must be set in LDAP for the users - ## Support Please open issues here : https://gitlab.e.foundation/e/backlog/issues -- GitLab From 042f9d37c17ebb2d52ec3f0594d49b2654e61c6c Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 13:13:51 +0530 Subject: [PATCH 4/8] removed e_ prefix from table names --- lib/Command/FixMissingEmails.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php index 230e0018..4ccf6718 100644 --- a/lib/Command/FixMissingEmails.php +++ b/lib/Command/FixMissingEmails.php @@ -103,8 +103,8 @@ class FixMissingEmails extends Command { private function getUsersWithoutEmail(string $usernameList = ''): array { $query = $this->dbConnection->getQueryBuilder(); $query->select('a.uid') - ->from('e_accounts', 'a') - ->leftJoin('a', 'e_preferences', 'p', $query->expr()->andX( + ->from('accounts', 'a') + ->leftJoin('a', 'preferences', 'p', $query->expr()->andX( $query->expr()->eq('a.uid', 'p.userid'), $query->expr()->eq('p.appid', $query->createNamedParameter('settings')), $query->expr()->eq('p.configkey', $query->createNamedParameter('email')) -- GitLab From cd23a36fb4de2aa5c755a56afca4220440ef8392 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 17:00:07 +0530 Subject: [PATCH 5/8] Fix: robust LDAP mailAddress lookup and SQL param bug in FixMissingEmails command --- lib/Command/FixMissingEmails.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php index 4ccf6718..9316625a 100644 --- a/lib/Command/FixMissingEmails.php +++ b/lib/Command/FixMissingEmails.php @@ -114,9 +114,11 @@ class FixMissingEmails extends Command { if (!empty($usernameList)) { $usernames = explode(',', $usernameList); $usernames = array_map('trim', $usernames); + + // Use positional parameters instead of named parameters $placeholders = []; - foreach ($usernames as $i => $username) { - $placeholders[] = $query->createNamedParameter($username, \PDO::PARAM_STR, 'username' . $i); + foreach ($usernames as $username) { + $placeholders[] = $query->createNamedParameter($username); } $query->andWhere($query->expr()->in('a.uid', $placeholders)); } @@ -167,10 +169,18 @@ class FixMissingEmails extends Command { } $userData = $users[0]; - $mailAddress = $userData['mailAddress'][0] ?? null; + // Try to find mailAddress in a case-insensitive way + $mailAddress = null; + foreach ($userData as $key => $value) { + if (strtolower($key) === 'mailaddress' && !empty($value[0])) { + $mailAddress = $value[0]; + break; + } + } if (empty($mailAddress)) { - throw new \Exception("No mailAddress found in LDAP for user"); + $availableKeys = implode(', ', array_keys($userData)); + throw new \Exception("No mailAddress found in LDAP for user. Available keys: $availableKeys"); } $this->commandOutput->writeln(sprintf(' Found email in LDAP: %s', $mailAddress)); -- GitLab From 33d74bbd0f8af2c48077dbfedfa4d11362deaabb Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 17:03:55 +0530 Subject: [PATCH 6/8] simplified Exception and lint php --- lib/Command/FixMissingEmails.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php index 9316625a..ab9e5742 100644 --- a/lib/Command/FixMissingEmails.php +++ b/lib/Command/FixMissingEmails.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\Command; +use Exception; use OCA\EcloudAccounts\AppInfo\Application; use OCA\EcloudAccounts\Service\LDAPConnectionService; use OCA\EcloudAccounts\Service\UserService; @@ -80,7 +81,7 @@ class FixMissingEmails extends Command { try { $this->fixUserEmail($username, $isDryRun); $successCount++; - } catch (\Exception $e) { + } catch (Exception $e) { $this->commandOutput->writeln(sprintf('Error fixing email for user %s: %s', $username, $e->getMessage())); $errorCount++; } @@ -88,7 +89,7 @@ class FixMissingEmails extends Command { $this->commandOutput->writeln(sprintf('Processed %d users successfully, %d errors.', $successCount, $errorCount)); return 0; - } catch (\Exception $e) { + } catch (Exception $e) { $this->commandOutput->writeln(sprintf('Error: %s', $e->getMessage())); return 1; } @@ -146,11 +147,11 @@ class FixMissingEmails extends Command { // Check if user exists and is on LDAP backend $user = $this->userManager->get($username); if (!$user) { - throw new \Exception("User not found in NextCloud"); + throw new Exception("User not found in NextCloud"); } if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { - throw new \Exception("User is not on LDAP backend"); + throw new Exception("User is not on LDAP backend"); } // Get LDAP access and fetch user attributes @@ -165,7 +166,7 @@ class FixMissingEmails extends Command { $users = $ldapAccess->fetchUsersByLoginName($username, $requiredAttributes); if (empty($users)) { - throw new \Exception("User not found in LDAP"); + throw new Exception("User not found in LDAP"); } $userData = $users[0]; @@ -180,7 +181,7 @@ class FixMissingEmails extends Command { if (empty($mailAddress)) { $availableKeys = implode(', ', array_keys($userData)); - throw new \Exception("No mailAddress found in LDAP for user. Available keys: $availableKeys"); + throw new Exception("No mailAddress found in LDAP for user. Available keys: $availableKeys"); } $this->commandOutput->writeln(sprintf(' Found email in LDAP: %s', $mailAddress)); -- GitLab From 3d33262c53ec43970eec33c1044f1e7791500a6a Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 17:05:52 +0530 Subject: [PATCH 7/8] removed test-fix sh file --- test-fix-emails.sh | 50 ---------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100755 test-fix-emails.sh diff --git a/test-fix-emails.sh b/test-fix-emails.sh deleted file mode 100755 index 0eda1641..00000000 --- a/test-fix-emails.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Test script for the FixMissingEmails command -# This script demonstrates how to use the new command to fix missing email addresses - -echo "=== Ecloud Accounts - Fix Missing Emails Test Script ===" -echo "" - -# Check if we're in a NextCloud environment -if [ ! -f "occ" ] && [ ! -f "../occ" ]; then - echo "Error: This script should be run from the NextCloud root directory" - echo "Please navigate to your NextCloud installation directory and run this script" - exit 1 -fi - -# Find the occ command -OCC_CMD="" -if [ -f "occ" ]; then - OCC_CMD="./occ" -elif [ -f "../occ" ]; then - OCC_CMD="../occ" -else - echo "Error: Could not find occ command" - exit 1 -fi - -echo "Using OCC command: $OCC_CMD" -echo "" - -# First, let's see what users are missing emails (dry run) -echo "1. Checking for users without email addresses (dry run)..." -echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails --dry-run" -echo "" - -$OCC_CMD ecloud-accounts:fix-missing-emails --dry-run - -echo "" -echo "2. If the dry run looks good, you can run the actual fix:" -echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails" -echo "" - -echo "3. To fix specific users only:" -echo "Command: $OCC_CMD ecloud-accounts:fix-missing-emails --users=username1,username2 --dry-run" -echo "" - -echo "4. To see all available commands for this app:" -echo "Command: $OCC_CMD list | grep ecloud-accounts" -echo "" - -echo "=== Test script completed ===" \ No newline at end of file -- GitLab From 03dcb1f6b1e5d97b92dbc2e37f477914bda08660 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 30 Jun 2025 23:30:29 +0530 Subject: [PATCH 8/8] removed explode, created new function in LDAPService and other changes --- README.md | 6 +-- lib/Command/FixMissingEmails.php | 53 ++++++--------------------- lib/Service/LDAPConnectionService.php | 39 ++++++++++++++++++++ lib/Service/UserService.php | 3 +- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 9ddfa2d3..a81343a3 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ occ ecloud-accounts:fix-missing-emails --dry-run # Fix all users without email addresses occ ecloud-accounts:fix-missing-emails -# Fix specific users -occ ecloud-accounts:fix-missing-emails --users=user1,user2,user3 +# Fix specific users (multiple --users arguments) +occ ecloud-accounts:fix-missing-emails --users=user1 --users=user2 --users=user3 # Fix specific users with dry run -occ ecloud-accounts:fix-missing-emails --users=user1,user2,user3 --dry-run +occ ecloud-accounts:fix-missing-emails --users=user1 --users=user2 --dry-run ``` ### What it does diff --git a/lib/Command/FixMissingEmails.php b/lib/Command/FixMissingEmails.php index ab9e5742..98734c4b 100644 --- a/lib/Command/FixMissingEmails.php +++ b/lib/Command/FixMissingEmails.php @@ -42,9 +42,9 @@ class FixMissingEmails extends Command { ->addOption( 'users', null, - InputOption::VALUE_OPTIONAL, - 'comma separated list of users to fix (if not provided, all users without email will be processed)', - '' + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Users to fix (can be specified multiple times, e.g., --users=user1 --users=user2)', + [] ) ->addOption( 'dry-run', @@ -58,14 +58,14 @@ class FixMissingEmails extends Command { try { $this->commandOutput = $output; $isDryRun = $input->getOption('dry-run'); - $usernameList = $input->getOption('users'); + $usernames = $input->getOption('users'); if ($isDryRun) { $this->commandOutput->writeln('DRY RUN MODE - No changes will be made'); } // Get users without email - $usersWithoutEmail = $this->getUsersWithoutEmail($usernameList); + $usersWithoutEmail = $this->getUsersWithoutEmail($usernames); if (empty($usersWithoutEmail)) { $this->commandOutput->writeln('No users found without email addresses.'); @@ -98,10 +98,10 @@ class FixMissingEmails extends Command { /** * Get users without email addresses * - * @param string $usernameList Comma-separated list of usernames (optional) + * @param array $usernames Array of usernames to check (if empty, all users without email will be processed) * @return array Array of usernames without email */ - private function getUsersWithoutEmail(string $usernameList = ''): array { + private function getUsersWithoutEmail(array $usernames = []): array { $query = $this->dbConnection->getQueryBuilder(); $query->select('a.uid') ->from('accounts', 'a') @@ -112,11 +112,7 @@ class FixMissingEmails extends Command { )) ->where($query->expr()->isNull('p.userid')); - if (!empty($usernameList)) { - $usernames = explode(',', $usernameList); - $usernames = array_map('trim', $usernames); - - // Use positional parameters instead of named parameters + if (!empty($usernames)) { $placeholders = []; foreach ($usernames as $username) { $placeholders[] = $query->createNamedParameter($username); @@ -154,42 +150,17 @@ class FixMissingEmails extends Command { throw new Exception("User is not on LDAP backend"); } - // Get LDAP access and fetch user attributes - $ldapAccess = $this->ldapConnectionService->getLDAPAccess(); - $requiredAttributes = $ldapAccess->userManager->getAttributes(); - - // Add mailAddress to required attributes if not already present - if (!in_array('mailAddress', $requiredAttributes)) { - $requiredAttributes[] = 'mailAddress'; - } - - $users = $ldapAccess->fetchUsersByLoginName($username, $requiredAttributes); - - if (empty($users)) { - throw new Exception("User not found in LDAP"); - } - - $userData = $users[0]; - // Try to find mailAddress in a case-insensitive way - $mailAddress = null; - foreach ($userData as $key => $value) { - if (strtolower($key) === 'mailaddress' && !empty($value[0])) { - $mailAddress = $value[0]; - break; - } - } + $mailAddress = $this->ldapConnectionService->getUserMailAddress($username); if (empty($mailAddress)) { - $availableKeys = implode(', ', array_keys($userData)); - throw new Exception("No mailAddress found in LDAP for user. Available keys: $availableKeys"); + throw new Exception("No mailAddress found in LDAP for user"); } $this->commandOutput->writeln(sprintf(' Found email in LDAP: %s', $mailAddress)); if (!$isDryRun) { - // Set the email address in NextCloud (only email, not quota) - $this->userService->setUserEmailOnly($username, $mailAddress); - $this->commandOutput->writeln(sprintf(' ✓ Email set successfully for user: %s', $username)); + $this->userService->setUserEmail($username, $mailAddress); + $this->commandOutput->writeln(sprintf(' Email set successfully for user: %s', $username)); } else { $this->commandOutput->writeln(sprintf(' Would set email to: %s', $mailAddress)); } diff --git a/lib/Service/LDAPConnectionService.php b/lib/Service/LDAPConnectionService.php index f7609941..b63fb339 100644 --- a/lib/Service/LDAPConnectionService.php +++ b/lib/Service/LDAPConnectionService.php @@ -122,4 +122,43 @@ class LDAPConnectionService { $this->closeLDAPConnection($conn); } + + /** + * Get mailAddress for a user directly from LDAP + * + * @param string $username The username to search for + * @return string|null The mailAddress or null if not found + * @throws Exception If LDAP is not enabled or user not found + */ + public function getUserMailAddress(string $username): ?string { + if (!$this->isLDAPEnabled()) { + throw new Exception('LDAP backend is not enabled'); + } + + $conn = $this->getLDAPConnection(); + $userDn = $this->username2dn($username); + + if ($userDn === false) { + throw new Exception('Could not find DN for username: ' . $username); + } + + $result = ldap_read($conn, $userDn, '(objectClass=*)', ['mailAddress']); + + if (!$result) { + $this->closeLDAPConnection($conn); + throw new Exception('Could not read user entry from LDAP for username: ' . $username); + } + + $entries = ldap_get_entries($conn, $result); + $this->closeLDAPConnection($conn); + + if ($entries['count'] === 0) { + throw new Exception('User not found in LDAP: ' . $username); + } + + $entry = $entries[0]; + $mailAddress = $entry['mailaddress'][0] ?? null; + + return $mailAddress; + } } diff --git a/lib/Service/UserService.php b/lib/Service/UserService.php index 21464011..1cd8fece 100644 --- a/lib/Service/UserService.php +++ b/lib/Service/UserService.php @@ -394,12 +394,11 @@ class UserService { * * @return void */ - public function setUserEmailOnly(string $uid, string $mailAddress): void { + public function setUserEmail(string $uid, string $mailAddress): void { $user = $this->getUser($uid); if (is_null($user)) { throw new Exception("User with username '$uid' not found."); } - // Set only the email address for the user, leave quota unchanged $user->setEMailAddress($mailAddress); } -- GitLab