mirror of
https://github.com/loewexy/pdnsmanager.git
synced 2025-01-26 07:24:10 +01:00
Added PUT /domains/{domainId}/soa
This commit is contained in:
parent
01cd32e27c
commit
28c0b0d08d
7 changed files with 306 additions and 3 deletions
backend
src
test
|
@ -156,4 +156,50 @@ class Domains
|
|||
return $res->withJson(['error' => 'Domain is not a slave zone'], 405);
|
||||
}
|
||||
}
|
||||
|
||||
public function putSoa(Request $req, Response $res, array $args)
|
||||
{
|
||||
$userId = $req->getAttribute('userId');
|
||||
$domainId = $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);
|
||||
}
|
||||
|
||||
$body = $req->getParsedBody();
|
||||
|
||||
if (!array_key_exists('primary', $body) ||
|
||||
!array_key_exists('email', $body) ||
|
||||
!array_key_exists('refresh', $body) ||
|
||||
!array_key_exists('retry', $body) ||
|
||||
!array_key_exists('expire', $body) ||
|
||||
!array_key_exists('ttl', $body)) {
|
||||
$this->logger->debug('One of the required fields is missing');
|
||||
return $res->withJson(['error' => 'One of the required fields is missing'], 422);
|
||||
}
|
||||
|
||||
$soa = new \Operations\Soa($this->c);
|
||||
|
||||
try {
|
||||
$soa->setSoa(
|
||||
intval($domainId),
|
||||
$body['email'],
|
||||
$body['primary'],
|
||||
intval($body['refresh']),
|
||||
intval($body['retry']),
|
||||
intval($body['expire']),
|
||||
intval($body['ttl'])
|
||||
);
|
||||
|
||||
return $res->withStatus(204);
|
||||
} catch (\Exceptions\NotFoundException $e) {
|
||||
$this->logger->warning('Trying to set soa for not existing domain.', ['domainId' => $domainId]);
|
||||
return $res->withJson(['error' => 'No domain found for id ' . $domainId], 404);
|
||||
} catch (\Exceptions\SemanticException $e) {
|
||||
$this->logger->warning('Trying to set soa for slave domain.', ['domainId' => $domainId]);
|
||||
return $res->withJson(['error' => 'SOA can not be set for slave domains'], 405);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
188
backend/src/operations/Soa.php
Normal file
188
backend/src/operations/Soa.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace Operations;
|
||||
|
||||
require '../vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* This class provides functions for retrieving and modifying soa records.
|
||||
*/
|
||||
class Soa
|
||||
{
|
||||
/** @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 $domainId Domain to update soa
|
||||
* @param $mail Mail of zone master
|
||||
* @param $primary The primary nameserver
|
||||
* @param $refresh The refresh interval
|
||||
* @param $retry The retry interval
|
||||
* @param $expire The expire timeframe
|
||||
* @param $ttl The zone ttl
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws NotFoundException If the given domain does not exist
|
||||
*/
|
||||
public function setSoa(int $domainId, string $mail, string $primary, int $refresh, int $retry, int $expire, int $ttl)
|
||||
{
|
||||
$this->db->beginTransaction();
|
||||
|
||||
$query = $this->db->prepare('SELECT id,name,type FROM domains WHERE id=:id');
|
||||
$query->bindValue(':id', $domainId);
|
||||
$query->execute();
|
||||
$record = $query->fetch();
|
||||
|
||||
if ($record === false) {
|
||||
$this->db->rollBack();
|
||||
throw new \Exceptions\NotFoundException();
|
||||
} elseif ($record['type'] === 'SLAVE') {
|
||||
$this->db->rollBack();
|
||||
throw new \Exceptions\SemanticException();
|
||||
} else {
|
||||
$domainName = $record['name'];
|
||||
}
|
||||
|
||||
//Generate soa content string without serial
|
||||
$soaArray = [
|
||||
$primary,
|
||||
$this->fromEmail($mail),
|
||||
'serial',
|
||||
$refresh,
|
||||
$retry,
|
||||
$expire,
|
||||
$ttl
|
||||
];
|
||||
|
||||
$query = $this->db->prepare('SELECT content FROM records WHERE domain_id=:id AND type=\'SOA\'');
|
||||
$query->bindValue(':id', $domainId);
|
||||
$query->execute();
|
||||
|
||||
$content = $query->fetch();
|
||||
|
||||
if ($content === false) { //No soa exists yet
|
||||
$soaArray[2] = strval($this->calculateSerial(0));
|
||||
$soaString = implode(' ', $soaArray);
|
||||
$changeDate = strval(time());
|
||||
|
||||
$query = $this->db->prepare('
|
||||
INSERT INTO records (domain_id, name, type, content, ttl, change_date)
|
||||
VALUES (:domainId, :name, \'SOA\', :content, :ttl, :changeDate)
|
||||
');
|
||||
$query->bindValue(':domainId', $domainId, \PDO::PARAM_INT);
|
||||
$query->bindValue(':name', $domainName, \PDO::PARAM_STR);
|
||||
$query->bindValue(':content', $soaString, \PDO::PARAM_STR);
|
||||
$query->bindValue(':ttl', $ttl, \PDO::PARAM_STR);
|
||||
$query->bindValue(':changeDate', $changeDate, \PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
} else {
|
||||
$oldSerial = intval(explode(' ', $content['content'])[2]);
|
||||
|
||||
$soaArray[2] = strval($this->calculateSerial($oldSerial));
|
||||
$soaString = implode(' ', $soaArray);
|
||||
$changeDate = strval(time());
|
||||
|
||||
$query = $this->db->prepare('UPDATE records SET content=:content, ttl=:ttl,
|
||||
change_date=:changeDate WHERE domain_id=:domainId AND type=\'SOA\'');
|
||||
$query->bindValue(':domainId', $domainId, \PDO::PARAM_INT);
|
||||
$query->bindValue(':content', $soaString, \PDO::PARAM_STR);
|
||||
$query->bindValue(':ttl', $ttl, \PDO::PARAM_STR);
|
||||
$query->bindValue(':changeDate', $changeDate, \PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
$this->db->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the serial number of the given domain to the next required.
|
||||
*
|
||||
* If domain has no present soa record this method does nothing.
|
||||
*
|
||||
* @param $domainId Domain to update
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateSerial(int $domainId) : void
|
||||
{
|
||||
$query = $this->db->prepare('SELECT content FROM records WHERE domain_id=:id AND type=\'SOA\'');
|
||||
$query->bindValue(':id', $domainId);
|
||||
$query->execute();
|
||||
$content = $query->fetch();
|
||||
|
||||
if ($content === false) {
|
||||
$this->logger->warning('Trying to update serial of domain without soa set it first', ['domainId' => $domainId]);
|
||||
return;
|
||||
}
|
||||
|
||||
$soaArray = explode(' ', $content['content']);
|
||||
$soaArray[2] = strval($this->calculateSerial(intval($soaArray[2])));
|
||||
$soaString = implode(' ', $soaArray);
|
||||
|
||||
$query = $this->db->prepare('UPDATE records SET content=:content WHERE domain_id=:domainId AND type=\'SOA\'');
|
||||
$query->bindValue(':content', $soaString, \PDO::PARAM_STR);
|
||||
$query->bindValue(':domainId', $domainId, \PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate new serial from old
|
||||
*
|
||||
* @param $oldSerial Old serial number
|
||||
*
|
||||
* @return int New serial number
|
||||
*/
|
||||
private function calculateSerial(int $oldSerial) : int
|
||||
{
|
||||
$time = new \DateTime(null, new \DateTimeZone('UTC'));
|
||||
$currentTime = intval($time->format('Ymd')) * 100;
|
||||
|
||||
return \max($oldSerial + 1, $currentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert email to soa mail string
|
||||
*
|
||||
* @param $email Email address
|
||||
*
|
||||
* @return string Soa email address
|
||||
*/
|
||||
private function fromEmail(string $email)
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
$parts[0] = str_replace('.', '\.', $parts[0]);
|
||||
$parts[] = '';
|
||||
return rtrim(implode(".", $parts), ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert soa mail to mail string
|
||||
*
|
||||
* @param $soaMail Soa email address
|
||||
*
|
||||
* @return string Email address
|
||||
*/
|
||||
private function toEmail(string $soaEmail)
|
||||
{
|
||||
$tmp = preg_replace('/([^\\\\])\\./', '\\1@', $soaEmail, 1);
|
||||
$tmp = preg_replace('/\\\\\\./', ".", $tmp);
|
||||
$tmp = preg_replace('/\\.$/', "", $tmp);
|
||||
return $tmp;
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ $app->group('/v1', function () {
|
|||
$this->delete('/domains/{domainId}', '\Controllers\Domains:delete');
|
||||
$this->get('/domains/{domainId}', '\Controllers\Domains:getSingle');
|
||||
$this->put('/domains/{domainId}', '\Controllers\Domains:put');
|
||||
|
||||
$this->put('/domains/{domainId}/soa', '\Controllers\Domains:putSoa');
|
||||
})->add('\Middlewares\Authentication');
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ return [
|
|||
'dbname' => '$DBNAME'
|
||||
],
|
||||
'logging' => [
|
||||
'level' => 'warning',
|
||||
'level' => 'error',
|
||||
'path' => '../../test/logfile.log'
|
||||
],
|
||||
'authentication' => [
|
||||
|
|
67
backend/test/tests/domain-soa.js
Normal file
67
backend/test/tests/domain-soa.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const test = require('../testlib');
|
||||
|
||||
test.run(async function () {
|
||||
await test('admin', async function (assert, req) {
|
||||
//Try to set soa for non exitent domain
|
||||
var res = await req({
|
||||
url: '/domains/100/soa',
|
||||
method: 'put',
|
||||
data: {
|
||||
primary: 'ns1.example.com',
|
||||
email: 'hostmaster@example.com',
|
||||
refresh: 3600,
|
||||
retry: 900,
|
||||
expire: 604800,
|
||||
ttl: 86400
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 404, 'Updating SOA for not existing domain should fail');
|
||||
|
||||
//Try to set soa for slave domain
|
||||
var res = await req({
|
||||
url: '/domains/2/soa',
|
||||
method: 'put',
|
||||
data: {
|
||||
primary: 'ns1.example.com',
|
||||
email: 'hostmaster@example.com',
|
||||
refresh: 3600,
|
||||
retry: 900,
|
||||
expire: 604800,
|
||||
ttl: 86400
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 405, 'Updating SOA for slave domain should fail');
|
||||
|
||||
//Try to set soa with missing fields
|
||||
var res = await req({
|
||||
url: '/domains/2/soa',
|
||||
method: 'put',
|
||||
data: {
|
||||
primary: 'ns1.example.com',
|
||||
retry: 900,
|
||||
expire: 604800,
|
||||
ttl: 86400
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 422, 'Updating SOA with missing fields should fail.');
|
||||
|
||||
//Set soa for zone without one
|
||||
var res = await req({
|
||||
url: '/domains/1/soa',
|
||||
method: 'put',
|
||||
data: {
|
||||
primary: 'ns1.example.com',
|
||||
email: 'hostmaster@example.com',
|
||||
refresh: 3600,
|
||||
retry: 900,
|
||||
expire: 604800,
|
||||
ttl: 86400
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 204, 'Updating SOA for Zone without one should succeed.');
|
||||
});
|
||||
});
|
|
@ -2,7 +2,7 @@ const test = require('../testlib');
|
|||
const cartesianProduct = require('cartesian-product');
|
||||
|
||||
test.run(async function () {
|
||||
test('admin', async function (assert, req) {
|
||||
await 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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const test = require('../testlib');
|
||||
|
||||
test.run(async function () {
|
||||
test('admin', async function (assert, req) {
|
||||
await test('admin', async function (assert, req) {
|
||||
//Try to login with invalid username and password
|
||||
var res = await req({
|
||||
url: '/sessions',
|
||||
|
|
Loading…
Add table
Reference in a new issue