diff --git a/lib/LDAPUserManager.php b/lib/LDAPUserManager.php index 98e4824a..e83bd990 100644 --- a/lib/LDAPUserManager.php +++ b/lib/LDAPUserManager.php @@ -43,6 +43,7 @@ use OCP\LDAP\IDeletionFlagSupport; use OCP\LDAP\ILDAPProvider; use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\GenericEvent; class LDAPUserManager implements ILDAPUserPlugin { /** @var ILDAPProvider */ @@ -74,6 +75,10 @@ public function __construct(IUserManager $userManager, IUserSession $userSession $this->logger = $logger; $this->userManager->listen('\OC\User', 'changeUser', [$this, 'changeUserHook']); + if ($this->configuration->canUpdateContactInfo()) { + $eventDispatcher = \OC::$server->getEventDispatcher(); + $eventDispatcher->addListener('OC\AccountManager::userUpdated', [$this, 'userUpdatedHook']); + } $this->makeLdapBackendFirst(); } @@ -441,6 +446,96 @@ public function changeUserHook(IUser $user, string $feature, $attr1, $attr2): vo } } + public function userUpdatedHook(GenericEvent $event): void { + $user = $event->getSubject(); + $args = $event->getArguments(); + + try { + $userDN = $this->getUserDN($user->getUID()); + } catch (Exception $e) { + return; + } + + $connection = $this->ldapProvider->getLDAPConnection($user->getUID()); + + if (!is_resource($connection) && !is_object($connection)) { + $this->logger->debug('LDAP resource not available', ['app' => 'ldap_write_support']); + throw new ServerNotAvailableException('LDAP server is not available'); + } + + try { + $updates = array(); + $urls = array(); + $deletes = array(); + foreach ($args as $arg) { + // `displayname` and `email` also exist and could replace above until + // non-Symfony calls are standardized. + // `headline`, `twitter`, and `fediverse` are also available. + switch ($arg['name']) { + case 'phone': + if ($arg['value'] == "") { + $deletes['telephoneNumber'] = array(); + } else { + $updates['telephoneNumber'] = $arg['value']; + } + break; + case 'address': + if ($arg['value'] == "") { + $deletes['l'] = array(); + } else { + $updates['l'] = $arg['value']; + } + break; + case 'website': + if ($arg['value'] == "") { + $deletes['wWWHomePage'] = array(); + } else { + $updates['wWWHomePage'] = $arg['value']; + } + break; + case 'organisation': + if ($arg['value'] == "") { + $deletes['department'] = array(); + } else { + $updates['department'] = $arg['value']; + } + break; + case 'role': + if ($arg['value'] == "") { + $deletes['title'] = array(); + } else { + $updates['title'] = $arg['value']; + } + break; + case 'biography': + if ($arg['value'] == "") { + $deletes['description'] = array(); + } else { + $updates['description'] = $arg['value']; + } + break; + } + } + if (ldap_mod_replace($connection, $userDN, $updates)) { + if (count($deletes) > 0) { + try { + @ldap_mod_del($connection, $userDN, $deletes); + } catch(Exception $e) { + // Nothing required + } + } + return; + } + throw new HintException('Failed to set LDAP information'); + } catch (ConstraintViolationException $e) { + throw new HintException( + $e->getMessage(), + $this->l10n->t('LDAP change rejected'), + $e->getCode() + ); + } + } + private function getUserDN($uid): string { return $this->ldapProvider->getUserDN($uid); } diff --git a/lib/Service/Configuration.php b/lib/Service/Configuration.php index 2bd7b9d8..4dee523f 100644 --- a/lib/Service/Configuration.php +++ b/lib/Service/Configuration.php @@ -48,6 +48,10 @@ public function hasAvatarPermission(): bool { return $this->config->getAppValue('ldap_write_support', 'hasAvatarPermission', '1') === '1'; } + public function canUpdateContactInfo(): bool { + return $this->config->getAppValue('ldap_write_support', 'canUpdateContactInfo', '1') === '1'; + } + public function getUserTemplate() { return $this->config->getAppValue( Application::APP_ID, diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 99612e5f..a49e50f3 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -61,6 +61,7 @@ public function getForm() { 'createRequireActorFromLdap' => $this->config->isLdapActorRequired(), 'createPreventFallback' => $this->config->isPreventFallback(), 'hasAvatarPermission' => $this->config->hasAvatarPermission(), + 'canUpdateContactInfo' => $this->config->canUpdateContactInfo(), 'newUserRequireEmail' => $this->config->isRequireEmail(), 'newUserGenerateUserID' => $this->config->isGenerateUserId(), ] diff --git a/src/components/AdminSettings.vue b/src/components/AdminSettings.vue index 3dd91a39..faafce7b 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -44,6 +44,10 @@ @change.stop.prevent="toggleSwitch('hasAvatarPermission', !switches.hasAvatarPermission)"> {{ t('ldap_write_support', 'Allow users to set their avatar') }} + + {{ t('ldap_write_support', 'Update LDAP/AD when contact info changes') }} +

{{ t('ldap_write_support', 'User template') }}

{{ t('ldap_write_support', 'LDIF template for creating users. Following placeholders may be used') }}