diff --git a/backend/composer.json b/backend/composer.json index 563e157..f982093 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -10,7 +10,8 @@ "Operations\\": "operations/", "Plugins\\": "plugins/", "Middlewares\\": "middlewares/", - "Exceptions\\": "exceptions/" + "Exceptions\\": "exceptions/", + "Utils\\": "utils/" } } } \ No newline at end of file diff --git a/backend/controllers/Domains.php b/backend/controllers/Domains.php new file mode 100644 index 0000000..7cab32a --- /dev/null +++ b/backend/controllers/Domains.php @@ -0,0 +1,42 @@ +logger = $c->logger; + $this->container = $c; + } + + public function getList(Request $req, Response $res, array $args) + { + $domains = new \Operations\Domains($this->container); + + $paging = new \Utils\PagingInfo($req->getQueryParam('page'), $req->getQueryParam('pagesize')); + $query = $req->getQueryParam('query'); + $sort = $req->getQueryParam('sort'); + $type = $req->getQueryParam('type'); + + $userId = $req->getAttribute('userId'); + + $results = $domains->getDomains($paging, $userId, $query, $sort, $type); + + return $res->withJson([ + 'paging' => $paging->toArray(), + 'results' => $results + ], 200); + } +} diff --git a/backend/operations/AccessControl.php b/backend/operations/AccessControl.php new file mode 100644 index 0000000..05d7fc3 --- /dev/null +++ b/backend/operations/AccessControl.php @@ -0,0 +1,46 @@ +logger = $c->logger; + $this->db = $c->db; + } + + /** + * Determines if the given user has admin privileges. + * + * @param $userId User id of the user + * + * @return bool true if admin, false otherwise + */ + public function isAdmin(int $userId) : bool + { + $query = $this->db->prepare('SELECT type FROM users WHERE id=:id'); + $query->bindValue(':id', $userId, \PDO::PARAM_STR); + $query->execute(); + + $record = $query->fetch(); + + if ($record === false) { + $this->logger->error('Queried record for non existing user id, this should not happen.', ['userId' => $userId]); + return false; + } + + return $record['type'] == 'admin'; + } +} \ No newline at end of file diff --git a/backend/operations/Domains.php b/backend/operations/Domains.php new file mode 100644 index 0000000..d2d110b --- /dev/null +++ b/backend/operations/Domains.php @@ -0,0 +1,107 @@ +logger = $c->logger; + $this->db = $c->db; + $this->c = $c; + } + + /** + * Get a list of domains according to filter criteria + * + * @param $pi PageInfo object, which is also updated with total page number + * @param $userId Id of the user for which the table should be retrieved + * @param $query Search query to search in the domain name, null for no filter + * @param $sorting Sort string in format 'field-asc,field2-desc', null for default + * @param $type Type to filter for, null for no filter + */ + public function getDomains(\Utils\PagingInfo &$pi, int $userId, ? string $query, ? string $sorting, ? string $type) : array + { + $ac = new \Operations\AccessControl($this->c); + $userIsAdmin = $ac->isAdmin($userId); + + $queryStr = $query === null ? '%' : '%' . $query . '%'; + + //Count elements + if ($pi->pageSize === null) { + $pi->totalPages = 1; + } else { + $query = $this->db->prepare(' + SELECT COUNT(*) AS total + FROM domains D + LEFT OUTER JOIN permissions P ON D.id = P.domain_id + WHERE (P.user_id=:userId OR :userIsAdmin) AND + (D.name LIKE :nameQuery) AND + (D.type = :domainType OR :noTypeFilter) + '); + + $query->bindValue(':userId', $userId, \PDO::PARAM_INT); + $query->bindValue(':userIsAdmin', intval($userIsAdmin), \PDO::PARAM_INT); + $query->bindValue(':nameQuery', $queryStr, \PDO::PARAM_STR); + $query->bindValue(':domainType', (string)$type, \PDO::PARAM_STR); + $query->bindValue(':noTypeFilter', intval($type === null), \PDO::PARAM_INT); + + $query->execute(); + $record = $query->fetch(); + + $pi->totalPages = ceil($record['total'] / $pi->pageSize); + } + + //Query and return result + $ordStr = \Services\Database::makeSortingString($sorting, [ + 'id' => 'D.id', + 'name' => 'D.name', + 'type' => 'D.type', + 'records' => 'records' + ]); + $pageStr = \Services\Database::makePagingString($pi); + + $query = $this->db->prepare(' + SELECT D.id,D.name,D.type,D.master,count(R.domain_id) AS records + FROM domains D + LEFT OUTER JOIN records R ON D.id = R.domain_id + LEFT OUTER JOIN permissions P ON D.id = P.domain_id + WHERE (P.user_id=:userId OR :userIsAdmin) + GROUP BY D.id + HAVING + (D.name LIKE :nameQuery) AND + (D.type=:domainType OR :noTypeFilter)' + . $ordStr . $pageStr); + + $query->bindValue(':userId', $userId, \PDO::PARAM_INT); + $query->bindValue(':userIsAdmin', intval($userIsAdmin), \PDO::PARAM_INT); + $query->bindValue(':nameQuery', $queryStr, \PDO::PARAM_STR); + $query->bindValue(':domainType', (string)$type, \PDO::PARAM_STR); + $query->bindValue(':noTypeFilter', intval($type === null), \PDO::PARAM_INT); + + $query->execute(); + + $data = $query->fetchAll(); + + return array_map(function ($item) { + if ($item['type'] != 'SLAVE') { + unset($item['master']); + } + return $item; + }, $data); + } +} \ No newline at end of file diff --git a/backend/public/index.php b/backend/public/index.php index c5a4ffe..8b24107 100644 --- a/backend/public/index.php +++ b/backend/public/index.php @@ -26,6 +26,8 @@ $app->group('/v1', function () { $this->group('', function () { $this->delete('/sessions/{sessionId}', '\Controllers\Sessions:delete'); + + $this->get('/domains', '\Controllers\Domains:getList'); })->add('\Middlewares\Authentication'); }); diff --git a/backend/services/Database.php b/backend/services/Database.php index 0e868f8..e905989 100644 --- a/backend/services/Database.php +++ b/backend/services/Database.php @@ -12,10 +12,10 @@ class Database try { $pdo = new \PDO( - 'mysql:host=' . $config['host'] . ';port=' . $config['port'] . ';dbname=' . $config['dbname'], - $config['user'], - $config['password'] - ); + 'mysql:host=' . $config['host'] . ';port=' . $config['port'] . ';dbname=' . $config['dbname'], + $config['user'], + $config['password'] + ); } catch (\PDOException $e) { $c->logger->critical("SQL Connect Error: " . $e->getMessage()); $c->logger->critical("DB Config was", $config); @@ -31,7 +31,68 @@ class Database } $c->logger->debug("Database setup successfull"); - + return $pdo; } + + /** + * Makes a SQL LIMIT string from paging information + * + * @param $pi PagingInfo object to use + * + * @return string SQL string to use + */ + public static function makePagingString(\Utils\PagingInfo $pi) : string + { + if ($pi->pageSize === null) { + return ''; + } + + if ($pi->page === null) { + $pi->page = 1; + } + + $offset = ($pi->page - 1) * $pi->pageSize; + + return ' LIMIT ' . intval($pi->pageSize) . ' OFFSET ' . intval($offset); + } + + /** + * Makes a SQL ORDER BY string from order information. + * + * This is done from a string with format 'field-asc,field2-desc' + * where fields are mapped to columns in param $colMap. This also + * should prevent SQL injections. + * + * @param $sort Sort string + * @param $colMap Map which assigns to each field name a column to use + * + * @return string SQL string to use + */ + public static function makeSortingString(? string $sort, array $colMap) + { + if ($sort === null) { + return ''; + } + + $orderStrings = []; + + foreach (explode(',', $sort) as $value) { + $parts = explode('-', $value); + + if (array_key_exists($parts[0], $colMap) && count($parts) == 2) { // is valid known field + if ($parts[1] == 'asc') { + $orderStrings[] = $colMap[$parts[0]] . ' ASC'; + } else if ($parts[1] == 'desc') { + $orderStrings[] = $colMap[$parts[0]] . ' DESC'; + } + } + } + + if (count($orderStrings) == 0) { // none was valid + return ''; + } + + return ' ORDER BY ' . implode(', ', $orderStrings); + } } diff --git a/backend/utils/PagingInfo.php b/backend/utils/PagingInfo.php new file mode 100644 index 0000000..8cf2752 --- /dev/null +++ b/backend/utils/PagingInfo.php @@ -0,0 +1,37 @@ +page = $page === null ? 1 : $page; + $this->pageSize = $pageSize; + $this->totalPages = $totalPages; + } + + public function toArray() + { + $val = [ + 'page' => $this->page, + 'total' => $this->totalPages, + 'pagesize' => $this->pageSize + ]; + + return array_filter($val, function ($var) { + return !is_null($var); + });; + } +}