diff --git a/README.md b/README.md
index d594f18..58ea540 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ The table below lists the supported API connector providers:
| OVH | https://api.ovh.com | **Yes** |
| GANDI | https://api.gandi.net/docs/domains/ | **Yes** |
| NAMECHEAP | https://www.namecheap.com/support/api/methods/domains/create/ | **Yes** |
+| AUTODNS | https://cloud.autodns.com/ | **Yes** |
If a domain has expired and a connector is linked to the Watchlist, then Domain Watchdog will try to order it via the
connector provider's API.
diff --git a/assets/components/tracking/connector/ConnectorForm.tsx b/assets/components/tracking/connector/ConnectorForm.tsx
index 953ff9a..af697e6 100644
--- a/assets/components/tracking/connector/ConnectorForm.tsx
+++ b/assets/components/tracking/connector/ConnectorForm.tsx
@@ -1,4 +1,4 @@
-import {Button, Checkbox, Form, FormInstance, Input, Popconfirm, Select, Space, Typography} from "antd";
+import {Alert, Button, Checkbox, Form, FormInstance, Input, Popconfirm, Select, Space, Typography} from "antd";
import React, {useState} from "react";
import {Connector, ConnectorProvider} from "../../../utils/api/connectors";
import {t} from "ttag";
@@ -132,6 +132,61 @@ export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate:
>
}
+ {
+ provider === ConnectorProvider.AUTODNS && <>
+
+
+
{t`Attention: AutoDNS do not support 2-Factor Authentication on API Users for automated systems`}}
+ rules={[{required: true, message: t`Required`}]}>
+
+
+
+
+
+ {t`The Contact ID for ownership of registered Domains. `}{t`You got from this page`}}
+ rules={[{required: true, message: t`Required`}]}
+ required={true}>
+
+
+
+ {t`If you not sure, use the default value 4`}}
+
+ required={false}>
+
+
+
+
+ {t`Owner confirms his consent of domain order jobs`}
+
+ >
+
+ }
{
provider === ConnectorProvider.NAMECHEAP && <>
{
return
{t`Retreive an API key and whitelist this instance's IP address on Namecheap's website`}
+ case ConnectorProvider.AUTODNS:
+ return
+ {t`Because of some limitations in API of AutoDNS, we suggest to create an dedicated user for API with limited rights.`}
+
default:
return <>>
@@ -31,6 +35,8 @@ export const tosHyperlink = (provider?: string) => {
return 'https://www.ovhcloud.com/fr/terms-and-conditions/contracts/'
case ConnectorProvider.GANDI:
return 'https://www.gandi.net/en/contracts/terms-of-service'
+ case ConnectorProvider.AUTODNS:
+ return 'https://www.internetx.com/agb/'
default:
return ''
}
diff --git a/src/Config/ConnectorProvider.php b/src/Config/ConnectorProvider.php
index 42ea0b0..6051556 100644
--- a/src/Config/ConnectorProvider.php
+++ b/src/Config/ConnectorProvider.php
@@ -2,6 +2,7 @@
namespace App\Config;
+use App\Service\Connector\AutodnsProvider;
use App\Service\Connector\GandiProvider;
use App\Service\Connector\NamecheapProvider;
use App\Service\Connector\OvhProvider;
@@ -10,6 +11,7 @@ enum ConnectorProvider: string
{
case OVH = 'ovh';
case GANDI = 'gandi';
+ case AUTODNS = 'autodns';
case NAMECHEAP = 'namecheap';
public function getConnectorProvider(): string
@@ -17,6 +19,7 @@ enum ConnectorProvider: string
return match ($this) {
ConnectorProvider::OVH => OvhProvider::class,
ConnectorProvider::GANDI => GandiProvider::class,
+ ConnectorProvider::AUTODNS => AutodnsProvider::class,
ConnectorProvider::NAMECHEAP => NamecheapProvider::class,
};
}
diff --git a/src/Service/Connector/AutodnsProvider.php b/src/Service/Connector/AutodnsProvider.php
new file mode 100644
index 0000000..d97f94f
--- /dev/null
+++ b/src/Service/Connector/AutodnsProvider.php
@@ -0,0 +1,254 @@
+getDeleted()) {
+ throw new \InvalidArgumentException('The domain name still appears in the WHOIS database');
+ }
+
+ $ldhName = $domain->getLdhName();
+ if (!$ldhName) {
+ throw new \InvalidArgumentException('Domain name cannot be null');
+ }
+
+ if ($dryRun) {
+ return;
+ }
+
+ $this->client->request(
+ 'POST',
+ '/v1/domain',
+ (new HttpOptions())
+ ->setAuthBasic($this->authData['username'], $this->authData['password'])
+ ->setHeader('Accept', 'application/json')
+ ->setHeader('X-Domainrobot-Context', $this->authData['context'])
+ ->setBaseUri(self::BASE_URL)
+ ->setJson([
+ 'name' => $ldhName,
+ 'ownerc' => [
+ 'id' => $this->authData['contactid'],
+ ],
+ 'adminc' => [
+ 'id' => $this->authData['contactid'],
+ ],
+ 'techc' => [
+ 'id' => $this->authData['contactid'],
+ ],
+ 'confirmOrder' => $this->authData['ownerConfirm'],
+ 'nameServers' => [
+ [
+ 'name' => 'a.ns14.net',
+ ],
+ [
+ 'name' => 'b.ns14.net',
+ ],
+ [
+ 'name' => 'c.ns14.net',
+ ],
+ [
+ 'name' => 'd.ns14.net',
+ ],
+ ],
+ ])
+ ->toArray()
+ )->toArray();
+ }
+
+ /**
+ * @throws RedirectionExceptionInterface
+ * @throws DecodingExceptionInterface
+ * @throws ClientExceptionInterface
+ * @throws TransportExceptionInterface
+ * @throws ServerExceptionInterface
+ */
+ public function registerZone(Domain $domain, bool $dryRun = false): void
+ {
+ $authData = $this->authData;
+
+ $ldhName = $domain->getLdhName();
+
+ if ($dryRun) {
+ return;
+ }
+
+ $zoneCheck = $this->client->request(
+ 'POST',
+ '/v1/zone/_search?keys=name',
+ (new HttpOptions())
+ ->setAuthBasic($authData['username'], $authData['password'])
+ ->setHeader('Accept', 'application/json')
+ ->setHeader('X-Domainrobot-Context', $authData['context'])
+ ->setBaseUri(self::BASE_URL)
+ ->setJson([
+ 'filters' => [
+ [
+ 'key' => 'name',
+ 'value' => $ldhName,
+ 'operator' => 'EQUAL',
+ ],
+ ],
+ ])
+ ->toArray()
+ )->toArray();
+
+ $responseDataIsEmpty = empty($zoneCheck['data']);
+
+ if ($responseDataIsEmpty) {
+ // The domain not yet exists in DNS Server, we create them
+
+ $this->client->request(
+ 'POST',
+ '/v1/zone',
+ (new HttpOptions())
+ ->setAuthBasic($authData['username'], $authData['password'])
+ ->setHeader('Accept', 'application/json')
+ ->setHeader('X-Domainrobot-Context', $authData['context'])
+ ->setBaseUri(self::BASE_URL)
+ ->setJson([
+ 'origin' => $ldhName,
+ 'main' => [
+ 'address' => $authData['dns_ip'],
+ ],
+ 'soa' => [
+ 'refresh' => 3600,
+ 'retry' => 7200,
+ 'expire' => 604800,
+ 'ttl' => 600,
+ ],
+ 'action' => 'COMPLETE',
+ 'wwwInclude' => true,
+ 'nameServers' => [
+ [
+ 'name' => 'a.ns14.net',
+ ],
+ [
+ 'name' => 'b.ns14.net',
+ ],
+ [
+ 'name' => 'c.ns14.net',
+ ],
+ [
+ 'name' => 'd.ns14.net',
+ ],
+ ],
+ ])
+ ->toArray()
+ )->toArray();
+ }
+ }
+
+ public function verifyAuthData(array $authData): array
+ {
+ $username = $authData['username'];
+ $password = $authData['password'];
+
+ $acceptConditions = $authData['acceptConditions'];
+ $ownerLegalAge = $authData['ownerLegalAge'];
+ $waiveRetractationPeriod = $authData['waiveRetractationPeriod'];
+
+ if (empty($authData['context'])) {
+ $authData['context'] = 4;
+ }
+
+ if (
+ empty($username) || empty($password)
+ ) {
+ throw new BadRequestHttpException('Bad authData schema');
+ }
+
+ if (
+ true !== $acceptConditions
+ || true !== $authData['ownerConfirm']
+ || true !== $ownerLegalAge
+ || true !== $waiveRetractationPeriod
+ ) {
+ throw new HttpException(451, 'The user has not given explicit consent');
+ }
+
+ return [
+ 'username' => $authData['username'],
+ 'password' => $authData['password'],
+ 'acceptConditions' => $authData['acceptConditions'],
+ 'ownerLegalAge' => $authData['ownerLegalAge'],
+ 'ownerConfirm' => $authData['ownerConfirm'],
+ 'waiveRetractationPeriod' => $authData['waiveRetractationPeriod'],
+ 'context' => $authData['context'],
+ ];
+ }
+
+ public function isSupported(Domain ...$domainList): bool
+ {
+ return true;
+ }
+
+ protected function getSupportedTldList(): array
+ {
+ return [];
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ protected function getCachedTldList(): CacheItemInterface
+ {
+ return $this->cacheItemPool->getItem('app.provider.autodns.supported-tld');
+ }
+
+ /**
+ * @throws TransportExceptionInterface
+ */
+ public function assertAuthentication(): void
+ {
+ try {
+ $response = $this->client->request(
+ 'GET',
+ '/v1/hello',
+ (new HttpOptions())
+ ->setAuthBasic($this->authData['username'], $this->authData['password'])
+ ->setHeader('Accept', 'application/json')
+ ->setHeader('X-Domainrobot-Context', $this->authData['context'])
+ ->setBaseUri(self::BASE_URL)
+ ->toArray()
+ );
+ } catch (\Exception) {
+ throw new BadRequestHttpException('Invalid Login');
+ }
+
+ if (Response::HTTP_OK !== $response->getStatusCode()) {
+ throw new BadRequestHttpException('The status of these credentials is not valid');
+ }
+ }
+}
diff --git a/src/Service/Connector/GandiProvider.php b/src/Service/Connector/GandiProvider.php
index 35205aa..348c134 100644
--- a/src/Service/Connector/GandiProvider.php
+++ b/src/Service/Connector/GandiProvider.php
@@ -20,7 +20,7 @@ class GandiProvider extends AbstractProvider
{
private const BASE_URL = 'https://api.gandi.net';
- public function __construct(CacheItemPoolInterface $cacheItemPool, private HttpClientInterface $client)
+ public function __construct(CacheItemPoolInterface $cacheItemPool, private readonly HttpClientInterface $client)
{
parent::__construct($cacheItemPool);
}
diff --git a/translations/translations.pot b/translations/translations.pot
index e3f252a..30df91e 100644
--- a/translations/translations.pot
+++ b/translations/translations.pot
@@ -42,9 +42,13 @@ msgstr ""
#: assets/components/tracking/connector/ConnectorForm.tsx:84
#: assets/components/tracking/connector/ConnectorForm.tsx:92
#: assets/components/tracking/connector/ConnectorForm.tsx:122
-#: assets/components/tracking/connector/ConnectorForm.tsx:158
-#: assets/components/tracking/connector/ConnectorForm.tsx:172
-#: assets/components/tracking/connector/ConnectorForm.tsx:181
+#: assets/components/tracking/connector/ConnectorForm.tsx:146
+#: assets/components/tracking/connector/ConnectorForm.tsx:152
+#: assets/components/tracking/connector/ConnectorForm.tsx:162
+#: assets/components/tracking/connector/ConnectorForm.tsx:182
+#: assets/components/tracking/connector/ConnectorForm.tsx:213
+#: assets/components/tracking/connector/ConnectorForm.tsx:227
+#: assets/components/tracking/connector/ConnectorForm.tsx:236
#: assets/components/tracking/watchlist/WatchlistForm.tsx:115
#: assets/components/tracking/watchlist/WatchlistForm.tsx:212
msgid "Required"
@@ -218,48 +222,96 @@ msgid "It indicates the organization that will pay for the ordered product"
msgstr ""
#: assets/components/tracking/connector/ConnectorForm.tsx:138
+msgid ""
+"This provider does not provide a list of supported TLD. Please double check "
+"if the domain you want to register is supported."
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:142
+msgid "AutoDNS Username"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:145
+msgid ""
+"Attention: AutoDNS do not support 2-Factor Authentication on API Users for "
+"automated systems"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:150
+msgid "AutoDNS Password"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:157
+msgid "Domain Contact Handle ID"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:160
+msgid "The Contact ID for ownership of registered Domains. "
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:161
+msgid "You got from this page"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:168
+msgid "Context Value"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:171
+msgid "If you not sure, use the default value 4"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:179
+msgid "Owner confirmation"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:185
+msgid "Owner confirms his consent of domain order jobs"
+msgstr ""
+
+#: assets/components/tracking/connector/ConnectorForm.tsx:193
#: assets/pages/UserPage.tsx:18
msgid "Username"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:144
+#: assets/components/tracking/connector/ConnectorForm.tsx:199
msgid "API key"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:156
+#: assets/components/tracking/connector/ConnectorForm.tsx:211
msgid "API Terms of Service"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:164
+#: assets/components/tracking/connector/ConnectorForm.tsx:219
msgid ""
"I have read and accepted the conditions of use of the Provider API, "
"accessible from this hyperlink"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:170
+#: assets/components/tracking/connector/ConnectorForm.tsx:225
msgid "Legal age"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:175
+#: assets/components/tracking/connector/ConnectorForm.tsx:230
msgid "I am of the minimum age required to consent to these conditions"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:179
+#: assets/components/tracking/connector/ConnectorForm.tsx:234
msgid "Withdrawal period"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:184
+#: assets/components/tracking/connector/ConnectorForm.tsx:239
msgid ""
"I waive my right of withdrawal regarding the purchase of domain names via "
"the Provider's API"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:192
+#: assets/components/tracking/connector/ConnectorForm.tsx:247
#: assets/components/tracking/watchlist/WatchlistForm.tsx:252
msgid "Create"
msgstr ""
-#: assets/components/tracking/connector/ConnectorForm.tsx:195
+#: assets/components/tracking/connector/ConnectorForm.tsx:250
#: assets/components/tracking/watchlist/WatchlistForm.tsx:255
msgid "Reset"
msgstr ""
@@ -1047,6 +1099,12 @@ msgid ""
"website"
msgstr ""
+#: assets/utils/providers/index.tsx:24
+msgid ""
+"Because of some limitations in API of AutoDNS, we suggest to create an "
+"dedicated user for API with limited rights."
+msgstr ""
+
#: assets/utils/providers/ovh.tsx:5
msgid "Application key"
msgstr ""