'uid', 'groups' => 'memberof', 'email' => 'mail', 'firstname' => 'displayname', 'lastname' => '', 'phone' => 'telephonenumber', 'jobTitle' => 'title', 'jobLevel' => 'level', 'department' => 'department', ]; private mixed $ldapLtGroupAssignments = []; private mixed $settingsRepo; private int $defaultRoleKey; private mixed $directoryType = 'OL'; private Environment $config; public mixed $useLdap; public mixed $autoCreateUser; /** * @throws BindingResolutionException */ public function __construct(bool|Environment $differentConfig = false) { $this->settingsRepo = app()->make(SettingRepository::class); if (! $differentConfig) { $this->config = app()->make(Environment::class); // Map config vars $this->useLdap = $this->config->useLdap; // Don't do anything else if ldap is turned off if ($this->useLdap === false) { return; } // Prepare and map in case we want to get the config from somewhere else in the future $this->host = $this->config->ldapHost; $this->ldapDn = $this->config->ldapDn; $this->defaultRoleKey = (int) $this->config->ldapDefaultRoleKey; $this->port = $this->config->ldapPort; $this->ldapLtGroupAssignments = json_decode(stripslashes(trim($this->config->ldapLtGroupAssignments))); $this->ldapKeys = $this->settingsRepo->getSetting('companysettings.ldap.ldapKeys') ? json_decode($this->settingsRepo->getSetting('companysettings.ldap.ldapKeys')) : json_decode(stripslashes(trim($this->config->ldapKeys))); $this->directoryType = $this->config->ldapType; $this->ldapDomain = $this->config->ldapDomain; $this->ldapUri = $this->config->ldapUri; if (! is_object($this->ldapLtGroupAssignments) || json_last_error() !== JSON_ERROR_NONE) { Log::error('LDAP: Group Assignment array failed to parse. Please check for valid json. Error: '.json_last_error_msg()); $this->ldapLtGroupAssignments = []; } if (! is_object($this->ldapKeys) || json_last_error() !== JSON_ERROR_NONE) { Log::error('LDAP: Ldap Keys failed to parse. Please check for valid json. Error: '.json_last_error_msg()); $this->ldapKeys = (object) [ 'username' => 'uid', 'groups' => 'memberof', 'email' => 'mail', 'firstname' => 'displayname', 'lastname' => '', 'phone' => 'telephonenumber', 'jobTitle' => 'title', 'jobLevel' => 'level', 'department' => 'department', ]; } } } public function connect(): bool { if (! $this->config->useLdap) { return false; } if (function_exists('ldap_connect')) { if ($this->ldapUri !== '' && str_starts_with($this->ldapUri, 'ldap')) { $this->ldapConnection = ldap_connect($this->ldapUri); } else { $this->ldapConnection = ldap_connect($this->host, $this->port); } ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3) or exit('Unable to set LDAP protocol version'); ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, 0); // We need this for doing an LDAP search. if ($this->config->debug) { ldap_set_option($this->ldapConnection, LDAP_OPT_DEBUG_LEVEL, 7); } return true; } Log::error('ldap extension not installed'); return false; } public function bind(string $username = '', string $password = ''): bool { if ($username !== '' && $password !== '') { $passwordBind = $password; // AD allows usenrame login if ($this->directoryType === 'AD') { $usernameDN = $username; // Suppress the PHP warning ldap_bind() emits on invalid // credentials. Without @, Laravel's error handler promotes it to // an ErrorException and a 500, instead of the normal // wrong-username/password message. We act on the bool return. (#3374) if (str_contains($usernameDN, '@')) { $bind = @ldap_bind($this->ldapConnection, $usernameDN, $passwordBind); } else { $bind = @ldap_bind($this->ldapConnection, $usernameDN.'@'.$this->ldapDomain, $passwordBind); } if ($bind) { return true; } } else { // OL requires distinguished name login $usernameDN = $this->ldapKeys->username.'='.ldap_escape($username, '', LDAP_ESCAPE_DN).','.$this->ldapDn; $bind = @ldap_bind($this->ldapConnection, $usernameDN, $passwordBind); } if ($bind) { return true; } if ((bool) $this->config->debug === true) { Log::error(ldap_error($this->ldapConnection)); ldap_get_option($this->ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $err); if ($err) { Log::error($err); } } } return false; } public function getEmail(string $username): string { if (! $this->ldapConnection) { Log::error('LDAP: No connection established'); return ''; } $filter = '('.$this->ldapKeys->username.'='.ldap_escape($this->extractLdapFromUsername($username), '', LDAP_ESCAPE_FILTER).')'; $attr = [$this->ldapKeys->groups, $this->ldapKeys->firstname, $this->ldapKeys->lastname, $this->ldapKeys->email]; $result = ldap_search($this->ldapConnection, $this->ldapDn, $filter, $attr) or exit('Unable to search LDAP server'); $entries = ldap_get_entries($this->ldapConnection, $result); if ($entries === false && (bool) $this->config->debug === true) { Log::error(ldap_error($this->ldapConnection)); ldap_get_option($this->ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $err); if ($err) { Log::error($err); } } return isset($entries[0][$this->ldapKeys->email]) ? $entries[0][$this->ldapKeys->email][0] : ''; } public function getSingleUser(string $username): array|false { if (! $this->ldapConnection) { Log::error('LDAP: No connection established'); return false; } $filter = '('.$this->ldapKeys->username.'='.ldap_escape($this->extractLdapFromUsername($username), '', LDAP_ESCAPE_FILTER).')'; $attr = [$this->ldapKeys->groups, $this->ldapKeys->firstname, $this->ldapKeys->lastname, $this->ldapKeys->email, $this->ldapKeys->phone, $this->ldapKeys->jobTitle, $this->ldapKeys->jobLevel, $this->ldapKeys->department]; $result = ldap_search($this->ldapConnection, $this->ldapDn, $filter, $attr) or exit('Unable to search LDAP server'); $entries = ldap_get_entries($this->ldapConnection, $result); if ($entries === false || ! isset($entries[0])) { if ((bool) $this->config->debug === true) { Log::error(ldap_error($this->ldapConnection)); ldap_get_option($this->ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $err); if ($err) { Log::error($err); } } return false; } // Find Role $role = $this->defaultRoleKey; if (isset($entries[0][$this->ldapKeys->groups]) && is_array($entries[0][$this->ldapKeys->groups])) { foreach ($entries[0][$this->ldapKeys->groups] as $grps) { foreach ($this->ldapLtGroupAssignments as $key => $row) { if ($row->ldapRole !== '' && (int) $key > $role && strpos($grps, $row->ldapRole) !== false) { $role = $key; } } } } /* * The ldap_get_entries function returns all LDAP atribute names in lowercase to insure consistency. * A few of these were not camelCase and were already lowercase so they showed up ie; lastname * But givenName did not as it needed to be normalized to match the PHP LDAP return * Example before and after code change - which has been applied to all LDAP vars in the same fashion $firstname = isset($entries[0][$this->ldapKeys->firstname]) ? $entries[0][ $this->ldapKeys->firstname ][0] : ''; $firstname = isset($entries[0][strtolower($this->ldapKeys->firstname)]) ? $entries[0][strtolower($this->ldapKeys->firstname)][0] : ''; */ // Find Firstname & Lastname $firstname = isset($entries[0][strtolower($this->ldapKeys->firstname)]) ? $entries[0][strtolower($this->ldapKeys->firstname)][0] : ''; $lastname = isset($entries[0][strtolower($this->ldapKeys->lastname)]) ? $entries[0][strtolower($this->ldapKeys->lastname)][0] : ''; $phone = isset($entries[0][strtolower($this->ldapKeys->phone)]) ? $entries[0][strtolower($this->ldapKeys->phone)][0] : ''; $uname = isset($entries[0][strtolower($this->ldapKeys->email)]) ? $entries[0][strtolower($this->ldapKeys->email)][0] : ''; $jobTitle = isset($entries[0][strtolower($this->ldapKeys->jobTitle)]) ? $entries[0][strtolower($this->ldapKeys->jobTitle)][0] : ''; $jobLevel = isset($entries[0][strtolower($this->ldapKeys->jobLevel)]) ? $entries[0][strtolower($this->ldapKeys->jobLevel)][0] : ''; $department = isset($entries[0][strtolower($this->ldapKeys->department)]) ? $entries[0][strtolower($this->ldapKeys->department)][0] : ''; if ($this->config->debug) { Log::debug("LEANTIME: Testing the logging\n"); Log::debug("LEANTIME: >>>Attributes Begin>>>>>>\n"); Log::debug("LEANTIME: fn $firstname"); Log::debug("LEANTIME: sn $lastname"); Log::debug("LEANTIME: phone $phone"); Log::debug("LEANTIME: role $role"); Log::debug("LEANTIME: username $uname "); Log::debug("LEANTIME: jobTitle $jobTitle "); Log::debug("LEANTIME: jobLevel $jobLevel "); Log::debug("LEANTIME: department $department "); Log::debug("LEANTIME: >>>Attributes End>>>>>>\n"); } return [ 'user' => $uname, 'firstname' => $firstname, 'lastname' => $lastname, 'role' => $role, 'phone' => $phone, 'jobTitle' => $jobTitle, 'jobLevel' => $jobLevel, 'department' => $department, ]; } public function extractLdapFromUsername(string $username): string { $getLdap = explode('@', $username); if (is_array($getLdap)) { return $getLdap[0]; } } public function getAllMembers(): array|false { if (function_exists('ldap_search')) { $attr = [$this->ldapKeys->groups, $this->ldapKeys->firstname, $this->ldapKeys->lastname]; $filter = '(cn=*)'; $result = ldap_search($this->ldapConnection, $this->ldapDn, $filter, $attr) or exit('Unable to search LDAP server'); $entries = ldap_get_entries($this->ldapConnection, $result); $allUsers = []; foreach ($entries as $key => $row) { if (isset($row['dn'])) { preg_match('/(?:^|.*,)uid=(.*?)(?:,.*$|$)/', $row['dn'], $usernameArray); if (count($usernameArray) > 0) { $allUsers[] = $this->getSingleUser($usernameArray[1]); } } } return $allUsers; } Log::error('ldap extension not installed'); return false; } /** * @throws BindingResolutionException */ public function upsertUsers(array $ldapUsers): bool { $userRepo = app()->make(UserRepository::class); foreach ($ldapUsers as $user) { // Update $checkUser = $userRepo->getUserByEmail($user['user']); if (is_array($checkUser)) { $userRepo->patchUser($checkUser['id'], ['firstname' => $user['firstname'], 'lastname' => $user['lastname'], 'role' => $user['role']]); } else { // Insert $userArray = [ 'firstname' => $user['firstname'], 'lastname' => $user['lastname'], 'phone' => $user['phone'], 'user' => $user['user'], 'role' => $user['role'], 'password' => '', 'clientId' => '', 'jobTitle' => $user['jobTitle'], 'jobLevel' => $user['jobLevel'], 'department' => $user['department'], 'source' => 'ldap', ]; $userRepo->addUser($userArray); } } return true; } }