Added POST /domains api
This commit is contained in:
parent
b38cf25a75
commit
5928203a0a
|
@ -13,17 +13,17 @@ class Domains
|
||||||
private $logger;
|
private $logger;
|
||||||
|
|
||||||
/** @var \Slim\Container */
|
/** @var \Slim\Container */
|
||||||
private $container;
|
private $c;
|
||||||
|
|
||||||
public function __construct(\Slim\Container $c)
|
public function __construct(\Slim\Container $c)
|
||||||
{
|
{
|
||||||
$this->logger = $c->logger;
|
$this->logger = $c->logger;
|
||||||
$this->container = $c;
|
$this->c = $c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getList(Request $req, Response $res, array $args)
|
public function getList(Request $req, Response $res, array $args)
|
||||||
{
|
{
|
||||||
$domains = new \Operations\Domains($this->container);
|
$domains = new \Operations\Domains($this->c);
|
||||||
|
|
||||||
$paging = new \Utils\PagingInfo($req->getQueryParam('page'), $req->getQueryParam('pagesize'));
|
$paging = new \Utils\PagingInfo($req->getQueryParam('page'), $req->getQueryParam('pagesize'));
|
||||||
$query = $req->getQueryParam('query');
|
$query = $req->getQueryParam('query');
|
||||||
|
@ -39,4 +39,42 @@ class Domains
|
||||||
'results' => $results
|
'results' => $results
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function postNew(Request $req, Response $res, array $args)
|
||||||
|
{
|
||||||
|
$ac = new \Operations\AccessControl($this->c);
|
||||||
|
if (!$ac->isAdmin($req->getAttribute('userId'))) {
|
||||||
|
$this->logger->info('Non admin user tries to add domain');
|
||||||
|
return $res->withJson(['error' => 'You must be admin to use this feature'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $req->getParsedBody();
|
||||||
|
|
||||||
|
if (!array_key_exists('name', $body) ||
|
||||||
|
!array_key_exists('type', $body) || ($body['type'] === 'SLAVE' && !array_key_exists('master', $body))) {
|
||||||
|
$this->logger->debug('One of the required fields is missing');
|
||||||
|
return $res->withJson(['error' => 'One of the required fields is missing'], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $body['name'];
|
||||||
|
$type = $body['type'];
|
||||||
|
$master = isset($body['master']) ? $body['master'] : null;
|
||||||
|
|
||||||
|
if (!in_array($type, ['MASTER', 'NATIVE', 'SLAVE'])) {
|
||||||
|
$this->logger->info('Invalid type for new domain', ['type' => $type]);
|
||||||
|
return $res->withJson(['error' => 'Invalid type allowed are MASTER, NATIVE and SLAVE'], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$domains = new \Operations\Domains($this->c);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $domains->addDomain($name, $type, $master);
|
||||||
|
|
||||||
|
$this->logger->debug('Domain was added', $result);
|
||||||
|
return $res->withJson($result, 201);
|
||||||
|
} catch (\Exceptions\AlreadyExistentException $e) {
|
||||||
|
$this->logger->debug('Zone with name ' . $name . ' already exists.');
|
||||||
|
return $res->withJson(['error' => 'Zone with name ' . $name . ' already exists.'], 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
9
backend/src/exceptions/AlreadyExistentException.php
Normal file
9
backend/src/exceptions/AlreadyExistentException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
require '../vendor/autoload.php';
|
||||||
|
|
||||||
|
class AlreadyExistentException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -33,6 +33,8 @@ class Domains
|
||||||
* @param $query Search query to search in the domain name, null for no filter
|
* @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 $sorting Sort string in format 'field-asc,field2-desc', null for default
|
||||||
* @param $type Type to filter for, null for no filter
|
* @param $type Type to filter for, null for no filter
|
||||||
|
*
|
||||||
|
* @return array Array with matching domains
|
||||||
*/
|
*/
|
||||||
public function getDomains(\Utils\PagingInfo &$pi, int $userId, ? string $query, ? string $sorting, ? string $type) : array
|
public function getDomains(\Utils\PagingInfo &$pi, int $userId, ? string $query, ? string $sorting, ? string $type) : array
|
||||||
{
|
{
|
||||||
|
@ -104,4 +106,53 @@ class Domains
|
||||||
return $item;
|
return $item;
|
||||||
}, $data);
|
}, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of domains according to filter criteria
|
||||||
|
*
|
||||||
|
* @param $name Name of the new zone
|
||||||
|
* @param $type Type of the new zone
|
||||||
|
* @param $master Master for slave zones, otherwise null
|
||||||
|
*
|
||||||
|
* @return array New domain entry
|
||||||
|
*/
|
||||||
|
public function addDomain(string $name, string $type, ? string $master)
|
||||||
|
{
|
||||||
|
$this->db->beginTransaction();
|
||||||
|
|
||||||
|
$query = $this->db->prepare('SELECT id FROM domains WHERE name=:name');
|
||||||
|
$query->bindValue(':name', $name, \PDO::PARAM_STR);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$record = $query->fetch();
|
||||||
|
|
||||||
|
if ($record !== false) { // Domain already exists
|
||||||
|
$this->db->rollBack();
|
||||||
|
throw new \Exceptions\AlreadyExistentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type === 'SLAVE') {
|
||||||
|
$query = $this->db->prepare('INSERT INTO domains (name, type, master) VALUES(:name, :type, :master)');
|
||||||
|
$query->bindValue(':master', $master, \PDO::PARAM_STR);
|
||||||
|
} else {
|
||||||
|
$query = $this->db->prepare('INSERT INTO domains (name, type) VALUES(:name, :type)');
|
||||||
|
}
|
||||||
|
$query->bindValue(':name', $name, \PDO::PARAM_STR);
|
||||||
|
$query->bindValue(':type', $type, \PDO::PARAM_STR);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
|
||||||
|
$query = $this->db->prepare('SELECT id,name,type,master FROM domains WHERE name=:name');
|
||||||
|
$query->bindValue(':name', $name, \PDO::PARAM_STR);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$record = $query->fetch();
|
||||||
|
if ($type !== 'SLAVE') {
|
||||||
|
unset($record['master']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->commit();
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ $app->group('/v1', function () {
|
||||||
$this->delete('/sessions/{sessionId}', '\Controllers\Sessions:delete');
|
$this->delete('/sessions/{sessionId}', '\Controllers\Sessions:delete');
|
||||||
|
|
||||||
$this->get('/domains', '\Controllers\Domains:getList');
|
$this->get('/domains', '\Controllers\Domains:getList');
|
||||||
|
$this->post('/domains', '\Controllers\Domains:postNew');
|
||||||
})->add('\Middlewares\Authentication');
|
})->add('\Middlewares\Authentication');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -175,10 +175,11 @@ CREATE TABLE `users` (
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Dumping data for table `users`
|
-- Dumping data for table `users`
|
||||||
--
|
--$2y$10$MktCI4XcfD0FpIFSkxex6OVifnIw3Nqw6QJueWmjVte99wx6XGBoq
|
||||||
|
|
||||||
INSERT INTO `users` (`id`, `name`, `backend`, `type`, `password`) VALUES
|
INSERT INTO `users` (`id`, `name`, `backend`, `type`, `password`) VALUES
|
||||||
(1, 'admin', 'native', 'admin', '$2y$10$9iIDHWgjY0pEsz8pZLXPx.gkMNDxTMzb7U0Um5hUGjKmUUHWQNXcW');
|
(1, 'admin', 'native', 'admin', '$2y$10$9iIDHWgjY0pEsz8pZLXPx.gkMNDxTMzb7U0Um5hUGjKmUUHWQNXcW'),
|
||||||
|
(2, 'user', 'native', 'user', '$2y$10$MktCI4XcfD0FpIFSkxex6OVifnIw3Nqw6QJueWmjVte99wx6XGBoq');
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Indexes for dumped tables
|
-- Indexes for dumped tables
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
async function runTest(f) {
|
async function runTest(user, f) {
|
||||||
const assertObj = {
|
const assertObj = {
|
||||||
equal: assert.deepStrictEqual,
|
equal: assert.deepStrictEqual,
|
||||||
true: assert.ok
|
true: assert.ok
|
||||||
|
@ -13,7 +13,7 @@ async function runTest(f) {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = await logIn(assertObj, requestObj);
|
const token = await logIn(assertObj, requestObj, user);
|
||||||
|
|
||||||
requestObj = axios.create({
|
requestObj = axios.create({
|
||||||
baseURL: process.argv[2],
|
baseURL: process.argv[2],
|
||||||
|
@ -41,19 +41,19 @@ async function runTest(f) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logIn(assert, req) {
|
async function logIn(assert, req, username) {
|
||||||
//Try to login with valid username and password
|
//Try to login with valid username and password
|
||||||
var res = await req({
|
var res = await req({
|
||||||
url: '/sessions',
|
url: '/sessions',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: {
|
data: {
|
||||||
username: 'admin',
|
username: username,
|
||||||
password: 'admin'
|
password: username
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(res.status, 201, 'LOGIN: Status not valid');
|
assert.equal(res.status, 201, 'LOGIN: Status not valid');
|
||||||
assert.equal(res.data.username, 'admin', 'LOGIN: Username should be admin');
|
assert.equal(res.data.username, username, 'LOGIN: Username should be ' + username);
|
||||||
assert.equal(res.data.token.length, 86, 'LOGIN: Token length fail');
|
assert.equal(res.data.token.length, 86, 'LOGIN: Token length fail');
|
||||||
|
|
||||||
return res.data.token;
|
return res.data.token;
|
||||||
|
|
100
backend/test/tests/domains-crud.js
Normal file
100
backend/test/tests/domains-crud.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
const cartesianProduct = require('cartesian-product');
|
||||||
|
|
||||||
|
require('../testlib')('admin', async function (assert, req) {
|
||||||
|
//Test missing fields
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'abc.de'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 422, 'Missing type filed should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'abc.de',
|
||||||
|
type: 'SLAVE'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 422, 'Missing master field for SLAVE domain should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'foo.de',
|
||||||
|
type: 'MASTER'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 409, 'Existing domain should trigger error.');
|
||||||
|
|
||||||
|
//Test creation of master zone
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'master.de',
|
||||||
|
type: 'MASTER'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 201, 'Creation should be successfull');
|
||||||
|
assert.equal(res.data, {
|
||||||
|
id: '6',
|
||||||
|
name: 'master.de',
|
||||||
|
type: 'MASTER'
|
||||||
|
}, 'Creation result fail.')
|
||||||
|
|
||||||
|
//Test creation of native zone
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'native.de',
|
||||||
|
type: 'NATIVE'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 201, 'Creation should be successfull');
|
||||||
|
assert.equal(res.data, {
|
||||||
|
id: '7',
|
||||||
|
name: 'native.de',
|
||||||
|
type: 'NATIVE'
|
||||||
|
}, 'Creation result fail.')
|
||||||
|
|
||||||
|
//Test creation of slave zone
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'slave.de',
|
||||||
|
type: 'SLAVE'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 201, 'Creation should be successfull');
|
||||||
|
assert.equal(res.data, {
|
||||||
|
id: '8',
|
||||||
|
name: 'slave.de',
|
||||||
|
type: 'SLAVE'
|
||||||
|
}, 'Creation result fail.')
|
||||||
|
});
|
||||||
|
|
||||||
|
require('../testlib')('user', async function (assert, req) {
|
||||||
|
//Test insufficient privileges
|
||||||
|
var res = await req({
|
||||||
|
url: '/domains',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
name: 'foo.de'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 403, 'Domain creation should be forbidden for users.')
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
const cartesianProduct = require('cartesian-product');
|
const cartesianProduct = require('cartesian-product');
|
||||||
|
|
||||||
require('../testlib')(async function (assert, req) {
|
require('../testlib')('admin', async function (assert, req) {
|
||||||
//GET /domains?page=5&pagesize=10&query=foo&sort=id-asc,name-desc,type-asc,records-asc&type=MASTER
|
//GET /domains?page=5&pagesize=10&query=foo&sort=id-asc,name-desc,type-asc,records-asc&type=MASTER
|
||||||
|
|
||||||
//Test sorting in all combinations
|
//Test sorting in all combinations
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
require('../testlib')(async function (assert, req) {
|
require('../testlib')('admin', async function (assert, req) {
|
||||||
//Try to login with invalid username and password
|
//Try to login with invalid username and password
|
||||||
var res = await req({
|
var res = await req({
|
||||||
url: '/sessions',
|
url: '/sessions',
|
||||||
|
|
Loading…
Reference in a new issue