From cc334eb62da37baeebcc16102986d3cf51241bcf Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Thu, 2 Apr 2020 16:16:58 +0200 Subject: [PATCH] Database fixes --- core/Driver/SQL/MySQL.class.php | 26 +++++---- core/Driver/SQL/PostgreSQL.class.php | 66 +++++++++++------------ core/Objects/User.class.php | 1 - test/apiTest.py | 31 ++++++----- test/installTest.py | 79 +++++++++++----------------- test/phpTest.py | 37 +++++++++++-- test/test.py | 1 - 7 files changed, 132 insertions(+), 109 deletions(-) diff --git a/core/Driver/SQL/MySQL.class.php b/core/Driver/SQL/MySQL.class.php index b72f03e..7b79d1a 100644 --- a/core/Driver/SQL/MySQL.class.php +++ b/core/Driver/SQL/MySQL.class.php @@ -249,11 +249,7 @@ class MySQL extends SQL { $columns = array(); foreach($select->getColumns() as $col) { - if ($col instanceof Keyword) { - $columns[] = $col->getValue(); - } else { - $columns[] = "`$col`"; - } + $columns[] = $this->columnName($col); } $columns = implode(",", $columns); @@ -429,22 +425,32 @@ class MySQL extends SQL { } protected function columnName($col) { - if ($col instanceof KeyWord) { + if ($col instanceof Keyword) { return $col->getValue(); } else { - return "`$col`"; + if (($index = strrpos($col, ".")) !== FALSE) { + $tableName = $this->tableName(substr($col, 0, $index)); + $columnName = $this->columnName(substr($col, $index + 1)); + return "$tableName.$columnName"; + } else if(($index = stripos($col, " as ")) !== FALSE) { + $columnName = $this->columnName(trim(substr($col, 0, $index))); + $alias = trim(substr($col, $index + 4)); + return "$columnName as $alias"; + } else { + return "`$col`"; + } } } public function currentTimestamp() { - return new KeyWord("NOW()"); + return new Keyword("NOW()"); } public function count($col = NULL) { if (is_null($col)) { - return new Keyword("COUNT(*)"); + return new Keyword("COUNT(*) AS count"); } else { - return new Keyword("COUNT($col)"); + return new Keyword("COUNT($col) AS count"); } } diff --git a/core/Driver/SQL/PostgreSQL.class.php b/core/Driver/SQL/PostgreSQL.class.php index 90a3f43..eeabbc2 100644 --- a/core/Driver/SQL/PostgreSQL.class.php +++ b/core/Driver/SQL/PostgreSQL.class.php @@ -174,14 +174,8 @@ class PostgreSQL extends SQL { if (is_null($columns) || empty($columns)) { $columnStr = ""; - $numColumns = count($rows[0]); } else { - $numColumns = count($columns); - $columnStr = array(); - foreach($columns as $col) { - $columnStr[] = $this->columnName($col); - } - $columnStr = " (" . implode(",", $columnStr) . ")"; + $columnStr = " (" . $this->columnName($columns) . ")"; } $numRows = count($rows); @@ -235,23 +229,14 @@ class PostgreSQL extends SQL { public function executeSelect($select) { - $columns = array(); - foreach($select->getColumns() as $col) { - $columns[] = $this->columnName($col); - } - - $columns = implode(",", $columns); + $columns = $this->columnName($select->getColumns()); $tables = $select->getTables(); $params = array(); if (is_null($tables) || empty($tables)) { return "SELECT $columns"; } else { - $tableStr = array(); - foreach($tables as $table) { - $tableStr[] = $this->tableName($table); - } - $tableStr = implode(",", $tableStr); + $tableStr = $this->tableName($tables); } $conditions = $select->getConditions(); @@ -346,7 +331,7 @@ class PostgreSQL extends SQL { } protected function getColumnDefinition($column) { - $columnName = $column->getName(); + $columnName = $this->columnName($column->getName()); if ($column instanceof StringColumn) { $maxSize = $column->getMaxSize(); @@ -378,22 +363,20 @@ class PostgreSQL extends SQL { $defaultValue = " DEFAULT " . $this->getValueDefinition($column->getDefaultValue()); } - return "\"$columnName\" $type$notNull$defaultValue"; + return "$columnName $type$notNull$defaultValue"; } protected function getConstraintDefinition($constraint) { - $columnName = $constraint->getColumnName(); + $columnName = $this->columnName($constraint->getColumnName()); if ($constraint instanceof PrimaryKey) { - if (is_array($columnName)) $columnName = implode('","', $columnName); - return "PRIMARY KEY (\"$columnName\")"; + return "PRIMARY KEY ($columnName)"; } else if ($constraint instanceof Unique) { - if (is_array($columnName)) $columnName = implode('","', $columnName); - return "UNIQUE (\"$columnName\")"; + return "UNIQUE ($columnName)"; } else if ($constraint instanceof ForeignKey) { - $refTable = $constraint->getReferencedTable(); - $refColumn = $constraint->getReferencedColumn(); + $refTable = $this->tableName($constraint->getReferencedTable()); + $refColumn = $this->columnName($constraint->getReferencedColumn()); $strategy = $constraint->onDelete(); - $code = "FOREIGN KEY (\"$columnName\") REFERENCES \"$refTable\" (\"$refColumn\")"; + $code = "FOREIGN KEY ($columnName) REFERENCES $refTable ($refColumn)"; if ($strategy instanceof SetDefaultStrategy) { $code .= " ON DELETE SET DEFAULT"; } else if($strategy instanceof SetNullStrategy) { @@ -431,20 +414,33 @@ class PostgreSQL extends SQL { } protected function tableName($table) { - return "\"$table\""; + if (is_array($table)) { + $tables = array(); + foreach($table as $t) $tables[] = $this->tableName($t); + return implode(",", $tables); + } else { + return "\"$table\""; + } } protected function columnName($col) { if ($col instanceof KeyWord) { return $col->getValue(); + } elseif(is_array($col)) { + $columns = array(); + foreach($col as $c) $columns[] = $this->columnName($c); + return implode(",", $columns); } else { - $index = strrpos($col, "."); - if ($index === FALSE) { - return "\"$col\""; - } else { + if (($index = strrpos($col, ".")) !== FALSE) { $tableName = $this->tableName(substr($col, 0, $index)); $columnName = $this->columnName(substr($col, $index + 1)); return "$tableName.$columnName"; + } else if(($index = stripos($col, " as ")) !== FALSE) { + $columnName = $this->columnName(trim(substr($col, 0, $index))); + $alias = $this->columnName(trim(substr($col, $index + 4))); + return "$columnName as $alias"; + } else { + return "\"$col\""; } } } @@ -456,9 +452,9 @@ class PostgreSQL extends SQL { public function count($col = NULL) { if (is_null($col)) { - return new Keyword("COUNT(*)"); + return new Keyword("COUNT(*) AS count"); } else { - return new Keyword("COUNT(\"$col\")"); + return new Keyword("COUNT(" . $this->columnName($col) . ") AS count"); } } } diff --git a/core/Objects/User.class.php b/core/Objects/User.class.php index 2558bee..e6c2a34 100644 --- a/core/Objects/User.class.php +++ b/core/Objects/User.class.php @@ -128,7 +128,6 @@ class User extends ApiObject { $this->session->stayLoggedIn($row["stay_logged_in"]); if($sessionUpdate) $this->session->update(); $this->loggedIn = true; - if(!is_null($row['langId'])) { $this->setLangauge(Language::newInstance($row['langId'], $row['langCode'], $row['langName'])); } diff --git a/test/apiTest.py b/test/apiTest.py index 3b01207..9a6cf28 100644 --- a/test/apiTest.py +++ b/test/apiTest.py @@ -1,21 +1,28 @@ -import requests -import json - from phpTest import PhpTest class ApiTestCase(PhpTest): def __init__(self): - super().__init__("test_api") - self.session = requests.Session() + super().__init__({ + "Testing login…": self.test_login, + "Testing already logged in…": self.test_already_logged_in, + "Testing get api keys empty…": self.test_get_api_keys, + }) def api(self, method): - return "%s/api/%s" % (self.url, method) + return "/api/%s" % method - def test_api(self): - - res = self.session.post(self.api("login"), data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = json.loads(res.text) + def test_login(self): + obj = self.httpPost(self.api("login"), data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD }) self.assertEquals(True, obj["success"], obj["msg"]) + return obj + + def test_already_logged_in(self): + obj = self.test_login() + self.assertEquals("You are already logged in", obj["msg"]) + + def test_get_api_keys(self): + obj = self.httpPost(self.api("getApiKeys")) + self.assertEquals(True, obj["success"], obj["msg"]) + self.assertEquals([], obj["api_keys"]) + return obj diff --git a/test/installTest.py b/test/installTest.py index 621c064..80ce7da 100644 --- a/test/installTest.py +++ b/test/installTest.py @@ -1,68 +1,53 @@ -import requests - from phpTest import PhpTest +import sys + class InstallTestCase(PhpTest): def __init__(self, args): - super().__init__("test_install") + super().__init__({ + "Testing connection…": self.test_connection, + "Testing database setup…": self.test_database_setup, + "Testing invalid usernames…": self.test_invalid_usernames, + "Testing invalid password…": self.test_invalid_password, + "Testing not matching password…": self.test_not_matching_passwords, + "Testing user creation…": self.test_create_user, + "Testing skip mail configuration…": self.test_skil_mail_config, + "Testing complete setup…": self.test_complete_setup, + }) self.args = args - self.session = requests.Session() - def test_install(self): + def test_connection(self): + self.httpGet() - # Test Connection - res = self.session.get(self.url) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) + def test_database_setup(self): + obj = self.httpPost(data=vars(self.args)) + self.assertEquals(True, obj["success"], obj["msg"]) - # Database Setup - res = self.session.post(self.url, data=vars(self.args)) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - - # Create User - - # 1. Invalid username + def test_invalid_usernames(self): for username in ["a", "a"*33]: - res = self.session.post(self.url, data={ "username": username, "password": "123456", "confirmPassword": "123456" }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = self.getJson(res) + obj = self.httpPost(data={ "username": username, "password": "123456", "confirmPassword": "123456" }) self.assertEquals(False, obj["success"]) self.assertEquals("The username should be between 5 and 32 characters long", obj["msg"]) - # 2. Invalid password - res = self.session.post(self.url, data={ "username": PhpTest.ADMIN_USERNAME, "password": "1", "confirmPassword": "1" }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = self.getJson(res) + def test_invalid_password(self): + obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": "1", "confirmPassword": "1" }) self.assertEquals(False, obj["success"]) self.assertEquals("The password should be at least 6 characters long", obj["msg"]) - # 3. Passwords do not match - res = self.session.post(self.url, data={ "username": PhpTest.ADMIN_USERNAME, "password": "1", "confirmPassword": "2" }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = self.getJson(res) + def test_not_matching_passwords(self): + obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": "1", "confirmPassword": "2" }) self.assertEquals(False, obj["success"]) self.assertEquals("The given passwords do not match", obj["msg"]) - # 4. User creation OK - res = self.session.post(self.url, data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD, "confirmPassword": PhpTest.ADMIN_PASSWORD }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = self.getJson(res) - self.assertEquals(True, obj["success"]) + def test_create_user(self): + obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD, "confirmPassword": PhpTest.ADMIN_PASSWORD }) + self.assertEquals(True, obj["success"], obj["msg"]) - # Mail: SKIP - res = self.session.post(self.url, data={ "skip": "true" }) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) - obj = self.getJson(res) - self.assertEquals(True, obj["success"]) + def test_skil_mail_config(self): + obj = self.httpPost(data={ "skip": "true" }) + self.assertEquals(True, obj["success"], obj["msg"]) - # Creation successful: - res = self.session.get(self.url) - self.assertEquals(200, res.status_code, self.httpError(res)) - self.assertEquals([], self.getPhpErrors(res)) + def test_complete_setup(self): + res = self.httpGet() + self.assertTrue("Installation finished" in res.text) diff --git a/test/phpTest.py b/test/phpTest.py index 0c251f9..19ca157 100644 --- a/test/phpTest.py +++ b/test/phpTest.py @@ -1,8 +1,10 @@ import unittest import string import random +import requests import re import json +import sys class PhpTest(unittest.TestCase): @@ -13,11 +15,13 @@ class PhpTest(unittest.TestCase): ADMIN_USERNAME = "Administrator" ADMIN_PASSWORD = randomString(16) - def __init__(self, test_method): - super().__init__(test_method) + def __init__(self, methods): + super().__init__("test_methods") keywords = ["Fatal error", "Warning", "Notice", "Parse error", "Deprecated"] + self.methods = methods self.phpPattern = re.compile("(%s):" % ("|".join(keywords))) - self.url = "http://localhost/" + self.url = "http://localhost" + self.session = requests.Session() def httpError(self, res): return "Server returned: %d %s" % (res.status_code, res.reason) @@ -25,6 +29,21 @@ class PhpTest(unittest.TestCase): def getPhpErrors(self, res): return [line for line in res.text.split("\n") if self.phpPattern.search(line)] + def httpGet(self, target="/"): + url = self.url + target + res = self.session.get(url) + self.assertEquals(200, res.status_code, self.httpError(res)) + self.assertEquals([], self.getPhpErrors(res)) + return res + + def httpPost(self, target="/", data={}): + url = self.url + target + res = self.session.post(url, data=data) + self.assertEquals(200, res.status_code, self.httpError(res)) + self.assertEquals([], self.getPhpErrors(res)) + obj = self.getJson(res) + return obj + def getJson(self, res): obj = None try: @@ -34,3 +53,15 @@ class PhpTest(unittest.TestCase): finally: self.assertTrue(isinstance(obj, dict), res.text) return obj + + def test_methods(self): + print() + print("Running Tests in %s" % self.__class__.__name__) + for msg, method in self.methods.items(): + self.test_method(msg, method) + + def test_method(self, msg, method): + sys.stdout.write("[ ] %s" % msg) + method() + sys.stdout.write("\r[+] %s\n" % msg) + sys.stdout.flush() diff --git a/test/test.py b/test/test.py index 50a57a1..eb63682 100644 --- a/test/test.py +++ b/test/test.py @@ -44,7 +44,6 @@ def testMysql(args): cursor = connection.cursor() print("[ ] Creating temporary databse %s" % args.database) cursor.execute("CREATE DATABASE %s" % args.database) - cursor.commit() print("[+] Success") # perform test