Skip to content

Commit af5d2e3

Browse files
[FIX] LDAP: Support failover during authentication process
See: https://mantis.ilias.de/view.php?id=20651
1 parent 955e7ac commit af5d2e3

File tree

5 files changed

+63
-14
lines changed

5 files changed

+63
-14
lines changed

components/ILIAS/LDAP/classes/class.ilAuthProviderLDAP.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ protected function updateAccount(ilAuthStatus $status, array $user): bool
149149
protected function initServer(int $a_server_id): void
150150
{
151151
$this->server = new ilLDAPServer($a_server_id);
152+
$this->server->doConnectionCheck(true);
152153
}
153154

154155
/**

components/ILIAS/LDAP/classes/class.ilLDAPServer.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,15 @@ class ilLDAPServer
8181
private ilDBInterface $db;
8282
private ilLanguage $lng;
8383
private ilErrorHandling $ilErr;
84+
private ilLogger $logger;
8485

8586
public function __construct(int $a_server_id = 0)
8687
{
8788
global $DIC;
8889

8990
$this->db = $DIC->database();
9091
$this->lng = $DIC->language();
92+
$this->logger = $DIC->logger()->auth();
9193
$this->ilErr = $DIC['ilErr'];
9294

9395
$this->server_id = $a_server_id;
@@ -525,22 +527,49 @@ public function getUrlString(): string
525527
* @access public
526528
*
527529
*/
528-
public function doConnectionCheck(): bool
530+
public function doConnectionCheck(bool $prevent_persisted_rotation = false): bool
529531
{
530-
foreach (array_merge(array(0 => $this->url), $this->fallback_urls) as $url) {
532+
$connection_failures = [];
533+
foreach (array_merge([0 => $this->url], $this->fallback_urls) as $url) {
531534
try {
532-
ilLoggerFactory::getLogger('auth')->debug('Using url: ' . $url);
535+
$this->logger->debug('Attempting LDAP connection to: {url}', ['url' => $url]);
536+
533537
// Need to do a full bind, since openldap return valid connection links for invalid hosts
534538
$query = new ilLDAPQuery($this, $url);
535539
$query->bind(ilLDAPQuery::LDAP_BIND_TEST);
536540
$this->url = $url;
541+
542+
if ($connection_failures !== []) {
543+
$this->logger->info(
544+
'Successfully connected to LDAP server: {url} after {failures} failed attempts',
545+
[
546+
'url' => $url,
547+
'failures' => count($connection_failures)
548+
]
549+
);
550+
}
551+
537552
return true;
538553
} catch (ilLDAPQueryException $exc) {
539-
$this->rotateFallbacks();
540-
ilLoggerFactory::getLogger('auth')->error('Cannot connect to LDAP server: ' . $url . ' ' . $exc->getCode() . ' ' . $exc->getMessage());
554+
$connection_failures[] = $url;
555+
556+
$this->logger->error('LDAP connection failed for server: {url} - {message}', [
557+
'url' => $url,
558+
'message' => $exc->getMessage(),
559+
'exception' => $exc
560+
]);
561+
562+
if (!$prevent_persisted_rotation) {
563+
$this->rotateFallbacks();
564+
}
541565
}
542566
}
543-
ilLoggerFactory::getLogger('auth')->warning('No valid LDAP server found');
567+
568+
$this->logger->warning('No valid LDAP server found. Tried {count} server(s)', [
569+
'count' => count($connection_failures),
570+
'urls' => implode(', ', $connection_failures)
571+
]);
572+
544573
return false;
545574
}
546575

components/ILIAS/LDAP/tests/ilLDAPServerTest.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@
2727
*/
2828
class ilLDAPServerTest extends TestCase
2929
{
30-
private Container $dic;
30+
private ?Container $dic = null;
3131

3232
protected function setUp(): void
3333
{
34-
$this->dic = new Container();
35-
$GLOBALS['DIC'] = $this->dic;
34+
global $DIC;
35+
36+
$this->dic = is_object($DIC) ? clone $DIC : $DIC;
37+
38+
$DIC = new Container();
3639

3740
$this->setGlobalVariable('lng', $this->getLanguageMock());
3841
$this->setGlobalVariable(
@@ -48,9 +51,27 @@ protected function setUp(): void
4851
'ilErr',
4952
$this->getMockBuilder(ilErrorHandling::class)->getMock()
5053
);
54+
55+
$logger = $this->getMockBuilder(ilLogger::class)->disableOriginalConstructor()->getMockForAbstractClass();
56+
$logger_factory = $this->getMockBuilder(ilLoggerFactory::class)->disableOriginalConstructor()->getMock();
57+
$logger_factory->method('getComponentLogger')->willReturn($logger);
58+
$this->setGlobalVariable(
59+
ilLoggerFactory::class,
60+
$logger_factory
61+
);
62+
5163
parent::setUp();
5264
}
5365

66+
protected function tearDown(): void
67+
{
68+
global $DIC;
69+
70+
$DIC = $this->dic;
71+
72+
parent::tearDown();
73+
}
74+
5475
/**
5576
* @param string $name
5677
* @param mixed $value
@@ -62,9 +83,7 @@ protected function setGlobalVariable(string $name, $value): void
6283
$GLOBALS[$name] = $value;
6384

6485
unset($DIC[$name]);
65-
$DIC[$name] = static function ($c) use ($name) {
66-
return $GLOBALS[$name];
67-
};
86+
$DIC[$name] = static fn(Container $c) => $GLOBALS[$name];
6887
}
6988

7089
/**

lang/ilias_de.lang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10874,7 +10874,7 @@ ldap#:#ldap_server_name#:#Name der LDAP-Konfiguration
1087410874
ldap#:#ldap_server_name_info#:#Bitte wählen Sie einen Namen für diese Server-Konfiguration!
1087510875
ldap#:#ldap_server_security_settings#:#Sicherheitseinstellungen
1087610876
ldap#:#ldap_server_short#:#Server URL:
10877-
ldap#:#ldap_server_url_info#:#Die komplette URL für die Verbindung zum LDAP-Server. Z.B. "ldap://ldaps.ilias.de:636".
10877+
ldap#:#ldap_server_url_info#:#Bitte tragen Sie die komplette URL für die Verbindung zum LDAP-Server ein, z.B. "ldap://ldaps.ilias.de:636". Mehrere URLs können kommagetrennt für Failover-Unterstützung angegeben werden. Bei Verbindungsfehlern rotiert ILIAS automatisch durch die verfügbaren Server. Hinweis: Während der Authentifizierung erfolgt die URL-Rotation nur im Arbeitsspeicher (nicht persistiert). In anderen Kontexten (z.B. Synchronisierungsaufgaben) wird eine erfolgreiche Rotation in der Datenbank gespeichert.
1087810878
ldap#:#ldap_server_version_info#:#Protokollversion des LDAP-Server (in den meisten Fällen "LDAPv3").
1087910879
ldap#:#ldap_servers#:#LDAP Server
1088010880
ldap#:#ldap_settings#:#Server-Einstellungen

lang/ilias_en.lang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10875,7 +10875,7 @@ ldap#:#ldap_server_name#:#Name of LDAP Configuration
1087510875
ldap#:#ldap_server_name_info#:#Please choose a name for this LDAP server configuration.
1087610876
ldap#:#ldap_server_security_settings#:#Security Settings
1087710877
ldap#:#ldap_server_short#:#Server URL:
10878-
ldap#:#ldap_server_url_info#:#A fully qualified URL for specifying the protocol, url and port to connect to. E.g "ldaps://ldap.ilias.de:636".
10878+
ldap#:#ldap_server_url_info#:#Please enter a fully qualified URL for specifying the protocol, url and port to connect to, e.g "ldaps://ldap.ilias.de:636". Multiple URLs can be comma-separated for failover support. In case of connection failures, ILIAS will automatically rotate through the available servers. Note: During authentication, URL rotation is performed in-memory only (not persisted). In other contexts (e.g., synchronization tasks), each failed connection will rotate the URL order and persist it to the database, helping future requests avoid recently-failed servers.
1087910879
ldap#:#ldap_server_version_info#:#LDAP version to use, usually 3.
1088010880
ldap#:#ldap_servers#:#LDAP Server
1088110881
ldap#:#ldap_settings#:#Server Settings

0 commit comments

Comments
 (0)