Added GET /domains functionality

This commit is contained in:
Lukas Metzger 2018-03-21 16:51:14 +01:00
parent 54bf2a1099
commit 5f93a13412
7 changed files with 302 additions and 6 deletions

View file

@ -10,7 +10,8 @@
"Operations\\": "operations/",
"Plugins\\": "plugins/",
"Middlewares\\": "middlewares/",
"Exceptions\\": "exceptions/"
"Exceptions\\": "exceptions/",
"Utils\\": "utils/"
}
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Controllers;
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
class Domains
{
/** @var \Monolog\Logger */
private $logger;
/** @var \Slim\Container */
private $container;
public function __construct(\Slim\Container $c)
{
$this->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);
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Operations;
require '../vendor/autoload.php';
/**
* This class provides access control for the application.
*/
class AccessControl
{
/** @var \Monolog\Logger */
private $logger;
/** @var \PDO */
private $db;
public function __construct(\Slim\Container $c)
{
$this->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';
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace Operations;
require '../vendor/autoload.php';
/**
* This class provides functions for retrieving and modifying domains.
*/
class Domains
{
/** @var \Monolog\Logger */
private $logger;
/** @var \PDO */
private $db;
/** @var \Slim\Container */
private $c;
public function __construct(\Slim\Container $c)
{
$this->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);
}
}

View file

@ -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');
});

View file

@ -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);
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Utils;
require '../vendor/autoload.php';
class PagingInfo
{
/** @var int */
public $page;
/** @var int */
public $pageSize;
/** @var int */
public $totalPages;
public function __construct(? int $page, ? int $pageSize, int $totalPages = null)
{
$this->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);
});;
}
}