Added POST /domains api

This commit is contained in:
Lukas Metzger 2018-03-24 14:48:49 +01:00
parent b38cf25a75
commit 5928203a0a
9 changed files with 213 additions and 13 deletions

View file

@ -13,17 +13,17 @@ class Domains
private $logger;
/** @var \Slim\Container */
private $container;
private $c;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
$this->container = $c;
$this->c = $c;
}
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'));
$query = $req->getQueryParam('query');
@ -39,4 +39,42 @@ class Domains
'results' => $results
], 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);
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Exceptions;
require '../vendor/autoload.php';
class AlreadyExistentException extends \Exception
{
}

View file

@ -33,6 +33,8 @@ class Domains
* @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
*
* @return array Array with matching domains
*/
public function getDomains(\Utils\PagingInfo &$pi, int $userId, ? string $query, ? string $sorting, ? string $type) : array
{
@ -104,4 +106,53 @@ class Domains
return $item;
}, $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;
}
}

View file

@ -28,6 +28,7 @@ $app->group('/v1', function () {
$this->delete('/sessions/{sessionId}', '\Controllers\Sessions:delete');
$this->get('/domains', '\Controllers\Domains:getList');
$this->post('/domains', '\Controllers\Domains:postNew');
})->add('\Middlewares\Authentication');
});

View file

@ -175,10 +175,11 @@ CREATE TABLE `users` (
--
-- Dumping data for table `users`
--
--$2y$10$MktCI4XcfD0FpIFSkxex6OVifnIw3Nqw6QJueWmjVte99wx6XGBoq
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

View file

@ -1,7 +1,7 @@
const assert = require('assert');
const axios = require('axios');
async function runTest(f) {
async function runTest(user, f) {
const assertObj = {
equal: assert.deepStrictEqual,
true: assert.ok
@ -13,7 +13,7 @@ async function runTest(f) {
});
try {
const token = await logIn(assertObj, requestObj);
const token = await logIn(assertObj, requestObj, user);
requestObj = axios.create({
baseURL: process.argv[2],
@ -41,19 +41,19 @@ async function runTest(f) {
process.exit(0);
}
async function logIn(assert, req) {
async function logIn(assert, req, username) {
//Try to login with valid username and password
var res = await req({
url: '/sessions',
method: 'post',
data: {
username: 'admin',
password: 'admin'
username: username,
password: username
}
});
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');
return res.data.token;

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

View file

@ -1,6 +1,6 @@
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
//Test sorting in all combinations

View file

@ -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
var res = await req({
url: '/sessions',