1<?php
2/***********************************************************
3Copyright Darshan Kansagara <kansagara.darshan97@gmail.com>
4SPDX-License-Identifier: GPL-2.0
5Author: Darshan Kansagara <kansagara.darshan97@gmail.com>
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9version 2 as published by the Free Software Foundation.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License along
17with this program; if not, write to the Free Software Foundation, Inc.,
1851 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19***********************************************************/
20
21/**
22 * \file
23 * \brief fossdash configuration functions.
24 */
25
26/**
27 * \brief Initialize the fossdash configuration after bootstrap().
28 *
29 * This function also opens a database connection (global PG_CONN).
30 *
31 * fossdash configuration variables are in below place:
32 *  - Database fossdashconfig table
33 *
34 *
35 * \param string $sysconfdir   Path to SYSCONFDIR
36 * \param[out] array &$SysConf Configuration variable array (updated by this function)
37 *
38 * If the fossdashconfig table doesn't exist then create it.
39 * Write records for the core variables into fossdashconfig table.
40 *
41 */
42function FossdashConfigInit($sysconfdir, &$SysConf)
43{
44  global $PG_CONN;
45
46  /*
47   * Connect to the database.  If the connection fails,
48   * DBconnect() will print a failure message and exit.
49   */
50  $PG_CONN = DBconnect($sysconfdir);
51
52  global $container;
53  $postgresDriver = new \Fossology\Lib\Db\Driver\Postgres($PG_CONN);
54  $container->get('db.manager')->setDriver($postgresDriver);
55
56  /**************** read/create/populate the fossdashconfig table *********/
57  /* create if fossdashconfig table if it doesn't exist */
58  $newTable  = Create_fossdashconfig();
59
60  /* populate it with core variables */
61  Populate_fossdashconfig();
62
63  /* populate the global $SysConf array with variable/value pairs */
64  $sql = "SELECT variablename, conf_value FROM fossdashconfig;";
65  $result = pg_query($PG_CONN, $sql);
66  DBCheckResult($result, $sql, __FILE__, __LINE__);
67
68  while ($row = pg_fetch_assoc($result)) {
69    $SysConf['FOSSDASHCONFIG'][$row['variablename']] = $row['conf_value'];
70  }
71  pg_free_result($result);
72
73  return;
74}
75
76
77/**
78 * \brief Create the fossdashconfig table.
79 *
80 * \return 0 if table already exists.
81 * 1 if it was created
82 */
83function Create_fossdashconfig()
84{
85  global $PG_CONN;
86
87  /* If fossdashconfig exists, then we are done */
88  $sql = "SELECT typlen  FROM pg_type WHERE typname='fossdashconfig' limit 1;";
89  $result = pg_query($PG_CONN, $sql);
90  DBCheckResult($result, $sql, __FILE__, __LINE__);
91  $numrows = pg_num_rows($result);
92  pg_free_result($result);
93  if ($numrows > 0) {
94    return 0;
95  }
96
97  /* Create the fossdashconfig table */
98  $sql = "
99CREATE TABLE fossdashconfig (
100    fossdashconfig_pk serial NOT NULL PRIMARY KEY,
101    variablename character varying(30) NOT NULL UNIQUE,
102    conf_value text,
103    ui_label character varying(60) NOT NULL,
104    vartype int NOT NULL,
105    group_name character varying(20) NOT NULL,
106    group_order int,
107    description text NOT NULL,
108    validation_function character varying(40) DEFAULT NULL,
109    option_value character varying(40) DEFAULT NULL
110);
111";
112
113  $result = pg_query($PG_CONN, $sql);
114  DBCheckResult($result, $sql, __FILE__, __LINE__);
115  pg_free_result($result);
116
117  /* Document columns */
118  $sql = "
119COMMENT ON TABLE fossdashconfig IS 'System configuration values';
120COMMENT ON COLUMN fossdashconfig.variablename IS 'Name of configuration variable';
121COMMENT ON COLUMN fossdashconfig.conf_value IS 'value of config variable';
122COMMENT ON COLUMN fossdashconfig.ui_label IS 'Label that appears on user interface to prompt for variable';
123COMMENT ON COLUMN fossdashconfig.group_name IS 'Name of this variables group in the user interface';
124COMMENT ON COLUMN fossdashconfig.group_order IS 'The order this variable appears in the user interface group';
125COMMENT ON COLUMN fossdashconfig.description IS 'Description of variable to document how/where the variable value is used.';
126COMMENT ON COLUMN fossdashconfig.validation_function IS 'Name of function to validate input. Not currently implemented.';
127COMMENT ON COLUMN fossdashconfig.vartype IS 'variable type.  1=int, 2=text, 3=textarea, 4=password, 5=dropdown';
128COMMENT ON COLUMN fossdashconfig.option_value IS 'If vartype is 5, provide options in format op1{val1}|op2{val2}|...';
129    ";
130  /* this is a non critical update */
131  $result = pg_query($PG_CONN, $sql);
132  DBCheckResult($result, $sql, __FILE__, __LINE__);
133  pg_free_result($result);
134  return 1;
135}
136
137
138/**
139 * \brief Populate the fossdashconfig table with core variables.
140 */
141function Populate_fossdashconfig()
142{
143  global $PG_CONN;
144
145  $columns = array("variablename", "conf_value", "ui_label", "vartype", "group_name",
146    "group_order", "description", "validation_function", "option_value");
147  $valueArray = array();
148
149  $variable = "FossdashEnableDisable";
150  $FossdashEnableDisablePrompt = _('Enable/Disable Fossdash');
151  $FossdashEnableDisableDesc = _('Start(Enable) or stop(Disable) the Fossdash');
152  $valueArray[$variable] = array("'$variable'", "'0'", "'$FossdashEnableDisablePrompt'",
153    strval(CONFIG_TYPE_DROP), "'FossDashAPI'", "1", "'$FossdashEnableDisableDesc'", "null", "'Disable{0}|Enable{1}'");
154
155  $variable = "FossDashReportingAPIUrl";
156  $fossdashApiUrlPrompt = _('FossDash Endpoint URL');
157  $URLValid = "check_fossdash_url";
158  $fossdashApiUrlDesc = _('Set the FossDash service endpoint. Disabled if empty.
159                           <br>e.g. for Source Code : <i>"http://localhost:8086/write?db=fossology_db"</i> OR for Docker Setup : <i>"http://influxdb:8086/write?db=fossology_db"</i>.');
160  $valueArray[$variable] = array("'$variable'", "null", "'$fossdashApiUrlPrompt'",
161    strval(CONFIG_TYPE_TEXT), "'FossDashAPI'", "2", "'$fossdashApiUrlDesc'", "'$URLValid'", "null");
162
163  $variable = "FossdashMetricConfig";
164  $FossdashMetricConfigPrompt = _('Fossdash metric-reporting config');
165  $FossdashMetricConfigValid = "check_fossdash_config";
166  $FossdashMetricConfigDesc = _('Modify the fossdash reporting metrics config. Leave empty to use default one.
167                                <br>e.g. Reporting config file <a target="_blank" href="https://github.com/darshank15/GSoC_2020_FOSSOlogy/wiki/Configuration-for-Fossdash-metric-reporting">Here</a>.
168                                <br>To add new query_metric : 1.Add query_metric name in <b>QUERIES_NAME</b> list. 2.Add same query_metric name and its corresponding DB_query under the <b>QUERY</b>');
169  $valueArray[$variable] = array("'$variable'", "null", "'$FossdashMetricConfigPrompt'",
170    strval(CONFIG_TYPE_TEXTAREA), "'FossDashAPI'", "3", "'$FossdashMetricConfigDesc'", "'$FossdashMetricConfigValid'", "null");
171
172  $variable = "FossDashScriptCronSchedule";
173  $FossDashScriptCronSchedulePromt = _('cron job to run script');
174  $cronIntervalCheck= "check_cron_job_inteval";
175  $FossDashScriptCronScheduleDesc = _('Set the cron job of publishing script file for pushing data to time series db.');
176  $valueArray[$variable] = array("'$variable'", "'* * * * *'", "'$FossDashScriptCronSchedulePromt'",
177    strval(CONFIG_TYPE_TEXT), "'FossDashAPI'", "4", "'$FossDashScriptCronScheduleDesc'", "'$cronIntervalCheck'", "null");
178
179  $variable = "FossologyInstanceName";
180  $FossologyInstanceNamePrompt = _('Fosslogy instance name');
181  $instanceNameValid = "check_fossology_instance_name";
182  $FossologyInstanceNameDesc = _('Set the fossology instance name, leave empty to use autogenerated UUID value.
183                                  <br>e.g. Instance name formate = <b>[a-zA-Z0-9_-]+ </b>.');
184  $valueArray[$variable] = array("'$variable'", "null", "'$FossologyInstanceNamePrompt'",
185    strval(CONFIG_TYPE_TEXT), "'FossDashAPI'", "5", "'$FossologyInstanceNameDesc'", "'$instanceNameValid'", "null");
186
187  $variable = "FossdashReportedCleaning";
188  $FossdashReportingCleaningPrompt = _('Fossdash reported files cleaning');
189  $FossdashReportingCleaningValid = "check_fossdash_cleaning";
190  $FossdashReportingCleaningDesc = _('number of days for which the successfully pushed metrics are archived. Older data will be deleted. Leave empty to disable cleanup');
191  $valueArray[$variable] = array("'$variable'", "null", "'$FossdashReportingCleaningPrompt'",
192    strval(CONFIG_TYPE_TEXT), "'FossDashAPI'", "6", "'$FossdashReportingCleaningDesc'", "'$FossdashReportingCleaningValid'", "null");
193
194  $variable = "AuthType";
195  $AuthTypePrompt = _('Auth_type for InfluxDB');
196  $AuthTypeDesc = _('Select authentication type for an InfluxDB');
197  $valueArray[$variable] = array("'$variable'", "'0'", "'$AuthTypePrompt'",
198    strval(CONFIG_TYPE_DROP), "'FossDashAPI'", "7", "'$AuthTypeDesc'", "null", "'Token_based{0}|Uname_pass{1}'");
199
200  $variable = "InfluxDBUser";
201  $InfluxDBUserPrompt = _('InlfuxDB User');
202  $InfluxDBUserValid = "check_username";
203  $InfluxDBUserDesc = _('Set the username for InfluxDB.');
204  $valueArray[$variable] = array("'$variable'", "null", "'$InfluxDBUserPrompt'",
205    strval(CONFIG_TYPE_TEXT), "'FossDashAPI'", "8", "'$InfluxDBUserDesc'", "'$InfluxDBUserValid'", "null");
206
207  $variable = "InfluxDBUserPassword";
208  $InfluxDBUserPasswordPrompt = _('InlfuxDB Password');
209  $InfluxDBUserPasswordValid = "check_password";
210  $InfluxDBUserPasswordDesc = _('Set the password for Influx user. Password must atleast of lenght=3');
211  $valueArray[$variable] = array("'$variable'", "null", "'$InfluxDBUserPasswordPrompt'",
212    strval(CONFIG_TYPE_PASSWORD), "'FossDashAPI'", "9", "'$InfluxDBUserPasswordDesc'", "'$InfluxDBUserPasswordValid'", "null");
213
214  $variable = "InfluxDBToken";
215  $InfluxDBTokenPrompt = _('InlfuxDB Encoded Token');
216  $InfluxDBTokenDesc = _('Please Enter encoded token for InfluxDB Authentication.
217                          <br>Check out the steps for <a target="_blank" href="https://github.com/darshank15/GSoC_2020_FOSSOlogy/wiki/Steps-to-generate-InfluxDB-token">Token Generation</a>.');
218  $valueArray[$variable] = array("'$variable'", "null", "'$InfluxDBTokenPrompt'",
219    strval(CONFIG_TYPE_TEXTAREA), "'FossDashAPI'", "10", "'$InfluxDBTokenDesc'", "null", "null");
220
221  /* Doing all the rows as a single insert will fail if any row is a dupe.
222   So insert each one individually so that new variables get added.
223  */
224  foreach ($valueArray as $variable => $values) {
225    /*
226     * Check if the variable already exists. Insert it if it does not.
227     * This is better than an insert ignoring duplicates, because that
228     * generates a postresql log message.
229     */
230    $VarRec = GetSingleRec("fossdashconfig", "WHERE variablename='$variable'");
231    if (empty($VarRec)) {
232      $sql = "INSERT INTO fossdashconfig (" . implode(",", $columns) . ") VALUES (" .
233        implode(",", $values) . ");";
234      $result = pg_query($PG_CONN, $sql);
235      DBCheckResult($result, $sql, __FILE__, __LINE__);
236      pg_free_result($result);
237    } else { // Values exist, update them
238      $updateString = [];
239      foreach ($columns as $index => $column) {
240        if ($index != 0 && $index != 1) { // Skip variablename and conf_value
241          $updateString[] = $column . "=" . $values[$index];
242        }
243      }
244      $sql = "UPDATE fossdashconfig SET " . implode(",", $updateString) .
245        " WHERE variablename='$variable';";
246      $result = pg_query($PG_CONN, $sql);
247      DBCheckResult($result, $sql, __FILE__, __LINE__);
248      pg_free_result($result);
249    }
250    unset($VarRec);
251  }
252
253}
254
255
256/**
257 * \brief Check if the fossdash url is valid
258 * \param string $url The url which will be checked
259 * \return 1: the url is valid, 0: invalid
260 */
261function check_fossdash_url($url)
262{
263  if (filter_var($url, FILTER_VALIDATE_URL) && preg_match("#^((http)|(https)|(ftp)|(www)|(localhost))://(.*)#", $url) == 1) {
264    return 1;
265  } else {
266    return 0;
267  }
268}
269
270/**
271 * \brief Check if the cron job schedule interval is valid
272 * \param string $cron_interval cron job interval
273 * \return 1: yes , 0: no
274 */
275function check_cron_job_inteval($cron_interval)
276{
277  $cron_regex = "#^((@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*|\*\/\d+) ?){5}))$#";
278  return preg_match($cron_regex, $cron_interval);
279}
280
281
282/**
283 * \brief Check if the fossology instance name is valid
284 * \param string $instance_name fossology instance name
285 * \return 1: yes , 0: no
286 */
287function check_fossology_instance_name($instance_name)
288{
289  $instance_UUID_regex = "#^([a-zA-Z0-9_-]+)$#";
290  return preg_match($instance_UUID_regex, $instance_name);
291}
292
293/**
294 * \brief Check if cleaning_days is valid or not
295 * \param string $cleaning_days Number of days after which successfully pushed metrics are cleaned up
296 * \return 1: yes , 0: no
297 */
298function check_fossdash_cleaning($cleaning_days)
299{
300  $numeric_day_regex = "#^[0-9]*$#";
301  return preg_match($numeric_day_regex, $cleaning_days);
302}
303
304/**
305 * \brief Check if given uname is valid or not
306 * \param string $uname username for influxDB
307 * \return 1: yes , 0: no
308 */
309function check_username($uname)
310{
311  $uname_regex = "#^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$#";
312  return preg_match($uname_regex, $uname);
313}
314
315/**
316 * \brief Check if given password is valid or not
317 * \param string $uname password for influxDB
318 * \return 1: yes , 0: no
319 */
320function check_password($password)
321{
322  $password_regex = "#^(?=.*[A-Za-z])[A-Za-z\d]{3,}$#";
323  return preg_match($password_regex, $password);
324}
325
326/**
327 * \brief Check if given config string does not contains any DB update or drop related commands
328 * \param string $config_str config for fossdash metrics
329 * \return 1: yes , 0: no
330 */
331function check_fossdash_config($config_str)
332{
333  $lower_config_str = strtolower($config_str);
334  $db_update_command_list = array("drop", "insert", "update", "alter", "truncate", "delete");
335  foreach ($db_update_command_list as $cmd) {
336    if (strpos($lower_config_str,$cmd) !== false) {
337      return 0;
338    }
339  }
340  return 1;
341}