Commit a9f1e9c8 authored by Ludwig Ruderstaller's avatar Ludwig Ruderstaller
Browse files

fixes #903938048443124 - Metadata Endpoint

parent 185faa92
Pipeline #17759 passed with stage
in 2 minutes and 3 seconds
<?php
/*
* This file is part of the CwdPowerDNS Client
*
* (c) 2018 cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Cwd\PowerDNSClient\Endpoints;
use Cwd\PowerDNSClient\Client;
use Cwd\PowerDNSClient\Model\Metadata;
class MetadataEndpoint extends AbstractEndpoint
{
const ENDPOINT_LIST = 'servers/%s/zones/%s/metadata';
const ENDPOINT_ELEMENT = 'servers/%s/zones/%s/metadata/%s';
private $zoneId;
public function __construct(Client $client, $defaultServerId, $zoneId)
{
$this->zoneId = $zoneId;
parent::__construct($client, $defaultServerId);
}
/**
* @param string $hydrationClass
*
* @return Metadata[]
*
* @throws \Http\Client\Exception
*/
public function all($hydrationClass = Metadata::class): array
{
$uri = sprintf(self::ENDPOINT_LIST, $this->defaultServerId, $this->zoneId);
return $this->getClient()->call(null, $uri, $hydrationClass, true, 'GET');
}
/**
* @param string $kind
*
* @return Metadata[]
*
* @throws \Http\Client\Exception
*/
public function get(string $kind, $hydrationClass = Metadata::class): ?Metadata
{
return $this->getClient()->call(null, $this->uriHelper($kind), $hydrationClass, false, 'GET');
}
/**
* @param Metadata $metadata
* @param bool $lacyLoad
* @param string $hydrationClass
*
* @return Metadata[]|null
*
* @throws \Http\Client\Exception
*/
public function create(Metadata $metadata, $lacyLoad = true, $hydrationClass = Metadata::class): ?Metadata
{
$this->validateEntity($metadata, ['CREATE']);
$uri = sprintf(self::ENDPOINT_LIST, $this->defaultServerId, $this->zoneId);
$payload = $this->getClient()->getSerializer()->serialize($metadata, 'json');
$this->getClient()->call($payload, $uri, null, false, 'POST');
if ($lacyLoad) {
return $this->get($metadata->getKind(), $hydrationClass);
}
return null;
}
/**
* @param Metadata $metadata
* @param bool $lacyLoad
*
* @return Metadata[]|null
*
* @throws \Http\Client\Exception
*/
public function update(Metadata $metadata, $lacyLoad = true, $hydrationClass = Metadata::class): ?Metadata
{
$this->validateEntity($metadata, ['UPDATE']);
$payload = $this->getClient()->getSerializer()->serialize($metadata, 'json');
$this->getClient()->call($payload, $this->uriHelper($metadata->getKind()), null, false, 'PUT');
if ($lacyLoad) {
return $this->get($metadata->getKind(), $hydrationClass);
}
return null;
}
/**
* @param Metadata|string $metadata
*
* @throws \Http\Client\Exception
*/
public function delete($metadata): void
{
$kind = $metadata;
if ($metadata instanceof Metadata) {
$kind = $metadata->getKind();
}
$this->getClient()->call(null, $this->uriHelper($kind), null, false, 'DELETE');
}
private function uriHelper($kind): string
{
return sprintf(self::ENDPOINT_ELEMENT, $this->defaultServerId, $this->zoneId, $kind);
}
}
......@@ -13,10 +13,52 @@ declare(strict_types=1);
namespace Cwd\PowerDNSClient\Model;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class Metadata
{
/** @var string */
// https://doc.powerdns.com/md/httpapi/api_spec/#zone-metadata
const UPDATE_FORBIDDEN = [
'NSEC3PARAM',
'NSEC3NARROW',
'PRESIGNED',
'LUA-AXFR-SCRIPT',
];
// https://doc.powerdns.com/authoritative/domainmetadata.html
const VALID_KINDs = [
'ALLOW-AXFR-FROM',
'API-RECTIFY',
'AXFR-SOURCE',
'ALLOW-DNSUPDATE-FROM',
'TSIG-ALLOW-DNSUPDATE',
'FORWARD-DNSUPDATE',
'SOA-EDIT-DNSUPDATE',
'NOTIFY-DNSUPDATE',
'ALSO-NOTIFY',
'AXFR-MASTER-TSIG',
'GSS-ALLOW-AXFR-PRINCIPAL',
'GSS-ACCEPTOR-PRINCIPAL',
'IXFR',
'LUA-AXFR-SCRIPT',
'NSEC3NARROW',
'NSEC3PARAM',
'PRESIGNED',
'PUBLISH-CDNSKEY',
'PUBLISH-CDS',
'SOA-EDIT',
'SOA-EDIT-API',
'TSIG-ALLOW-AXFR',
'TSIG-ALLOW-DNSUPDATE',
];
/**
* @var string
* @Assert\NotBlank(groups={"CREATE", "UPDATE"})
*/
private $kind;
/** @var string[] */
private $metadata = [];
......@@ -59,4 +101,38 @@ class Metadata
return $this;
}
/**
* @param ExecutionContextInterface $context
* @param $payload
* @Assert\Callback(groups={"CREATE", "UPDATE"})
*/
public function validateKinds(ExecutionContextInterface $context, $payload)
{
if (in_array($this->getKind(), self::VALID_KINDs)) {
return;
}
if (0 === strpos(strtoupper($this->getKind()), 'X-')) {
return;
}
$context->buildViolation(sprintf('Kind "%s" not in valid kinds or does not start with "X-"', $this->getKind()))
->atPath('kind')
->addViolation();
}
/**
* @param ExecutionContextInterface $context
* @param $payload
* @Assert\Callback(groups={"UPDATE"})
*/
public function validateForbidden(ExecutionContextInterface $context, $payload)
{
if (in_array($this->getKind(), self::UPDATE_FORBIDDEN)) {
$context->buildViolation(sprintf('Kind "%s" cant be updated', $this->getKind()))
->atPath('kind')
->addViolation();
}
}
}
......@@ -13,8 +13,10 @@ declare(strict_types=1);
namespace Cwd\PowerDNSClient;
use Cwd\PowerDNSClient\Endpoints\MetadataEndpoint;
use Cwd\PowerDNSClient\Endpoints\ServersEndpoint;
use Cwd\PowerDNSClient\Endpoints\ZonesEndpoint;
use Cwd\PowerDNSClient\Model\Zone;
class PowerDNSClient
{
......@@ -35,6 +37,17 @@ class PowerDNSClient
$this->client = $client;
}
public function metadata($zone): MetadataEndpoint
{
$zoneId = $zone;
if ($zone instanceof Zone) {
$zoneId = $zone->getId();
}
return new MetadataEndpoint($this->getClient(), $this->getDefaultServerId(), $zoneId);
}
public function servers(): ServersEndpoint
{
if (null === $this->serversEndpoint) {
......
<?php
/*
* This file is part of the CwdPowerDNS Client
*
* (c) 2018 cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Cwd\PowerDNSClient\Tests;
use Cwd\PowerDNSClient\Model\Metadata;
use Cwd\PowerDNSClient\Model\Zone;
use Cwd\PowerDNSClient\Validator\ValidationException;
use Webmozart\Assert\Assert;
class MetadataEndpointTest extends AbstractTest
{
const ZONE = 'metadata.net.';
public function setup()
{
$zone = (new Zone())
->setName(self::ZONE)
->setKind(Zone::KIND_MASTER)
->addRrset(
(new Zone\RRSet())->setName('www.'.self::ZONE)
->setType('A')
->setTtl(3600)
->addRecord(
(new Zone\Record())->setContent('127.0.0.1')
->setDisabled(false)
)
->addComment(
(new Zone\Comment())->setContent('Test Test')
->setAccount('Max Mustermann')
)
)
->addRrset((new Zone\RRSet())->setName('delete.'.self::ZONE)
->setType('A')
->setTtl(3600)
->addRecord(
(new Zone\Record())->setContent('127.0.0.1')
->setDisabled(false)
)
->addComment((new Zone\Comment())->setContent('test')->setAccount('Maxi'))
)
;
$zone = $this->getClient()->zones()->create($zone, true);
$this->assertNotEmpty($zone->getId());
return $zone;
}
public function tearDown()
{
$this->getClient()->zones()->delete(self::ZONE);
}
public function testAll()
{
$metadatas = $this->getClient()->metadata(self::ZONE)->all();
$this->assertGreaterThanOrEqual(2, count($metadatas));
Assert::allIsInstanceOf($metadatas, Metadata::class);
}
public function testMetadataWithObject()
{
$metadatas = $this->getClient()->metadata((new Zone())->setId(self::ZONE))->all();
$this->assertGreaterThanOrEqual(2, count($metadatas));
Assert::allIsInstanceOf($metadatas, Metadata::class);
}
public function testGet()
{
$metadata = $this->getClient()->metadata(self::ZONE)->get('ALLOW-AXFR-FROM');
$this->assertInstanceOf(Metadata::class, $metadata);
$this->assertEquals(0, count($metadata->getMetadata()));
}
public function testGetUnknownTypeThrowsException()
{
$this->expectException(\LogicException::class);
$metadata = $this->getClient()->metadata(self::ZONE)->get('FOOBAR');
}
public function testGetKindWithXPrefixDontThrowException()
{
$metadata = $this->getClient()->metadata(self::ZONE)->get('X-FOOBAR');
$this->assertInstanceOf(Metadata::class, $metadata);
}
public function testCreate()
{
$metadatas[] = (new Metadata())->setKind('ALLOW-AXFR-FROM')->setMetadata(['127.0.0.2']);
$metadatas[] = (new Metadata())->setKind('ALLOW-AXFR-FROM')->setMetadata(['127.0.0.3']);
$metadatas[] = (new Metadata())->setKind('X-MyVeryOwn')->setMetadata(['foobar']);
$result = $this->getClient()->metadata(self::ZONE)->create($metadatas[0], true);
$this->assertInstanceOf(Metadata::class, $result);
$this->assertEquals(1, count($result->getMetadata()));
$result = $this->getClient()->metadata(self::ZONE)->create($metadatas[1], true);
$this->assertInstanceOf(Metadata::class, $result);
$this->assertEquals(2, count($result->getMetadata()));
$result = $this->getClient()->metadata(self::ZONE)->create($metadatas[2], true);
$this->assertInstanceOf(Metadata::class, $result);
$this->assertEquals(1, count($result->getMetadata()));
}
public function testCreateWithUnknownThrowsException()
{
$metadata = (new Metadata())->setKind('FOOBAR')->setMetadata(['127.0.0.2']);
$this->expectException(ValidationException::class);
$this->getClient()->metadata(self::ZONE)->create($metadata, false);
}
public function testCreateWithXPrefixIsValid()
{
$metadata = (new Metadata())->setKind('X-BAR')->setMetadata(['127.0.0.2']);
try {
$this->getClient()->metadata(self::ZONE)->create($metadata, false);
$this->assertTrue(true, 'No Exception thrown');
} catch (ValidationException $e) {
$this->fail('Create with X-BAR failed');
}
}
public function testUpdate()
{
$metadata = (new Metadata())->setKind('ALLOW-AXFR-FROM')->setMetadata(['127.0.0.10']);
try {
$this->getClient()->metadata(self::ZONE)->update($metadata, false);
$this->assertTrue(true);
} catch (\Exception $e) {
$this->fail($e->getMessage());
}
$metadata = (new Metadata())->setKind('ALLOW-AXFR-FROM')->setMetadata(['127.0.0.10', '127.0.0.1']);
$result = $this->getClient()->metadata(self::ZONE)->update($metadata, true);
$this->assertInstanceOf(Metadata::class, $result);
$this->assertEquals(2, count($result->getMetadata()));
}
public function testUpdateForbidden()
{
$metadata = (new Metadata())->setKind('PRESIGNED')->setMetadata([1]);
$this->expectException(ValidationException::class);
$this->getClient()->metadata(self::ZONE)->update($metadata, true);
}
public function testDelete()
{
$metadata = (new Metadata())->setKind('X-TEST')->setMetadata(['127.0.0.10', '127.0.0.1']);
$metadata = $this->getClient()->metadata(self::ZONE)->create($metadata, true);
$this->assertEquals(2, count($metadata->getMetadata()));
$this->getClient()->metadata(self::ZONE)->delete($metadata);
$metadata = $this->getClient()->metadata(self::ZONE)->get('X-TEST');
$this->assertEquals(0, count($metadata->getMetadata()));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment