From fadaffac21801e37cb5494a54f997cb0ad0ac569 Mon Sep 17 00:00:00 2001 From: f0o Date: Thu, 7 May 2015 14:09:41 +0000 Subject: [PATCH 1/3] Allow alerting on tables with relative/indirect glues. This commit introduces a lookup using an adaption of left/right expansion searches to find a missing glue-column across several layers of relations. You can now alert on data having a glue mapped to other entities as long as it resolves to 'device_id' in under 30 cycles. First intention of usage is to alert on bill-usages. The bills-table does not contain a valid 'device_id' glue and has to be resolved via 'bills.bill_id -> bill_ports.port_id -> ports.device_id'. This example consumes 3+1 cycles. --- includes/alerts.inc.php | 98 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/includes/alerts.inc.php b/includes/alerts.inc.php index 320305c7a..9e4c767b8 100644 --- a/includes/alerts.inc.php +++ b/includes/alerts.inc.php @@ -33,6 +33,7 @@ include_once($config['install_dir'].'/html/includes/authentication/'.$config['au function GenSQL($rule) { $rule = RunMacros($rule); if( empty($rule) ) { + //Cannot resolve Macros due to recursion. Rule is invalid. return false; } //Pretty-print rule to dissect easier @@ -49,12 +50,40 @@ function GenSQL($rule) { } } $tables = array_keys(array_flip($tables)); - $x = sizeof($tables); + if( dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?',array($tables[0],'device_id')) != 1 ) { + //Our first table has no valid glue, append the 'devices' table to it! + array_unshift($tables, 'devices'); + } + $x = sizeof($tables)-1; $i = 0; $join = ""; while( $i < $x ) { if( isset($tables[$i+1]) ) { - $join .= $tables[$i].".device_id = ".$tables[$i+1].".device_id && "; + if( dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?',array($tables[$i+1],'device_id')) == 1 ) { + //Found valid glue in definition. + $join .= $tables[$i].".device_id = ".$tables[$i+1].".device_id && "; + } else { + //No valid glue, Resolve a glue-chain. + $gtmp = ResolveGlues(array($tables[$i+1]),'device_id'); + if( $gtmp === false ) { + //Cannot resolve glue-chain. Rule is invalid. + return false; + } + foreach( $gtmp as $glue ) { + if( !$last ) { + list($tmp,$last) = explode('.',$glue); + $qry .= $glue.' = '; + } else { + list($tmp,$new) = explode('.',$glue); + $qry .= $tmp.'.'.$last.' && '.$tmp.'.'.$new.' = '; + $last = $new; + } + if( !in_array($tmp, $tables) ) { + $tables[] = $tmp; + } + } + $join .= "( ".$qry.$tables[0].".device_id ) && "; + } } $i++; } @@ -62,9 +91,68 @@ function GenSQL($rule) { return $sql; } +/** + * Create a glue-chain + * @param array $tables Initial Tables to construct glue-chain + * @param string $target Glue to find (usual device_id) + * @param int $x Recursion Anchor + * @param array $hist History of processed tables + * @param array $last Glues on the fringe + * @return string|boolean + */ +function ResolveGlues($tables,$target,$x=0,$hist=array(),$last=array()) { + if( sizeof($tables) == 1 && $x != 0 ) { + if( dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?',array($tables[0],$target)) == 1 ) { + return array_merge($last,array($tables[0].'.'.$target)); + } else { + return false; + } + } else { + $x++; + if( $x > 30 ) { + //Too much recursion. Abort. + return false; + } + foreach( $tables as $table ) { + $glues = dbFetchRows('SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME LIKE "%\_id"',array($table)); + if( sizeof($glues) == 1 && $glues[0]['COLUMN_NAME'] != $target ) { + //Search for new candidates to expand + $tmp = dbFetchRows('SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME LIKE "'.substr($table,0,-1).'_%" && TABLE_NAME != "'.$table.'"'); + $ntables = array(); + foreach( $tmp as $expand ) { + $ntables[] = $expand['TABLE_NAME']; + } + $tmp = ResolveGlues($ntables,$target,$x++,array_merge($tables,$ntables),array_merge($last,array($table.'.'.$glues[0]['COLUMN_NAME']))); + if( is_array($tmp) ) { + return $tmp; + } + } else { + foreach( $glues as $glue ) { + if( $glue['COLUMN_NAME'] == $target ) { + return array_merge($last,array($table.'.'.$target)); + } else { + list($tmp) = explode('_',$glue['COLUMN_NAME']); + $tmp .= 's'; + if( !in_array($tmp,$tables) && !in_array($tmp,$hist) ) { + //Expand table + $tmp = ResolveGlues(array($tmp),$target,$x++,array_merge($tables,array($tmp)),array_merge($last,array($table.'.'.$glue['COLUMN_NAME']))); + if( is_array($tmp) ) { + return $tmp; + } + } + } + } + } + } + } + //You should never get here. + return false; +} + /** * Process Macros * @param string $rule Rule to process + * @param int $x Recursion-Anchor * @return string|boolean */ function RunMacros($rule,$x=1) { @@ -196,6 +284,12 @@ function GetContacts($results) { $uids = array(); foreach( $results as $result ) { $tmp = NULL; + if( is_numeric($result["bill_id"]) ) { + $tmpa = dbFetchRows("SELECT user_id FROM bill_perms WHERE bill_id = ?",array($result["bill_id"])); + foreach( $tmpa as $tmp ) { + $uids[$tmp['user_id']] = $tmp['user_id']; + } + } if( is_numeric($result["port_id"]) ) { $tmpa = dbFetchRows("SELECT user_id FROM ports_perms WHERE access_level >= 0 AND port_id = ?",array($result["port_id"])); foreach( $tmpa as $tmp ) { From 740e85a1ba04a1469ddca2da616e77f8a25b6224 Mon Sep 17 00:00:00 2001 From: f0o Date: Thu, 7 May 2015 19:55:52 +0000 Subject: [PATCH 2/3] Fix silly SQL bug --- alerts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alerts.php b/alerts.php index eceb62f45..e6807db31 100755 --- a/alerts.php +++ b/alerts.php @@ -82,7 +82,7 @@ function IsRuleValid($device,$rule) { */ function RunFollowUp() { global $config; - foreach( dbFetchRows("SELECT alerts.device_id, alerts.rule_id, alerts.state FROM alerts LEFT JOIN alert_rules ON alerts.rule_id = alert_rules.id WHERE alerts.state != 2 && alerts.open = 0") as $alert ) { + foreach( dbFetchRows("SELECT alerts.device_id, alerts.rule_id, alerts.state FROM alerts WHERE alerts.state != 2 && alerts.state > 0 && alerts.open = 0") as $alert ) { $tmp = array($alert['rule_id'],$alert['device_id']); $alert = dbFetchRow("SELECT alert_log.id,alert_log.rule_id,alert_log.device_id,alert_log.state,alert_log.details,alert_log.time_logged,alert_rules.rule,alert_rules.severity,alert_rules.extra,alert_rules.name FROM alert_log,alert_rules WHERE alert_log.rule_id = alert_rules.id && alert_log.device_id = ? && alert_log.rule_id = ? && alert_rules.disabled = 0 ORDER BY alert_log.id DESC LIMIT 1",array($alert['device_id'],$alert['rule_id'])); if( empty($alert['rule']) || !IsRuleValid($tmp[1],$tmp[0]) ) { @@ -125,7 +125,7 @@ function RunFollowUp() { function RunAlerts() { global $config; $default_tpl = "%title\r\nSeverity: %severity\r\n{if %state == 0}Time elapsed: %elapsed\r\n{/if}Timestamp: %timestamp\r\nUnique-ID: %uid\r\nRule: {if %name}%name{else}%rule{/if}\r\n{if %faults}Faults:\r\n{foreach %faults} #%key: %value.string\r\n{/foreach}{/if}Alert sent to: {foreach %contacts}%value <%key> {/foreach}"; //FIXME: Put somewhere else? - foreach( dbFetchRows("SELECT alerts.device_id, alerts.rule_id, alerts.state FROM alerts LEFT JOIN alert_rules ON alerts.rule_id = alert_rules.id WHERE alerts.state != 2 && alerts.open = 1") as $alert ) { + foreach( dbFetchRows("SELECT alerts.device_id, alerts.rule_id, alerts.state FROM alerts WHERE alerts.state != 2 && alerts.open = 1") as $alert ) { $tmp = array($alert['rule_id'],$alert['device_id']); $alert = dbFetchRow("SELECT alert_log.id,alert_log.rule_id,alert_log.device_id,alert_log.state,alert_log.details,alert_log.time_logged,alert_rules.rule,alert_rules.severity,alert_rules.extra,alert_rules.name FROM alert_log,alert_rules WHERE alert_log.rule_id = alert_rules.id && alert_log.device_id = ? && alert_log.rule_id = ? && alert_rules.disabled = 0 ORDER BY alert_log.id DESC LIMIT 1",array($alert['device_id'],$alert['rule_id'])); if( empty($alert['rule_id']) || !IsRuleValid($tmp[1],$tmp[0]) ) { From 6ab9b9727e3b4a111754c9fa5d153e956b538d0f Mon Sep 17 00:00:00 2001 From: f0o Date: Thu, 7 May 2015 20:05:41 +0000 Subject: [PATCH 3/3] Make scrut happy --- includes/alerts.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/alerts.inc.php b/includes/alerts.inc.php index 9e4c767b8..fe0699706 100644 --- a/includes/alerts.inc.php +++ b/includes/alerts.inc.php @@ -69,8 +69,10 @@ function GenSQL($rule) { //Cannot resolve glue-chain. Rule is invalid. return false; } + $last = ""; + $qry = ""; foreach( $gtmp as $glue ) { - if( !$last ) { + if( empty($last) ) { list($tmp,$last) = explode('.',$glue); $qry .= $glue.' = '; } else {