alextselegidis/easyappointments

LDAP issues

Closed this issue · 1 comments

After some tests with 1.5.0-alpha.1 I noticed the following problems with LDAP:

  • my LDAP doesn't return any passwords for security reasons, so the login always fails
  • ldap_connect() with host and port is deprecated in PHP 8.3
  • the warning prevents login (as described in issue #1540 )

I would recommend a "rebind" with the login user, then the LDAP-DN field can be omitted in the user management.
I use the host field for the URI (ldaps://...), the port field can be omitted, too.

I have adapted the check_login() for us like this, it would be great if you could find it useful and use it:

    public function check_login(string $username, string $password): ?array
    {
        if (empty($username)) {
            throw new InvalidArgumentException('No username value provided.');
        }

        $ldap_is_active = setting('ldap_is_active');

        if (!$ldap_is_active) {
            return null;
        }

        // Check LDAP environment and configuration

        if (!extension_loaded('ldap')) {
            throw new RuntimeException('The LDAP extension is not loaded.');
        }

        // Match user by username

        $user = $this->CI->accounts->get_user_by_username($username);

        if (empty($user['id'])) {
			// user does not exist in easyappointments
			return null;
		}

        // connect to LDAP server
        $uri= setting('ldap_host');
        $bind_dn = setting('ldap_user_dn');
        $bind_password = setting('ldap_password');
        $base_dn = setting('ldap_base_dn');
        $filter = setting('ldap_filter');

	$connection = ldap_connect($uri);

        if (!$connection) {
            throw new Exception('Could not connect to LDAP server: ' . ldap_error($connection));
        }

        ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($connection, LDAP_OPT_REFERRALS, 0); // We need this for doing an LDAP search.

        $bind = ldap_bind($connection, $bind_dn, $bind_password);
        if (!$bind) {
            throw new Exception('LDAP bind failed: ' . ldap_error($connection));
        }

        // search user against the LDAP service
		$filter = str_replace('{{KEYWORD}}', $username, $filter) ;

		$result = ldap_search($connection, $base_dn, $filter);
		if (!$result) {
			return null;
		}

		$ldap_entry = ldap_first_entry($connection, $result);

        if ($ldap_entry) {
            $userDn = ldap_get_dn($connection, $ldap_entry);
            $userBind = ldap_bind($connection, $userDn, $password);
            if ($userBind) {
                $role = $this->CI->roles_model->find($user['id_roles']);

                $default_timezone = $this->CI->timezones->get_default_timezone();

                return [
                    'user_id' => $user['id'],
                    'user_email' => $user['email'],
                    'username' => $username,
                    'timezone' => !empty($user['timezone']) ? $user['timezone'] : $default_timezone,
                    'language' => !empty($user['language']) ? $user['language'] : Config::LANGUAGE,
                    'role_slug' => $role['slug'],
                ];
			}
		}
        return null;
    }

Hello!

Great, thanks for submitting this solution.

I adapted it to the app, but kept the "ldap_dn" value, as this will allow developers and admins sync users in different ways (e.g. via API, via SQL or even manually).

Apart from that it works like a charm 🚀

Alex Tselegidis, Easy!Appointments Creator
Need a customization? Get a free quote!