From 7c539fd8736b0ff9acafe32d857b2a2021d778e6 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 31 Jul 2019 16:58:14 +0200 Subject: [locations] Add warnings/cleanup for bad machine to roomplan mappings --- .../locations/inc/autolocation.inc.php | 3 + modules-available/locations/inc/location.inc.php | 91 ++------- .../locations/inc/locationutil.inc.php | 203 +++++++++++++++++++++ 3 files changed, 223 insertions(+), 74 deletions(-) (limited to 'modules-available/locations/inc') diff --git a/modules-available/locations/inc/autolocation.inc.php b/modules-available/locations/inc/autolocation.inc.php index 61507ebf..82c61251 100644 --- a/modules-available/locations/inc/autolocation.inc.php +++ b/modules-available/locations/inc/autolocation.inc.php @@ -43,6 +43,9 @@ class AutoLocation Database::exec("UPDATE machine SET subnetlocationid = :lid WHERE machineuuid IN (:machines)", ['lid' => $lid, 'machines' => $machines]); } + // While we're at it, try to fix invalid entries, having a fixedlocationid but no actual position information + Database::exec('UPDATE machine SET fixedlocationid = NULL + WHERE fixedlocationid IS NOT NULL AND Length(position) = 0'); } } diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php index f1f05402..e35670cc 100644 --- a/modules-available/locations/inc/location.inc.php +++ b/modules-available/locations/inc/location.inc.php @@ -329,26 +329,30 @@ class Location if ($uuid !== false) { // Machine ip maps to a location, and we have a client supplied uuid (which might not be known if the client boots for the first time) $uuidLoc = self::getFromMachineUuid($uuid); - if ($uuidLoc === $ipLoc) { + if (self::isUuidLocationValid($uuidLoc, $ipLoc)) { $locationId = $uuidLoc; - } else if ($uuidLoc !== false) { - // Validate that the location the IP maps to is in the chain we get using the - // location determined by the uuid - $uuidLocations = self::getLocationRootChain($uuidLoc); - $ipLocations = self::getLocationRootChain($ipLoc); - if (in_array($uuidLoc, $ipLocations) // UUID loc is further up, OK - || (in_array($ipLoc, $uuidLocations) && count($ipLocations) + 1 >= count($uuidLocations)) // UUID is max one level deeper than IP loc, accept as well - ) { - // Close enough, allow - $locationId = $uuidLoc; - } - // UUID and IP disagree too much, play safe and ignore the UUID } } } return $locationId; } + public static function isUuidLocationValid($uuidLoc, $ipLoc) + { + if ($ipLoc !== false && $uuidLoc !== false && $uuidLoc !== $ipLoc) { + // Validate that the location the IP maps to is in the chain we get using the + // location determined by the uuid + $uuidLocations = self::getLocationRootChain($uuidLoc); + $ipLocations = self::getLocationRootChain($ipLoc); + if (count($ipLocations) + 2 >= count($uuidLocations) && in_array($ipLoc, $uuidLocations)) { + // UUID is max one level deeper than IP loc, accept + return true; + } + // UUID and IP disagree too much, play safe and ignore the UUID + } + return false; + } + /** * Get all location IDs from the given location up to the root. * @@ -401,62 +405,6 @@ class Location return $subnets; } - public static function getOverlappingSubnets(&$overlapSelf = false, &$overlapOther = false) - { - if ($overlapSelf === false && $overlapOther === false) { - return; - } - $locs = self::getLocationsAssoc(); - $subnets = self::getSubnets(); - if ($overlapSelf) { - $self = array(); - } - if ($overlapOther) { - $other = array(); - } - $cnt = count($subnets); - for ($i = 0; $i < $cnt; ++$i) { - for ($j = $i + 1; $j < $cnt; ++$j) { - if ($overlapSelf && $subnets[$i]['locationid'] === $subnets[$j]['locationid'] - && self::overlap($subnets[$i], $subnets[$j]) - ) { - $self[$subnets[$i]['locationid']] = $subnets[$i]['locationid']; - } - if ($overlapOther && $subnets[$i]['locationid'] !== $subnets[$j]['locationid'] - && self::overlap($subnets[$i], $subnets[$j]) - ) { - $a = min($subnets[$i]['locationid'], $subnets[$j]['locationid']); - $b = max($subnets[$i]['locationid'], $subnets[$j]['locationid']); - $other["$a|$b"] = array('lid1' => $subnets[$i]['locationid'], 'lid2' => $subnets[$j]['locationid']); - } - } - } - if ($overlapSelf) { - $overlapSelf = array(); - foreach ($self as $entry) { - if (!isset($locs[$entry])) - continue; - $overlapSelf[]['locationname'] = $locs[$entry]['locationname']; - } - } - if ($overlapOther) { - $overlapOther = array(); - foreach ($other as $entry) { - if (!isset($locs[$entry['lid1']]) || !isset($locs[$entry['lid2']])) - continue; - if (in_array($entry['lid1'], $locs[$entry['lid2']]['parents']) || in_array($entry['lid2'], $locs[$entry['lid1']]['parents'])) - continue; - if (isset($locs[$entry['lid1']])) { - $entry['name1'] = $locs[$entry['lid1']]['locationname']; - } - if (isset($locs[$entry['lid2']])) { - $entry['name2'] = $locs[$entry['lid2']]['locationname']; - } - $overlapOther[] = $entry; - } - } - } - /** * @return array|bool assoc array mapping from locationid to subnets */ @@ -535,9 +483,4 @@ class Location } } - private static function overlap($net1, $net2) - { - return ($net1['startaddr'] <= $net2['endaddr'] && $net1['endaddr'] >= $net2['startaddr']); - } - } diff --git a/modules-available/locations/inc/locationutil.inc.php b/modules-available/locations/inc/locationutil.inc.php index b3d9bbc7..6b18d864 100644 --- a/modules-available/locations/inc/locationutil.inc.php +++ b/modules-available/locations/inc/locationutil.inc.php @@ -1 +1,204 @@ $subnets[$i]['locationid'], 'lid2' => $subnets[$j]['locationid']); + } + } + } + if ($overlapSelf) { + $overlapSelf = array(); + foreach ($self as $entry) { + if (!isset($locs[$entry])) + continue; + $overlapSelf[]['locationname'] = $locs[$entry]['locationname']; + } + } + if ($overlapOther) { + $overlapOther = array(); + foreach ($other as $entry) { + if (!isset($locs[$entry['lid1']]) || !isset($locs[$entry['lid2']])) + continue; + if (in_array($entry['lid1'], $locs[$entry['lid2']]['parents']) || in_array($entry['lid2'], $locs[$entry['lid1']]['parents'])) + continue; + if (isset($locs[$entry['lid1']])) { + $entry['name1'] = $locs[$entry['lid1']]['locationname']; + } + if (isset($locs[$entry['lid2']])) { + $entry['name2'] = $locs[$entry['lid2']]['locationname']; + } + $overlapOther[] = $entry; + } + } + } + + /** + * Get information about machines where the location assigned by roomplanner + * mismatches what the subnet configuration says. + * If $locationId is 0, return list of all locations where a mismatch occurs, + * grouped by the location the client was assigned to via roomplanner. + * Otherwise, just return an assoc array with the requested locationid, name + * and a list of all clients that are wrongfully assigned to that room. + * @param int $locationId + * @return array + */ + public static function getMachinesWithLocationMismatch($locationId = 0, $checkPerms = false) + { + $locationId = (int)$locationId; + if ($checkPerms) { + if ($locationId !== 0) { + // Query details for specific location -- use assert and fake array + User::assertPermission('.roomplanner.edit', $locationId); + $roomplannerLocs = [$locationId]; + } else { + // Query summary for all locations -- get actual list + $roomplannerLocs = User::getAllowedLocations('.roomplanner.edit'); + } + if (User::hasPermission('subnets.edit')) { + $ipLocs = [0]; + } else { + $ipLocs = User::getAllowedLocations('location.edit.subnets'); + } + if (in_array(0, $ipLocs)) { + $ipLocs = true; + } + if (in_array(0, $roomplannerLocs)) { + $roomplannerLocs = true; + } + if ($ipLocs === true && $roomplannerLocs === true) { + $checkPerms = false; // User can do everything + } elseif ($ipLocs === true || $roomplannerLocs === true) { + $combinedLocs = true; + } else { + $combinedLocs = array_unique(array_merge($ipLocs, $roomplannerLocs)); + } + } + if ($checkPerms && empty($combinedLocs)) + return []; + if ($locationId === 0) { + if (!$checkPerms || $combinedLocs === true) { + $extra = 'IS NOT NULL'; + $params = []; + } else { + $extra = 'IN (:locs)'; + $params = ['locs' => $combinedLocs]; + } + $query = "SELECT subnetlocationid, fixedlocationid + FROM machine WHERE fixedlocationid $extra"; + } else { + $query = "SELECT machineuuid, hostname, clientip, subnetlocationid, fixedlocationid + FROM machine WHERE fixedlocationid = :locationid"; + $params = ['locationid' => $locationId]; + } + $res = Database::simpleQuery($query, $params); + $return = []; + $locs = false; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($row['subnetlocationid'] === $row['fixedlocationid']) + continue; + if (Location::isUuidLocationValid((int)$row['fixedlocationid'], (int)$row['subnetlocationid'])) + continue; + $lid = (int)$row['fixedlocationid']; + if (!isset($return[$lid])) { + if ($locs === false) { + $locs = Location::getLocationsAssoc(); + } + $return[$lid] = [ + 'locationid' => $lid, + 'locationname' => $locs[$lid]['locationname'], + 'clients' => [], + 'count' => 0, + ]; + } + if ($locationId === 0) { + $return[$lid]['count']++; + } else { + $slid = (int)$row['subnetlocationid']; + $return[$lid]['clients'][] = [ + 'machineuuid' => $row['machineuuid'], + 'hostname' => $row['hostname'], + 'clientip' => $row['clientip'], + 'iplocationid' => $slid, + 'iplocationname' => $locs[$slid]['locationname'], + 'canmove' => !$checkPerms || $ipLocs === true || in_array($slid, $ipLocs), // Can machine be moved to subnet's locationid? + ]; + } + } + if (empty($return)) + return $return; + if ($locationId === 0) { + return array_values($return); + } else { + return $return[$locationId]; + } + } + + private static function overlap($net1, $net2) + { + return ($net1['startaddr'] <= $net2['endaddr'] && $net1['endaddr'] >= $net2['startaddr']); + } + + public static function rangeToLongVerbose($start, $end) + { + $result = self::rangeToLong($start, $end); + list($startLong, $endLong) = $result; + if ($startLong === false) { + Message::addWarning('main.value-invalid', 'start addr', $start); + } + if ($endLong === false) { + Message::addWarning('main.value-invalid', 'end addr', $start); + } + if ($startLong === false || $endLong === false) + return false; + if ($startLong > $endLong) { + Message::addWarning('main.value-invalid', 'range', $start . ' - ' . $end); + return false; + } + return $result; + } + + public static function rangeToLong($start, $end) + { + $startLong = ip2long($start); + $endLong = ip2long($end); + if ($startLong !== false) { + $startLong = sprintf("%u", $startLong); + } + if ($endLong !== false) { + $endLong = sprintf("%u", $endLong); + } + return array($startLong, $endLong); + } + +} -- cgit v1.2.3-55-g7522