LDAP is available and not available at the same time

Operating system: Linux 5.15.0-67-generic #74-Ubuntu SMP Wed Feb 22 14:14:39 UTC 2023 x86_64
Webserver: Webserver Apache2
Database: pgsql PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
PHP version: 8.1.27
Nextcloud version: 27.1.6 - 27.1.6.2

Hi all. I have done some mess and can not find a way out.

It is about LDAP integration, which worked perfectly for years for a small installation with some few users. Now a new user was added to LDAP and it screwed up. (So I can’t even say, if it is some mess I did or if it was caused by updates.)

I have issues with the file representation in the Webbrowser on some accounts as well. But when I am able to see a stack trace with occ files:scan <uid> I get some:

Error during scan: ldap_read(): Search: No such object
Error during scan: ldap_read(): Search: No such object
Error during scan: ldap_read(): Search: No such object
Error during scan: ldap_read(): Search: No such object
Error during scan: ldap_read(): Search: No such object
Error during scan: ldap_read(): Search: No such object

So that I focus on LDAP in my issue report.

As it now looks like, for some reason in the LDAP web interface (LDAP/AD-Integration) from Nextcloud the whole LDAP integration seems to work properly. All tests run smooth and I can also test all respective user accounts with their uid positiv:

Benutzer gefunden und Einstellungen überprüft.

NC resolves some LDAP information. I proved this with changing Feld für den Anzeigenamen des Benutzers between uid and cn. Then the existing users show respecively either the login (uid) or the cn (readable name) in NC’s Users panel. So somehow the integration with LDAP works.

But:

  • A new user can not log in and the account does not appear in the Users list from NC.
  • Existing users seem to be forgotten by NC inregards to LDAP. Using occ for any LDAP user gives me:
> occ ldap:check-user testuser1
The user does not exists on LDAP anymore.
Clean up the user's remnants by: ./occ user:delete "testuser1"
  • For the user existing from before the issue I get:
> occ ldap:search testuser1
testuser1 (testuser1)
>
  • For new users existing from before the issue I get nothing in return:
> occ ldap:search testuser2
>

I guess, that something with the cached LDAP information seems not no match. And depending on where NC looks into, it finds the user in the cache or not. And some methods just go directly to the LDAP itself and get the results.
So I guess syncing does not work for now.

After spending the whole yesterday, messing around with LDAP settings and trying to understand, how the database tables are referring each other, I do not have clue, where to fix it.

For instance:

  • How is the oc_ldap_user_mapping working? Could it be, that some changed response from the LDAP server leads to a changed hash and so my users lost the connection to its home LDAP?
  • Is there a way to re-sync users to its LDAP? (I saw another thread, that linked a previous local user to an LDAP user juggling with the ‘LDAP’-referring and ‘local user’-referring tables in the database.)
  • I have a lot of rows in my oc_ldap_user_mapping table. Only a few of them really exist in the LDAP server. The others are artifacts from tests a long long time ago. Is there any process that clears or repairs the NC/LDAP dependencies?
  • If anything goes wrong during LDAP queries, how can I read logs and error messages, that NC experiences. The log file at data/nextcloud.log stays almost empty even with loglevel 4 in config.php and the log reader user interface in NC also does not show any issue. So: After Yak-shaving (Schnitzeljagd) still it is silently failing.
  • Did anyone experiance that LDAP only works partial? No persistent sync but some ephemeral live glimps into LDAP?

So happy for any hint. Thank you in advance.

While continuing to work on it, I created a small script, that can delete based on an output of the occ ldap:show-remnants. You can pipe the output of occ into text file and remove the lines (accounts), you do not want to delete:

#!/bin/bash

# Pfad zur Liste
liste="20240216.Nextcloud-Löschliste.txt"

# Überprüfen, ob die Liste existiert
if [ ! -f "$liste" ]; then
    echo "Die Liste $liste existiert nicht."
    exit 1
fi

# Schleife zum Lesen der Liste zeilenweise
while IFS= read -r line; do
   # Inkrementieren des Zählers
    ((count++))

    # Die ersten drei Zeilen überspringen
    if [ "$count" -le 3 ]; then
        continue
    fi

    # String aus erster Spalte extrahieren
    nextcloud_name=$(echo "$line" | awk -F '|' '{print $2}' | tr -d '[:space:]')

    # Überprüfen, ob die extrahierte Spalte leer ist
    if [ -z "$nextcloud_name" ]; then
        continue
    fi

    # Ausgabe des Strings aus erster Spalte
    echo "$nextcloud_name"
    sudo -u www-data php /home/nextcloud/nextcloud/occ user:delete $nextcloud_name
    # exit
done < "$liste"

(Feel free to use and extend this scrip, but on your own risk.)

That way I wanted to reduce the number of zombi accounts. But the effect still persists even after clearing known unused accounts.

…going down the rabbit hole.
It seems, that basically the LDAP response from the server is valid, but the PHP code discards it for some reason.

If in CheckUser.php I inject some $exist = true I get a complete result from the LDAP server:

        protected function execute(InputInterface $input, OutputInterface $output): int {
                try {
                        $this->assertAllowed($input->getOption('force'));
                        $uid = $input->getArgument('ocName');
                        if ($this->backend->getLDAPAccess($uid)->stringResemblesDN($uid)) {
                                $username = $this->backend->dn2UserName($uid);
                                if ($username !== false) {
                                        $uid = $username;
                                }
                        }
                        $wasMapped = $this->userWasMapped($uid);
                        $exists = $this->backend->userExistsOnLDAP($uid, true);
                        $exists = true;

Now I am trying to find out, why userExistsOnLDAP() returns a false.

It seems that the call to $this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter); in User_LDAP.php does not return any data.

The same query done with ldapsearch returns all the expected data.

In Access.php i replaced the following row (old one behind comment:

                # $rr = @$this->invokeLDAPMethod('read', $dn, $filter, [$attribute]);
                $rr = @$this->invokeLDAPMethod('search', $dn, $filter, [$attribute]);

That means with a search it works, but with a read it won’t.

This is a major change and I can not distinguish what the sideeffects could be.

I can say, that whenever I see this call, the $dn is full qualified and will return only the single record that I want.