diff --git a/src/Powerdns.php b/src/Powerdns.php index 94813bb..2724c73 100644 --- a/src/Powerdns.php +++ b/src/Powerdns.php @@ -236,6 +236,16 @@ public function cryptokeys(string $canonicalDomain): Cryptokey return new Cryptokey($this->connector, $canonicalDomain); } + /** + * Get a TSIGKey instance to work with. + * + * @return TSIGKey The TSIGKey instance + */ + public function tsigkeys(): TSIGKey + { + return new TSIGKey($this->connector); + } + /** * Query PowerDNS internal statistics. * The ring statistics are disabled by default to speedup the request and reduce the response size. @@ -292,7 +302,9 @@ public function search(string $query, int $size = 100, string $type = 'all'): Se ) ); - $searchResults = array_map(static function ($item) { return new SearchResult($item); }, $response); + $searchResults = array_map(static function ($item) { + return new SearchResult($item); + }, $response); return new SearchResultSet($searchResults); } diff --git a/src/Resources/TSIGKey.php b/src/Resources/TSIGKey.php new file mode 100644 index 0000000..7d132b5 --- /dev/null +++ b/src/Resources/TSIGKey.php @@ -0,0 +1,165 @@ +setName($content['name'] ?? ''); + $this->setId($content['id'] ?? ''); + $this->setAlgorithm($content['algorithm'] ?? ''); + $this->setKey($content['key'] ?? ''); + $this->setType('TSIGKey'); + } + } + + /** + * Get set to "TSIGKey". + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Get the Base64 encoded secret key, empty when listing keys. MAY be empty when POSTing to have the server generate the key material. + * + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Set the Base64 encoded secret key, empty when listing keys. MAY be empty when POSTing to have the server generate the key material. + * + * @param string $key The Base64 encoded secret key, empty when listing keys. MAY be empty when POSTing to have the server generate the key material + * + * @return self + */ + public function setKey(string $key) + { + $this->key = $key; + + return $this; + } + + /** + * Get the algorithm of the TSIG key. + * + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * Set the algorithm of the TSIG key. + * + * @param string $algorithm The algorithm of the TSIG key + * + * @return self + */ + public function setAlgorithm(string $algorithm) + { + $this->algorithm = $algorithm; + + return $this; + } + + /** + * Get the ID for this key, used in the TSIGkey URL endpoint. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set the ID for this key, used in the TSIGkey URL endpoint. + * + * @param string $id The ID for this key, used in the TSIGkey URL endpoint. + * + * @return self + */ + public function setId(string $id) + { + $this->id = $id; + + return $this; + } + + /** + * Get the name of the key. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set the name of the key. + * + * @param string $name The name of the key + * + * @return self + */ + public function setName(string $name) + { + $this->name = $name; + + return $this; + } +} diff --git a/src/Resources/TSIGKeySet.php b/src/Resources/TSIGKeySet.php new file mode 100644 index 0000000..058a1d6 --- /dev/null +++ b/src/Resources/TSIGKeySet.php @@ -0,0 +1,148 @@ +tsigResources = $resourceRecords; + } + } + + /** + * Add a single tsigkey resource to the existing collection. + * + * @param TSIGKey $metaResource The tsigkey resource to add. + * + * @return TSIGKeySet The current TSIGKeySet instance. + */ + public function addResource(TSIGKey $metaResource): self + { + $this->tsigResources[] = $metaResource; + + return $this; + } + + /** + * Get the number of tsigkey resources in this collection. + * + * @return int The number of tsigkey resources. + */ + public function count(): int + { + return count($this->tsigResources); + } + + /** + * Check if the current collection is not empty. + * + * @return bool True when there are tsigkey resources in this collection. + */ + public function isNotEmpty(): bool + { + return !$this->isEmpty(); + } + + /** + * Check if the current collection is empty. + * + * @return bool True when there are no tsigkey resources in this collection. + */ + public function isEmpty(): bool + { + return empty($this->tsigResources); + } + + /** + * Loop through the collection and call the given closure for each tsigkey resource. + * + * @param Closure $closure The closure to execute for each tsigkey resource. + * + * @return TSIGKeySet The current TSIGKeySet instance. + */ + public function map(Closure $closure): self + { + foreach ($this->tsigResources as $index => $resource) { + $this->tsigResources[$index] = $closure($resource, $index); + } + + return $this; + } + + /** + * Delete all tsigkey resources that are set in the current collection. + * + * @return bool True when the tsigkey resources are deleted. + */ + public function delete(): bool + { + foreach ($this->tsigResources as $resource) { + $resource->delete(); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->tsigResources); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($offset): bool + { + return isset($this->tsigResources[$offset]); + } + + /** + * {@inheritdoc} + * + * @return TSIGKey + */ + #[ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->tsigResources[$offset]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value): void + { + $this->tsigResources[$offset] = $value; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + unset($this->tsigResources[$offset]); + } +} diff --git a/src/TSIGKey.php b/src/TSIGKey.php new file mode 100644 index 0000000..67e6d7d --- /dev/null +++ b/src/TSIGKey.php @@ -0,0 +1,131 @@ +connector = $connector; + } + + /** + * Get all tsigkeys on the server. + * + * @return TSIGKeySet The meta data set. + */ + public function list(): TSIGKeySet + { + $items = $this->connector->get('tsigkeys'); + + $resultSet = new TSIGKeySet(); + foreach ($items as $item) { + $resultSet->addResource(new TSIGKeyResource($item)); + } + + return $resultSet; + } + + /** + * Get a single tsigkey. + * + * @param string $id the id + * + * @return TSIGKeyResource The meta data set. + */ + public function get(string $id): TSIGKeyResource + { + $item = $this->connector->get('tsigkeys/'.$id); + + return new TSIGKeyResource($item); + } + + /** + * Creat a new TSIG Key. + * + * @param array|string $data The data. + * + * @return TSIGKeyResource The created key data. + */ + public function create(TSIGKeyResource $data): TSIGKeyResource + { + $response = $this->connector->post('tsigkeys', new TSIGKeyCreateTransformer($data)); + + return new TSIGKeyResource($response); + } + + /** + * Update an existing TSIGKey and reset the algorithm. + * + * @param TSIGKeyResource $resource The key data item to update. + * + * @return TSIGKeyResource the updated key resource. + */ + public function updateAlgorithm(TSIGKeyResource $resource): TSIGKeyResource + { + $response = $this->connector->put('tsigkeys/'.$resource->getId(), new TSIGKeyUpdateAlgorithmTransformer($resource)); + + return new TSIGKeyResource($response); + } + + /** + * update the key of a tsigkey. + * + * @param TSIGKeyResource $resource + * + * @return TSIGKeyResource + */ + public function updateKey(TSIGKeyResource $resource): TSIGKeyResource + { + $response = $this->connector->put('tsigkeys/'.$resource->getId(), new TSIGKeyUpdateKeyTransformer($resource)); + + return new TSIGKeyResource($response); + } + + /** + * change the name of a tsigkey, this will change remove the old key and add a new one. + * + * @param TSIGKeyResource $resource + * + * @return TSIGKeyResource + */ + public function updateName(TSIGKeyResource $resource): TSIGKeyResource + { + $response = $this->connector->put('tsigkeys/'.$resource->getId(), new TSIGKeyUpdateNameTransformer($resource)); + + return new TSIGKeyResource($response); + } + + /** + * Delete an existing tsigkey item. + * + * @param TSIGKeyResource $key The tsigkey data item to delete. + * + * @return bool True if the delete was successful. + */ + public function delete(TSIGKeyResource $key): bool + { + $response = $this->connector->delete('tsigkeys/'.$key->getId()); + + // If the response is empty, everything is fine. + return empty($response); + } +} diff --git a/src/TSIGKeyAlgorithms.php b/src/TSIGKeyAlgorithms.php new file mode 100644 index 0000000..f8a6998 --- /dev/null +++ b/src/TSIGKeyAlgorithms.php @@ -0,0 +1,18 @@ + $this->data->getName(), + 'id' => $this->data->getId(), + 'algorithm' => $this->data->getAlgorithm(), + 'key' => $this->data->getKey(), + 'type' => $this->data->getType(), + ]; + } +} diff --git a/src/Transformers/TSIGKeyUpdateAlgorithmTransformer.php b/src/Transformers/TSIGKeyUpdateAlgorithmTransformer.php new file mode 100644 index 0000000..2c434d4 --- /dev/null +++ b/src/Transformers/TSIGKeyUpdateAlgorithmTransformer.php @@ -0,0 +1,16 @@ + $this->data->getAlgorithm(), + ]; + } +} diff --git a/src/Transformers/TSIGKeyUpdateKeyTransformer.php b/src/Transformers/TSIGKeyUpdateKeyTransformer.php new file mode 100644 index 0000000..3d3d4c8 --- /dev/null +++ b/src/Transformers/TSIGKeyUpdateKeyTransformer.php @@ -0,0 +1,16 @@ + $this->data->getKey(), + ]; + } +} diff --git a/src/Transformers/TSIGKeyUpdateNameTransformer.php b/src/Transformers/TSIGKeyUpdateNameTransformer.php new file mode 100644 index 0000000..ddae7e9 --- /dev/null +++ b/src/Transformers/TSIGKeyUpdateNameTransformer.php @@ -0,0 +1,16 @@ + $this->data->getName(), + ]; + } +} diff --git a/tests/functional/TSIGKeysTest.php b/tests/functional/TSIGKeysTest.php new file mode 100644 index 0000000..a0e87ea --- /dev/null +++ b/tests/functional/TSIGKeysTest.php @@ -0,0 +1,171 @@ +powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + $this->assertNotEquals('', $key->getKey()); + $this->assertEquals($key->getName(), $name); + + // cleanup + $manager->delete($key); + } + + /** + * testing the the single endpoint works. + */ + public function testGetSingle(): void + { + $name = 'test-key2-'.mt_rand(100, 10000).microtime(false); + + $manager = $this->powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + // get single key + $fromApi = $manager->get($key->getId()); + + $this->assertEquals($key->getId(), $fromApi->getId()); + $this->assertEquals($key->getName(), $fromApi->getName()); + $this->assertEquals($key->getAlgorithm(), $fromApi->getAlgorithm()); + $this->assertEquals($key->getKey(), $fromApi->getKey()); + + // cleanup + $res = $manager->delete($key); + } + + /** + * testing that creating a key with a very weird name works. + */ + public function testCreateWithNonUrlFriendlyName(): void + { + $name = "this/is/not/aa-_412'aur\\asd-url-friendly-".mt_rand(100, 10000); + + $manager = $this->powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + $this->assertNotEquals('', $key->getKey()); + + // cleanup + $manager->delete($key); + } + + /** + * testing that the delete function works. + */ + public function testDelete(): void + { + $name = 'tsigkey-'.mt_rand(100, 10000); + + $manager = $this->powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + // delete + $res = $manager->delete($key); + + $this->assertTrue($res); + } + + /** + * testing that changing the algorithm works. + * + * changing the algo does not regenerate the key. + */ + public function testChangeAlgorithm(): void + { + $name = 'tsigkey-'.time(); + + $manager = $this->powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + // update + $upd = new TSIGKeyResource([ + 'id' => $key->getId(), + 'algorithm' => TSIGKeyAlgorithms::HMAC_SHA256, + ]); + + $updatedKey = $manager->updateAlgorithm($upd); + + $this->assertNotEquals($updatedKey->getAlgorithm(), $key->getAlgorithm()); + $this->assertEquals($updatedKey->getKey(), $key->getKey()); + + // // delete + $res = $manager->delete($key); + } + + /** + * testign that changing the name works. + * + * As per the note in the powerdns documentation, updating the + * name does create a new key with the same values as the old key, removing the odl one after copy. + */ + public function testChangeName(): void + { + $name = 'tsigkey-'.time(); + $nameToUpdate = 'tsigkey2-'.time(); + + $manager = $this->powerdns->tsigkeys(); + $resource = new TSIGKeyResource(); + + $resource->setName($name); + $resource->setAlgorithm(TSIGKeyAlgorithms::HMAC_SHA512); + + $key = $manager->create($resource); + + // update + $upd = new TSIGKeyResource([ + 'id' => $key->getId(), + 'name' => $nameToUpdate, + ]); + + $updatedKey = $manager->updateName($upd); + + $this->assertEquals($updatedKey->getAlgorithm(), $key->getAlgorithm()); + // the key does not change when updating the name + $this->assertEquals($updatedKey->getKey(), $key->getKey()); + $this->assertEquals($updatedKey->getName(), $nameToUpdate); + + // // delete + $res = $manager->delete($updatedKey); + } +}