From 910b9b242e053d968959fda84da61b13dcbf50a2 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Tue, 17 Jun 2025 11:08:48 -0400 Subject: [PATCH 1/2] check for conflicts across entire LDAP tree --- resources/init.php | 1 + resources/lib/UnityLDAP.php | 66 +++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/resources/init.php b/resources/init.php index f04625e3..33c0e719 100644 --- a/resources/init.php +++ b/resources/init.php @@ -40,6 +40,7 @@ $CONFIG["ldap"]["user"], $CONFIG["ldap"]["pass"], __DIR__ . "/../deployment/custom_user_mappings", + $CONFIG["ldap"]["basedn"], $CONFIG["ldap"]["user_ou"], $CONFIG["ldap"]["group_ou"], $CONFIG["ldap"]["pigroup_ou"], diff --git a/resources/lib/UnityLDAP.php b/resources/lib/UnityLDAP.php index b63f75c9..2bae5eb8 100644 --- a/resources/lib/UnityLDAP.php +++ b/resources/lib/UnityLDAP.php @@ -26,6 +26,7 @@ class UnityLDAP extends ldapConn ); // string vars for OUs + private $STR_BASEOU; private $STR_USEROU; private $STR_GROUPOU; private $STR_PIGROUPOU; @@ -33,6 +34,7 @@ class UnityLDAP extends ldapConn private $STR_ADMINGROUP; // Instance vars for various ldapEntry objects + private $baseOU; private $userOU; private $groupOU; private $pi_groupOU; @@ -49,6 +51,7 @@ public function __construct( $dn, $pass, $custom_user_mappings, + $base_dn, $user_ou, $group_ou, $pigroup_ou, @@ -59,6 +62,7 @@ public function __construct( ) { parent::__construct($host, $dn, $pass); + $this->STR_BASEOU = $base_dn; $this->STR_USEROU = $user_ou; $this->STR_GROUPOU = $group_ou; $this->STR_PIGROUPOU = $pigroup_ou; @@ -66,6 +70,7 @@ public function __construct( $this->STR_ADMINGROUP = $admin_group; // Get Global Entries + $this->baseOU = $this->getEntry($base_dn); $this->userOU = $this->getEntry($user_ou); $this->groupOU = $this->getEntry($group_ou); $this->pi_groupOU = $this->getEntry($pigroup_ou); @@ -123,13 +128,11 @@ public function getNextUIDNumber($UnitySQL) { $max_uid = $UnitySQL->getSiteVar('MAX_UID'); $new_uid = $max_uid + 1; - - while ($this->IDNumInUse($new_uid)) { + $id_nums_in_use = $this->getIDNumsInUse(); + while ($this->IDNumInUse($new_uid, $id_nums_in_use)) { $new_uid++; } - $UnitySQL->updateSiteVar('MAX_UID', $new_uid); - return $new_uid; } @@ -137,13 +140,11 @@ public function getNextPiGIDNumber($UnitySQL) { $max_pigid = $UnitySQL->getSiteVar('MAX_PIGID'); $new_pigid = $max_pigid + 1; - - while ($this->IDNumInUse($new_pigid)) { + $id_nums_in_use = $this->getIDNumsInUse(); + while ($this->IDNumInUse($new_pigid, $id_nums_in_use)) { $new_pigid++; } - $UnitySQL->updateSiteVar('MAX_PIGID', $new_pigid); - return $new_pigid; } @@ -151,46 +152,41 @@ public function getNextOrgGIDNumber($UnitySQL) { $max_gid = $UnitySQL->getSiteVar('MAX_GID'); $new_gid = $max_gid + 1; - - while ($this->IDNumInUse($new_gid)) { + $id_nums_in_use = $this->getIDNumsInUse(); + while ($this->IDNumInUse($new_gid, $id_nums_in_use)) { $new_gid++; } - $UnitySQL->updateSiteVar('MAX_GID', $new_gid); - return $new_gid; } - private function IDNumInUse($id) + private function IDNumInUse($id_num, $id_nums_in_use) { - // id reserved for debian packages - if (($id >= 100 && $id <= 999) || ($id >= 60000 && $id <= 64999)) { + // reserved for debian packages + if (($id_num >= 100 && $id_num <= 999) || ($id_num >= 60000 && $id_num <= 64999)) { return true; } - $users = $this->userOU->getChildrenArray([], true); - foreach ($users as $user) { - if ($user["uidnumber"][0] == $id) { - return true; - } - } - $pi_groups = $this->pi_groupOU->getChildrenArray(["gidnumber"], true); - foreach ($pi_groups as $pi_group) { - if ($pi_group["gidnumber"][0] == $id) { - return true; - } - } - $groups = $this->groupOU->getChildrenArray(["gidnumber"], true); - foreach ($groups as $group) { - if ($group["gidnumber"][0] == $id) { - return true; - } - } + return in_array($id_num, $id_nums_in_use); + } - return false; + private function getIDNumsInUse() + { + return array_merge( + // search entire LDAP tree, not just for entries created by portal + array_map( + fn($x) => intval($x["uidnumber"][0]), + $this->baseOU->getChildrenArray(["uidnumber"], true, "objectClass=posixAccount") + ), + array_map( + fn($x) => intval($x["gidnumber"][0]), + $this->baseOU->getChildrenArray(["gidnumber"], true, "objectClass=posixGroup") + ), + ); } public function getUnassignedID($uid, $UnitySQL) { + $id_nums_in_use = $this->getIDNumsInUse(); $netid = strtok($uid, "_"); // extract netid // scrape all files in custom folder $dir = new \DirectoryIterator($this->custom_mappings_path); @@ -204,7 +200,7 @@ public function getUnassignedID($uid, $UnitySQL) if ($uid == $netid_match || $netid == $netid_match) { // found a match - if (!$this->IDNumInUse($uid_match)) { + if (!$this->IDNumInUse($uid_match, $id_nums_in_use)) { return $uid_match; } } From 56f0151cc1af6f08c4beed7ddad1cb821ef2af6b Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Tue, 17 Jun 2025 11:04:00 -0400 Subject: [PATCH 2/2] rewrite custom user mapping --- resources/lib/UnityLDAP.php | 67 ++++++++++++++++++++++++------------- resources/lib/UnityUser.php | 2 +- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/resources/lib/UnityLDAP.php b/resources/lib/UnityLDAP.php index 2bae5eb8..011e1d3f 100644 --- a/resources/lib/UnityLDAP.php +++ b/resources/lib/UnityLDAP.php @@ -124,11 +124,16 @@ public function getDefUserShell() // // ID Number selection functions // - public function getNextUIDNumber($UnitySQL) + public function getNextUIDNumber($uid, $UnitySQL) { + $id_nums_in_use = $this->getIDNumsInUse(); + $custom_id_mappings = $this->getCustomIDMappings(); + $custom_mapped_id = $this->getCustomMappedID($uid, $id_nums_in_use, $custom_id_mappings); + if (!is_null($custom_mapped_id)) { + return $custom_mapped_id; + } $max_uid = $UnitySQL->getSiteVar('MAX_UID'); $new_uid = $max_uid + 1; - $id_nums_in_use = $this->getIDNumsInUse(); while ($this->IDNumInUse($new_uid, $id_nums_in_use)) { $new_uid++; } @@ -181,37 +186,51 @@ private function getIDNumsInUse() fn($x) => intval($x["gidnumber"][0]), $this->baseOU->getChildrenArray(["gidnumber"], true, "objectClass=posixGroup") ), + array_map( + fn($x) => $x[1], + $this->getCustomIDMappings(), + ), ); } - public function getUnassignedID($uid, $UnitySQL) + private function getCustomIDMappings() { - $id_nums_in_use = $this->getIDNumsInUse(); - $netid = strtok($uid, "_"); // extract netid - // scrape all files in custom folder + $output = []; $dir = new \DirectoryIterator($this->custom_mappings_path); foreach ($dir as $fileinfo) { - if ($fileinfo->getExtension() == "csv") { - // found csv file - $handle = fopen($fileinfo->getPathname(), "r"); - while (($data = fgetcsv($handle, 1000, ",")) !== false) { - $netid_match = $data[0]; - $uid_match = $data[1]; - - if ($uid == $netid_match || $netid == $netid_match) { - // found a match - if (!$this->IDNumInUse($uid_match, $id_nums_in_use)) { - return $uid_match; - } - } - } + if (!$fileinfo->getExtension() == "csv") { + UnitySite::errorLog( + "warning", + "file in custom mappings directory does not have the .csv extension so it is ignored." + ); + continue; + } + $handle = fopen($fileinfo->getPathname(), "r"); + while (($data = fgetcsv($handle, 1000, ",")) !== false) { + array_merge($output, $data); } } + return $output; + } - // didn't find anything from existing mappings, use next available - $next_uid = $this->getNextUIDNumber($UnitySQL); - - return $next_uid; + private function getCustomMappedID($uid, $id_nums_in_use, $custom_id_mappings) + { + $netid = strtok($uid, "_"); + foreach ($custom_id_mappings as [$match, $uidnumber]) { + if (($uid != $match) || ($netid != $match)) { + continue; + } + if ($this->IDNumInUse($uidnumber, $id_nums_in_use)) { + UnitySite::errorLog( + "warning", + "user '$uid' has a custom mapping for ID $uidnumber but it's already in use!" + ); + return null; + } else { + return $uidnumber; + } + } + return null; } // diff --git a/resources/lib/UnityUser.php b/resources/lib/UnityUser.php index e1465251..c4f8a8b7 100644 --- a/resources/lib/UnityUser.php +++ b/resources/lib/UnityUser.php @@ -58,7 +58,7 @@ public function init($send_mail = true) // Create LDAP group // $ldapGroupEntry = $this->getLDAPGroup(); - $id = $this->LDAP->getUnassignedID($this->getUID(), $this->SQL); + $id = $this->LDAP->getNextUIDNumber($this->getUID(), $this->SQL); if (!$ldapGroupEntry->exists()) { $ldapGroupEntry->setAttribute("objectclass", UnityLDAP::POSIX_GROUP_CLASS);