Store config data serialized (#10651)

* Store config data serialized
This way we can store null, booleans, and more reliably.

* Use model to get the mutated output.

* fix whitespace and unused function

* use json_encode/decode and casts
migration to transfer

* json_encode JSON_UNESCAPED_SLASHES

* Use JSON_UNESCAPED_SLASHES.  That is only relevant if you are printing into an HTML page.

* pre-encode the seed...

* filter other fields besides config_value
This commit is contained in:
Tony Murray
2019-10-06 21:51:22 +00:00
committed by GitHub
parent a2c69cd62f
commit be04388137
8 changed files with 140 additions and 192 deletions

View File

@@ -44,7 +44,7 @@ class Config
self::loadFiles(); self::loadFiles();
// Make sure the database is connected // Make sure the database is connected
if (Eloquent::isConnected() || (function_exists('dbIsConnected') && dbIsConnected())) { if (Eloquent::isConnected()) {
// pull in the database config settings // pull in the database config settings
self::mergeDb(); self::mergeDb();
@@ -244,39 +244,23 @@ class Config
global $config; global $config;
if ($persist) { if ($persist) {
if (Eloquent::isConnected()) { try {
try { \App\Models\Config::updateOrCreate(['config_name' => $key], collect([
$config_array = collect([ 'config_name' => $key,
'config_name' => $key, 'config_default' => $default,
'config_value' => $value, 'config_descr' => $descr,
'config_default' => $default, 'config_group' => $group,
'config_descr' => $descr, 'config_sub_group' => $sub_group,
'config_group' => $group, ])->filter(function ($value, $field) {
'config_sub_group' => $sub_group, return !is_null($value);
])->filter(function ($value) { })->put('config_value', $value)->toArray());
return !is_null($value); } catch (QueryException $e) {
})->toArray(); if (class_exists(\Log::class)) {
\Log::error($e);
\App\Models\Config::updateOrCreate(['config_name' => $key], $config_array);
} catch (QueryException $e) {
// possibly table config doesn't exist yet
global $debug;
if ($debug) {
echo $e;
}
} }
} else { global $debug;
$res = dbUpdate(array('config_value' => $value), 'config', '`config_name`=?', array($key)); if ($debug) {
if (!$res && !dbFetchCell('SELECT 1 FROM `config` WHERE `config_name`=?', array($key))) { echo $e;
$insert = array(
'config_name' => $key,
'config_value' => $value,
'config_default' => $default,
'config_descr' => $descr,
'config_group' => $group,
'config_sub_group' => $sub_group,
);
dbInsert($insert, 'config');
} }
} }
} }
@@ -356,68 +340,27 @@ class Config
$db_config = []; $db_config = [];
if (Eloquent::isConnected()) { try {
try { \App\Models\Config::get(['config_name', 'config_value'])
\App\Models\Config::get(['config_name', 'config_value']) ->each(function ($item) use (&$db_config) {
->each(function ($item) use (&$db_config) { Arr::set($db_config, $item->config_name, $item->config_value);
array_set($db_config, $item->config_name, $item->config_value); });
}); } catch (QueryException $e) {
} catch (QueryException $e) { // possibly table config doesn't exist yet
// possibly table config doesn't exist yet
}
} else {
foreach (dbFetchRows('SELECT `config_name`,`config_value` FROM `config`') as $obj) {
self::assignArrayByPath($db_config, $obj['config_name'], $obj['config_value']);
}
} }
$config = array_replace_recursive($db_config, $config); $config = array_replace_recursive($db_config, $config);
} }
/**
* Assign a value into the passed array by a path
* 'snmp.version' = 'v1' becomes $arr['snmp']['version'] = 'v1'
*
* @param array $arr the array to insert the value into, will be modified in place
* @param string $path the path to insert the value at
* @param mixed $value the value to insert, will be type cast
* @param string $separator path separator
*/
private static function assignArrayByPath(&$arr, $path, $value, $separator = '.')
{
// type cast value. Is this needed here?
if (filter_var($value, FILTER_VALIDATE_INT)) {
$value = (int)$value;
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) {
$value = (float)$value;
} elseif (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
$keys = explode($separator, $path);
// walk the array creating keys if they don't exist
foreach ($keys as $key) {
$arr = &$arr[$key];
}
// assign the variable
$arr = $value;
}
private static function loadGraphsFromDb() private static function loadGraphsFromDb()
{ {
global $config; global $config;
if (Eloquent::isConnected()) { try {
try { $graph_types = GraphType::all()->toArray();
$graph_types = GraphType::all()->toArray(); } catch (QueryException $e) {
} catch (QueryException $e) { // possibly table config doesn't exist yet
// possibly table config doesn't exist yet $graph_types = [];
$graph_types = [];
}
} else {
$graph_types = dbFetchRows('SELECT * FROM graph_types');
} }
// load graph types from the database // load graph types from the database

View File

@@ -45,32 +45,17 @@ class Config extends BaseModel
'config_group' => '', 'config_group' => '',
'config_sub_group' => '', 'config_sub_group' => '',
]; ];
protected $casts = [
'config_default' => 'array'
];
/**
* Get the config_value (type cast)
*
* @param string $value
* @return mixed
*/
public function getConfigValueAttribute($value) public function getConfigValueAttribute($value)
{ {
if (filter_var($value, FILTER_VALIDATE_INT)) { return json_decode($value);
return (int)$value;
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) {
return (float)$value;
} elseif (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) {
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
return $value;
} }
public function setConfigValueAttribute($value) public function setConfigValueAttribute($value)
{ {
if (is_bool($value)) { $this->attributes['config_value'] = json_encode($value, JSON_UNESCAPED_SLASHES);
$this->attributes['config_value'] = $value ? 'true' : 'false';
} else {
$this->attributes['config_value'] = $value;
}
} }
} }

View File

@@ -0,0 +1,49 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class SerializeConfig extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::table('config')->get()->each(function ($config) {
$value = $config->config_value;
if (filter_var($value, FILTER_VALIDATE_INT)) {
$value = (int)$value;
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) {
$value = (float)$value;
} elseif (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
DB::table('config')
->where('config_id', $config->config_id)
->update(['config_value' => json_encode($value)]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::table('config')->get()->each(function ($config) {
$value = json_decode($config->config_value);
$value = is_bool($value) ? var_export($value, true) : (string)$value;
DB::table('config')
->where('config_id', $config->config_id)
->update(['config_value' => $value]);
});
}
}

View File

@@ -62,8 +62,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "alert.default_mail", "config_name" => "alert.default_mail",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "The default mail contact", "config_descr" => "The default mail contact",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -99,7 +99,7 @@ class DefaultConfigSeeder extends Seeder
[ [
"config_name" => "alert.fixed-contacts", "config_name" => "alert.fixed-contacts",
"config_value" => "true", "config_value" => "true",
"config_default" => "TRUE", "config_default" => "true",
"config_descr" => "If TRUE any changes to sysContact or users emails will not be honoured whilst alert is active", "config_descr" => "If TRUE any changes to sysContact or users emails will not be honoured whilst alert is active",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -111,7 +111,7 @@ class DefaultConfigSeeder extends Seeder
[ [
"config_name" => "alert.globals", "config_name" => "alert.globals",
"config_value" => "true", "config_value" => "true",
"config_default" => "TRUE", "config_default" => "true",
"config_descr" => "Alert read only administrators", "config_descr" => "Alert read only administrators",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -123,7 +123,7 @@ class DefaultConfigSeeder extends Seeder
[ [
"config_name" => "alert.syscontact", "config_name" => "alert.syscontact",
"config_value" => "true", "config_value" => "true",
"config_default" => "TRUE", "config_default" => "true",
"config_descr" => "Issue alerts to sysContact", "config_descr" => "Issue alerts to sysContact",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -182,8 +182,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_backend", "config_name" => "email_backend",
"config_value" => "mail", "config_value" => '"mail"',
"config_default" => "mail", "config_default" => '"mail"',
"config_descr" => "The backend to use for sending email, can be mail, sendmail or smtp", "config_descr" => "The backend to use for sending email, can be mail, sendmail or smtp",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -194,8 +194,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_from", "config_name" => "email_from",
"config_value" => "NULL", "config_value" => "null",
"config_default" => "NULL", "config_default" => "null",
"config_descr" => "Email address used for sending emails (from)", "config_descr" => "Email address used for sending emails (from)",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -218,8 +218,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_sendmail_path", "config_name" => "email_sendmail_path",
"config_value" => "/usr/sbin/sendmail", "config_value" => '"/usr/sbin/sendmail"',
"config_default" => "/usr/sbin/sendmail", "config_default" => '"/usr/sbin/sendmail"',
"config_descr" => "Location of sendmail if using this option", "config_descr" => "Location of sendmail if using this option",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -231,7 +231,7 @@ class DefaultConfigSeeder extends Seeder
[ [
"config_name" => "email_smtp_auth", "config_name" => "email_smtp_auth",
"config_value" => "false", "config_value" => "false",
"config_default" => "FALSE", "config_default" => "false",
"config_descr" => "Enable / disable smtp authentication", "config_descr" => "Enable / disable smtp authentication",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -242,8 +242,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_smtp_host", "config_name" => "email_smtp_host",
"config_value" => "localhost", "config_value" => '"localhost"',
"config_default" => "localhost", "config_default" => '"localhost"',
"config_descr" => "SMTP Host for sending email if using this option", "config_descr" => "SMTP Host for sending email if using this option",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -254,8 +254,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_smtp_password", "config_name" => "email_smtp_password",
"config_value" => "NULL", "config_value" => "null",
"config_default" => "NULL", "config_default" => "null",
"config_descr" => "SMTP Auth password", "config_descr" => "SMTP Auth password",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -278,8 +278,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_smtp_secure", "config_name" => "email_smtp_secure",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "Enable / disable encryption (use tls or ssl)", "config_descr" => "Enable / disable encryption (use tls or ssl)",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -302,8 +302,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_smtp_username", "config_name" => "email_smtp_username",
"config_value" => "NULL", "config_value" => "null",
"config_default" => "NULL", "config_default" => "null",
"config_descr" => "SMTP Auth username", "config_descr" => "SMTP Auth username",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -314,8 +314,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "email_user", "config_name" => "email_user",
"config_value" => "LibreNMS", "config_value" => '"LibreNMS"',
"config_default" => "LibreNMS", "config_default" => '"LibreNMS"',
"config_descr" => "Name used as part of the from address", "config_descr" => "Name used as part of the from address",
"config_group" => "alerting", "config_group" => "alerting",
"config_group_order" => "0", "config_group_order" => "0",
@@ -326,8 +326,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "fping", "config_name" => "fping",
"config_value" => "/usr/sbin/fping", "config_value" => '"/usr/sbin/fping"',
"config_default" => "fping", "config_default" => '"fping"',
"config_descr" => "Path to fping", "config_descr" => "Path to fping",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -338,8 +338,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "fping6", "config_name" => "fping6",
"config_value" => "/usr/sbin/fping6", "config_value" => '"/usr/sbin/fping6"',
"config_default" => "fping6", "config_default" => '"fping6"',
"config_descr" => "Path to fping6", "config_descr" => "Path to fping6",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -350,8 +350,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "geoloc.api_key", "config_name" => "geoloc.api_key",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "Geocoding API Key (Required to function)", "config_descr" => "Geocoding API Key (Required to function)",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -362,8 +362,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "geoloc.engine", "config_name" => "geoloc.engine",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "Geocoding Engine", "config_descr" => "Geocoding Engine",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -374,8 +374,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "oxidized.default_group", "config_name" => "oxidized.default_group",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "Set the default group returned", "config_descr" => "Set the default group returned",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -434,8 +434,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "oxidized.url", "config_name" => "oxidized.url",
"config_value" => "", "config_value" => '""',
"config_default" => "", "config_default" => '""',
"config_descr" => "Oxidized API url", "config_descr" => "Oxidized API url",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -482,8 +482,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "rrdtool", "config_name" => "rrdtool",
"config_value" => "/usr/bin/rrdtool", "config_value" => '"/usr/bin/rrdtool"',
"config_default" => "/usr/bin/rrdtool", "config_default" => '"/usr/bin/rrdtool"',
"config_descr" => "Path to rrdtool", "config_descr" => "Path to rrdtool",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -506,8 +506,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "snmpgetnext", "config_name" => "snmpgetnext",
"config_value" => "/usr/bin/snmpgetnext", "config_value" => '"/usr/bin/snmpgetnext"',
"config_default" => "snmpgetnext", "config_default" => '"snmpgetnext"',
"config_descr" => "Path to snmpgetnext", "config_descr" => "Path to snmpgetnext",
"config_group" => "external", "config_group" => "external",
"config_group_order" => "0", "config_group_order" => "0",
@@ -650,8 +650,8 @@ class DefaultConfigSeeder extends Seeder
], ],
[ [
"config_name" => "webui.graph_type", "config_name" => "webui.graph_type",
"config_value" => "png", "config_value" => '"png"',
"config_default" => "png", "config_default" => '"png"',
"config_descr" => "Set the default graph type", "config_descr" => "Set the default graph type",
"config_group" => "webui", "config_group" => "webui",
"config_group_order" => "0", "config_group_order" => "0",

View File

@@ -25,7 +25,7 @@ if (!is_numeric($_POST['config_id']) || empty($_POST['data'])) {
exit; exit;
} else { } else {
$data = mres($_POST['data']); $data = mres($_POST['data']);
$update = dbUpdate(array('config_value' => "$data"), 'config', '`config_id` = ?', array($_POST['config_id'])); $update = dbUpdate(array('config_value' => json_encode($data, JSON_UNESCAPED_SLASHES)), 'config', '`config_id` = ?', array($_POST['config_id']));
if (!empty($update) || $update == '0') { if (!empty($update) || $update == '0') {
echo 'success'; echo 'success';
exit; exit;

View File

@@ -83,8 +83,16 @@ if (!is_numeric($config_id)) {
$message = 'Config item has been updated:'; $message = 'Config item has been updated:';
$status = 'ok'; $status = 'ok';
} else { } else {
$state = mres($_POST['config_value']); $state = $_POST['config_value'];
$update = dbUpdate(array('config_value' => $state), 'config', '`config_id`=?', array($config_id)); if (filter_var($value, FILTER_VALIDATE_INT)) {
$state = (int)$value;
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) {
$state = (float)$value;
} elseif (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) {
$state = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
$update = dbUpdate(['config_value' => json_encode($state, JSON_UNESCAPED_SLASHES)], 'config', '`config_id`=?', array($config_id));
if (!empty($update) || $update == '0') { if (!empty($update) || $update == '0') {
$message = 'Alert rule has been updated.'; $message = 'Alert rule has been updated.';
$status = 'ok'; $status = 'ok';

View File

@@ -917,52 +917,15 @@ function clean_bootgrid($string)
function get_config_by_group($group) function get_config_by_group($group)
{ {
$items = array(); return \App\Models\Config::query()->where('config_group', $group)->get()->map(function ($config_item) {
foreach (dbFetchRows("SELECT * FROM `config` WHERE `config_group` = ?", array($group)) as $config_item) { if ($config_item['config_value'] === true) {
$val = $config_item['config_value']; $config_item['config_checked'] = 'checked';
if (filter_var($val, FILTER_VALIDATE_INT)) {
$val = (int)$val;
} elseif (filter_var($val, FILTER_VALIDATE_FLOAT)) {
$val = (float)$val;
} elseif (filter_var($val, FILTER_VALIDATE_BOOLEAN)) {
$val = (boolean)$val;
} }
if ($val === true) { return $config_item;
$config_item += array('config_checked' => 'checked'); })->keyBy('config_name')->toArray();
}
$items[$config_item['config_name']] = $config_item;
}
return $items;
}//end get_config_by_group() }//end get_config_by_group()
function get_config_like_name($name)
{
$items = array();
foreach (dbFetchRows("SELECT * FROM `config` WHERE `config_name` LIKE ?", array("%$name%")) as $config_item) {
$items[$config_item['config_id']] = $config_item;
}
return $items;
}//end get_config_like_name()
function get_config_by_name($name)
{
$config_item = dbFetchRow('SELECT * FROM `config` WHERE `config_name` = ?', array($name));
return $config_item;
}//end get_config_by_name()
function set_config_name($name, $config_value)
{
return dbUpdate(array('config_value' => $config_value), 'config', '`config_name`=?', array($name));
}//end set_config_name()
function get_url() function get_url()
{ {
// http://stackoverflow.com/questions/2820723/how-to-get-base-url-with-php // http://stackoverflow.com/questions/2820723/how-to-get-base-url-with-php

View File

@@ -126,7 +126,7 @@ class ConfigTest extends LaravelTestCase
$key = 'testing.persist'; $key = 'testing.persist';
$query = Eloquent::DB()->table('config')->where('config_name', $key); $query = \App\Models\Config::query()->where('config_name', $key);
$query->delete(); $query->delete();
$this->assertFalse($query->exists(), "$key should not be set, clean database"); $this->assertFalse($query->exists(), "$key should not be set, clean database");