mvc/ApiMutableModelControllerBase - move logic in checkAndThrowSafeDelete() to a protected function checkAndThrowValueInUse() which can search for strict or list tokens in configuration data (and throw a user exception when being used). needed for https://github.com/opnsense/core/issues/7248

This commit is contained in:
Ad Schellevis 2024-03-06 21:41:37 +01:00
parent 3d09a2cb60
commit 9bb20ab96c

View File

@ -90,6 +90,83 @@ abstract class ApiMutableModelControllerBase extends ApiControllerBase
return true;
}
/**
* check if a specific token is in use, either in a list of options (xxx,yyy) or as exact match
* @param string $token token to search recursive
* @param bool $contains exact match or in list
* @param bool $only_mvc only report (versioned) models
* @throws UserException containing additional information
*/
protected function checkAndThrowValueInUse($token, $contains=True, $only_mvc=True)
{
if ($contains) {
$xpath = "//text()[contains(.,'{$token}')]";
} else {
$xpath = "//*[text() = '{$token}']";
}
$usages = [];
// find uuid's in our config.xml
foreach (Config::getInstance()->object()->xpath($xpath) as $node) {
$referring_node = $node->xpath("..")[0];
if (!empty($referring_node->attributes()['uuid'])) {
// this looks like a model node, try to find module name (first tag with version attribute)
$item_path = array($referring_node->getName());
$item_uuid = $referring_node->attributes()['uuid'];
$parent_node = $referring_node;
do {
$parent_node = $parent_node->xpath("..");
$parent_node = $parent_node != null ? $parent_node[0] : null;
if ($parent_node != null) {
$item_path[] = $parent_node->getName();
}
} while ($parent_node != null && !isset($parent_node->attributes()['version']));
if ($parent_node != null) {
// construct usage info and add to usages if this looks like a model
$item_path = array_reverse($item_path);
$item_description = "";
foreach (["description", "descr", "name"] as $key) {
if (!empty($referring_node->$key)) {
$item_description = (string)$referring_node->$key;
break;
}
}
$usages[] = [
"reference" => implode(".", $item_path) . "." . $item_uuid,
"module" => $item_path[0],
"description" => $item_description
];
}
} elseif (!$only_mvc) {
$referring_node = $node->xpath("..")[0];
$item_description = "";
foreach (["description", "descr", "name"] as $key) {
if (!empty($referring_node->$key)) {
$item_description = (string)$referring_node->$key;
break;
}
}
$usages[] = [
"reference" => $referring_node->getName(),
"module" => $referring_node->getName(),
"description" => $item_description
];
}
}
if (!empty($usages)) {
// render exception message
$message = "";
foreach ($usages as $usage) {
$message .= sprintf(
gettext("%s - %s {%s}"),
$usage['module'],
$usage['description'],
$usage['reference']
) . "\n";
}
throw new UserException($message, gettext("Item in use by"));
}
}
/**
* Check if item can be safely deleted if $internalModelUseSafeDelete is enabled.
* Throws a user exception when the $uuid seems to be used in some other config section.
@ -99,54 +176,7 @@ abstract class ApiMutableModelControllerBase extends ApiControllerBase
private function checkAndThrowSafeDelete($uuid)
{
if (static::$internalModelUseSafeDelete) {
$configObj = Config::getInstance()->object();
$usages = [];
// find uuid's in our config.xml
foreach ($configObj->xpath("//text()[contains(.,'{$uuid}')]") as $node) {
$referring_node = $node->xpath("..")[0];
if (!empty($referring_node->attributes()['uuid'])) {
// this looks like a model node, try to find module name (first tag with version attribute)
$item_path = array($referring_node->getName());
$item_uuid = $referring_node->attributes()['uuid'];
$parent_node = $referring_node;
do {
$parent_node = $parent_node->xpath("..");
$parent_node = $parent_node != null ? $parent_node[0] : null;
if ($parent_node != null) {
$item_path[] = $parent_node->getName();
}
} while ($parent_node != null && !isset($parent_node->attributes()['version']));
if ($parent_node != null) {
// construct usage info and add to usages if this looks like a model
$item_path = array_reverse($item_path);
$item_description = "";
foreach (["description", "descr", "name"] as $key) {
if (!empty($referring_node->$key)) {
$item_description = (string)$referring_node->$key;
break;
}
}
$usages[] = array(
"reference" => implode(".", $item_path) . "." . $item_uuid,
"module" => $item_path[0],
"description" => $item_description
);
}
}
}
if (!empty($usages)) {
// render exception message
$message = "";
foreach ($usages as $usage) {
$message .= sprintf(
gettext("%s - %s {%s}"),
$usage['module'],
$usage['description'],
$usage['reference']
) . "\n";
}
throw new UserException($message, gettext("Item in use by"));
}
$this->checkAndThrowValueInUse($uuid);
}
}