From f4b06ae9107f6602d84ca20c2c8e7993b7f4221b Mon Sep 17 00:00:00 2001 From: Lukas Metzger Date: Sat, 24 Mar 2018 16:37:35 +0100 Subject: [PATCH] Added GET /domains/{domainId} --- backend/src/controllers/Domains.php | 25 +++++++- backend/src/operations/AccessControl.php | 28 +++++++++ backend/src/operations/Domains.php | 37 +++++++++++- backend/src/public/index.php | 1 + backend/test/testlib.js | 6 +- backend/test/tests/domains-crud.js | 76 ++++++++++++++++++++++-- backend/test/tests/domains-get.js | 7 ++- backend/test/tests/session.js | 7 ++- 8 files changed, 172 insertions(+), 15 deletions(-) diff --git a/backend/src/controllers/Domains.php b/backend/src/controllers/Domains.php index 2d5fe0d..66afb25 100644 --- a/backend/src/controllers/Domains.php +++ b/backend/src/controllers/Domains.php @@ -96,7 +96,30 @@ class Domains $this->logger->info('Deleted domain', ['id' => $domainId]); return $res->withStatus(204); } catch (\Exceptions\NotFoundException $e) { - return $res->withJson(['error' => 'No domain found for id ' + $domainId], 404); + return $res->withJson(['error' => 'No domain found for id ' . $domainId], 404); + } + } + + public function getSingle(Request $req, Response $res, array $args) + { + $userId = $req->getAttribute('userId'); + $domainId = intval($args['domainId']); + + $ac = new \Operations\AccessControl($this->c); + if (!$ac->canAccessDomain($userId, $domainId)) { + $this->logger->info('Non admin user tries to get domain without permission.'); + return $res->withJson(['error' => 'You have no permissions for this domain.'], 403); + } + + $domains = new \Operations\Domains($this->c); + + try { + $result = $domains->getDomain($domainId); + + $this->logger->debug('Get domain info', ['id' => $domainId]); + return $res->withJson($result, 200); + } catch (\Exceptions\NotFoundException $e) { + return $res->withJson(['error' => 'No domain found for id ' . $domainId], 404); } } } diff --git a/backend/src/operations/AccessControl.php b/backend/src/operations/AccessControl.php index 5450a17..8ce1607 100644 --- a/backend/src/operations/AccessControl.php +++ b/backend/src/operations/AccessControl.php @@ -43,4 +43,32 @@ class AccessControl return $record['type'] == 'admin'; } + + /** + * Check if a given user has permissons for a given domain. + * + * @param $userId User id of the user + * @param $domainId Domain to check + * + * @return bool true if access is granted, false otherwise + */ + public function canAccessDomain(int $userId, int $domainId) : bool + { + if ($this->isAdmin($userId)) { + return true; + } + + $query = $this->db->prepare('SELECT user_id,domain_id FROM permissions WHERE user_id=:userId AND domain_id=:domainId'); + $query->bindValue(':userId', $userId, \PDO::PARAM_INT); + $query->bindValue(':domainId', $domainId, \PDO::PARAM_INT); + $query->execute(); + + $record = $query->fetch(); + + if ($record === false) { + return false; + } else { + return true; + } + } } diff --git a/backend/src/operations/Domains.php b/backend/src/operations/Domains.php index 65539d8..cc0fe93 100644 --- a/backend/src/operations/Domains.php +++ b/backend/src/operations/Domains.php @@ -162,7 +162,7 @@ class Domains } /** - * Add new domain + * Delete domain * * @param $id Id of the domain to delete * @@ -187,4 +187,39 @@ class Domains $query->bindValue(':id', $id, \PDO::PARAM_INT); $query->execute(); } + + /** + * Get domain + * + * @param $id Id of the domain to get + * + * @return array Domain data + * + * @throws NotFoundException if domain does not exist + */ + public function getDomain(int $id) : array + { + $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 + WHERE D.id=:id + GROUP BY D.id,D.name,D.type,D.master + '); + $query->bindValue(':id', $id, \PDO::PARAM_INT); + $query->execute(); + + $record = $query->fetch(); + + if ($record === false) { + throw new \Exceptions\NotFoundException(); + } + + $record['id'] = intval($record['id']); + $record['records'] = intval($record['records']); + if ($record['type'] !== 'SLAVE') { + unset($record['master']); + } + + return $record; + } } diff --git a/backend/src/public/index.php b/backend/src/public/index.php index e5b824e..60ccda1 100644 --- a/backend/src/public/index.php +++ b/backend/src/public/index.php @@ -30,6 +30,7 @@ $app->group('/v1', function () { $this->get('/domains', '\Controllers\Domains:getList'); $this->post('/domains', '\Controllers\Domains:postNew'); $this->delete('/domains/{domainId}', '\Controllers\Domains:delete'); + $this->get('/domains/{domainId}', '\Controllers\Domains:getSingle'); })->add('\Middlewares\Authentication'); }); diff --git a/backend/test/testlib.js b/backend/test/testlib.js index 8bb7a74..4bd9f1b 100644 --- a/backend/test/testlib.js +++ b/backend/test/testlib.js @@ -37,7 +37,10 @@ async function runTest(user, f) { process.exit(1); } } +} +async function run(f) { + await f(); process.exit(0); } @@ -69,4 +72,5 @@ async function logOut(assert, req, token) { assert.equal(res.status, 204, 'LOGOUT: Answer should be successfull but empty'); } -module.exports = runTest; \ No newline at end of file +module.exports = runTest; +module.exports.run = run; \ No newline at end of file diff --git a/backend/test/tests/domains-crud.js b/backend/test/tests/domains-crud.js index 70ee621..4d95701 100644 --- a/backend/test/tests/domains-crud.js +++ b/backend/test/tests/domains-crud.js @@ -1,7 +1,7 @@ -const cartesianProduct = require('cartesian-product'); +const test = require('../testlib'); -(async function () { - await require('../testlib')('admin', async function (assert, req) { +test.run(async function () { + await test('admin', async function (assert, req) { //Test missing fields var res = await req({ url: '/domains', @@ -88,6 +88,49 @@ const cartesianProduct = require('cartesian-product'); master: '1.2.3.4' }, 'Creation result fail.') + //Get master domain + var res = await req({ + url: '/domains/6', + method: 'get' + }); + + assert.equal(res.status, 200, 'Domain access for master domain should be OK.'); + assert.equal(res.data, { + id: 6, + name: 'master.de', + type: 'MASTER', + records: 0 + }, 'Master domain data mismatch'); + + //Get native domain + var res = await req({ + url: '/domains/7', + method: 'get' + }); + + assert.equal(res.status, 200, 'Domain access for native domain should be OK.'); + assert.equal(res.data, { + id: 7, + name: 'native.de', + type: 'NATIVE', + records: 0 + }, 'Native domain data mismatch'); + + //Get slave domain + var res = await req({ + url: '/domains/8', + method: 'get' + }); + + assert.equal(res.status, 200, 'Domain access for slave domain should be OK.'); + assert.equal(res.data, { + id: 8, + name: 'slave.de', + type: 'SLAVE', + records: 0, + master: '1.2.3.4' + }, 'Slave domain data mismatch'); + //Delete not existing domain var res = await req({ url: '/domains/100', @@ -105,7 +148,7 @@ const cartesianProduct = require('cartesian-product'); assert.equal(res.status, 204, 'Deletion of domain 1 should be successfull.'); }); - await require('../testlib')('user', async function (assert, req) { + await test('user', async function (assert, req) { //Test insufficient privileges for add var res = await req({ url: '/domains', @@ -124,6 +167,27 @@ const cartesianProduct = require('cartesian-product'); }); assert.equal(res.status, 403, 'Domain deletion should be forbidden for users.'); - }); -})(); \ No newline at end of file + //Test insufficient privileges for get + var res = await req({ + url: '/domains/3', + method: 'get' + }); + + assert.equal(res.status, 403, 'Domain get for domain 3 should be forbidden.'); + + //Test privileges for get + var res = await req({ + url: '/domains/1', + method: 'get' + }); + + assert.equal(res.status, 200, 'Domain access for domain 1 should be OK.'); + assert.equal(res.data, { + id: 1, + name: 'example.com', + type: 'MASTER', + records: 1 + }, 'Domain 3 data mismatch'); + }); +}); \ No newline at end of file diff --git a/backend/test/tests/domains-get.js b/backend/test/tests/domains-get.js index 2d17e33..cd2c57a 100644 --- a/backend/test/tests/domains-get.js +++ b/backend/test/tests/domains-get.js @@ -1,7 +1,8 @@ +const test = require('../testlib'); const cartesianProduct = require('cartesian-product'); -(async function () { - require('../testlib')('admin', async function (assert, req) { +test.run(async function () { + test('admin', async function (assert, req) { //GET /domains?page=5&pagesize=10&query=foo&sort=id-asc,name-desc,type-asc,records-asc&type=MASTER //Test sorting in all combinations @@ -105,4 +106,4 @@ const cartesianProduct = require('cartesian-product'); } ], 'Result fail for ' + res.config.url); }); -})(); \ No newline at end of file +}); \ No newline at end of file diff --git a/backend/test/tests/session.js b/backend/test/tests/session.js index 58f2d3d..257ed6e 100644 --- a/backend/test/tests/session.js +++ b/backend/test/tests/session.js @@ -1,6 +1,7 @@ +const test = require('../testlib'); -(async function () { - require('../testlib')('admin', async function (assert, req) { +test.run(async function () { + test('admin', async function (assert, req) { //Try to login with invalid username and password var res = await req({ url: '/sessions', @@ -60,4 +61,4 @@ assert.equal(res.status, 201, 'Status not valid'); }); -})(); \ No newline at end of file +}); \ No newline at end of file