mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-18 02:05:36 +00:00
Merge branch 'feat/epp-protocol'
This commit is contained in:
commit
f666fd76a9
@ -52,9 +52,9 @@ export function ConnectorsList({connectors, onDelete}: { connectors: ConnectorEl
|
||||
{t`You can stop using a connector at any time. To delete a connector, you must remove it from each linked Watchlist.
|
||||
The creation date corresponds to the date on which you consented to the creation of the connector and on which you declared in particular that you fulfilled the conditions of use of the supplier's API, waived the right of withdrawal and were of the minimum age to consent to these conditions.`}
|
||||
|
||||
<Typography.Link href={providersConfig[connector.provider].tosLink}>
|
||||
{t`The Provider’s conditions are accessible by following this hyperlink.`}
|
||||
</Typography.Link>
|
||||
{providersConfig[connector.provider].tosLink && <Typography.Link href={providersConfig[connector.provider].tosLink}>
|
||||
{t`The Provider’s conditions are accessible by following this hyperlink.`}
|
||||
</Typography.Link>}
|
||||
</>
|
||||
}/>
|
||||
</Card>
|
||||
|
||||
@ -6,12 +6,16 @@ export enum ConnectorProvider {
|
||||
Gandi = 'gandi',
|
||||
AutoDNS = 'autodns',
|
||||
Namecheap = 'namecheap',
|
||||
'Name.com' = 'namecom'
|
||||
'Name.com' = 'namecom',
|
||||
EPP = 'epp'
|
||||
}
|
||||
|
||||
export interface Connector {
|
||||
provider: ConnectorProvider
|
||||
authData: object
|
||||
authData: Record<string, Record<string, string>>,
|
||||
|
||||
objURI?: { key: string, value: string }[],
|
||||
extURI?: { key: string, value: string }[]
|
||||
}
|
||||
|
||||
interface ConnectorResponse {
|
||||
@ -27,6 +31,18 @@ export async function getConnectors(): Promise<ConnectorResponse> {
|
||||
}
|
||||
|
||||
export async function postConnector(connector: Connector) {
|
||||
|
||||
for (const key of ['objURI', 'extURI'] as (keyof Connector)[]) {
|
||||
if (key in connector) {
|
||||
const obj = connector[key] as { key: string, value: string }[]
|
||||
connector.authData[key] = obj.reduce((acc: { [key: string]: string }, x) => ({
|
||||
...acc,
|
||||
[x.key]: x.value
|
||||
}), {})
|
||||
delete connector[key]
|
||||
}
|
||||
}
|
||||
|
||||
const response = await request<Connector & { id: string }>({
|
||||
method: 'POST',
|
||||
url: 'connectors',
|
||||
|
||||
@ -2,7 +2,7 @@ import {Checkbox, Form, Typography} from "antd"
|
||||
import {t} from "ttag"
|
||||
import React from "react"
|
||||
|
||||
export default function DefaultConnectorFormItems({tosLink}: { tosLink: string }) {
|
||||
export default function DefaultConnectorFormItems({tosLink}: { tosLink?: string }) {
|
||||
return <>
|
||||
<Form.Item
|
||||
valuePropName='checked'
|
||||
|
||||
286
assets/utils/providers/forms/EppConnectorForm.tsx
Normal file
286
assets/utils/providers/forms/EppConnectorForm.tsx
Normal file
@ -0,0 +1,286 @@
|
||||
import React from "react"
|
||||
import {t} from "ttag"
|
||||
import {Alert, Card, Col, Form, Input, InputNumber, Row, Select, Space, Switch} from "antd"
|
||||
import {
|
||||
CloseOutlined,
|
||||
DatabaseOutlined,
|
||||
DollarOutlined,
|
||||
FieldTimeOutlined,
|
||||
IdcardOutlined,
|
||||
KeyOutlined,
|
||||
LockOutlined,
|
||||
PlusOutlined,
|
||||
SignatureOutlined,
|
||||
ToolOutlined,
|
||||
UserOutlined,
|
||||
ShopOutlined,
|
||||
ShoppingOutlined
|
||||
} from "@ant-design/icons"
|
||||
|
||||
const DynamicKeyValueList = ({name, label, initialValue, keyPlaceholder, valuePlaceholder}: {
|
||||
name: string[],
|
||||
label: string,
|
||||
initialValue: { [key: string]: string }[],
|
||||
keyPlaceholder: string,
|
||||
valuePlaceholder: string
|
||||
}) => <Form.Item label={label}>
|
||||
<Form.List name={name} initialValue={initialValue}>
|
||||
{(subFields, subOpt) => (
|
||||
<>
|
||||
{subFields.map((subField, index) => (
|
||||
<Row key={subField.key} gutter={[16, 16]}>
|
||||
<Col span={10}>
|
||||
<Form.Item name={[subField.name, 'key']}>
|
||||
<Input placeholder={keyPlaceholder}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Form.Item name={[subField.name, 'value']}>
|
||||
<Input placeholder={valuePlaceholder}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Space>
|
||||
<CloseOutlined
|
||||
onClick={() => subOpt.remove(subField.name)}
|
||||
/>
|
||||
{index === subFields.length - 1 && <PlusOutlined onClick={() => subOpt.add()}/>}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
|
||||
export default function EppConnectorForm() {
|
||||
|
||||
return <>
|
||||
<Alert
|
||||
message={t`This connector is experimental and its implementation has not been fully tested. Please submit an issue for any issues related to it.`}
|
||||
type='warning'
|
||||
style={{marginBottom: '1em'}}
|
||||
/>
|
||||
<Alert
|
||||
message={t`The EPP connector is a special type of connector. Be careful.`}
|
||||
type='info'
|
||||
style={{marginBottom: '2em'}}
|
||||
/>
|
||||
|
||||
<Card size="small" title={t`Server configuration`} bordered={false}>
|
||||
<Form.Item label={t`Protocol`}>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
help={t`Version`}
|
||||
name={['authData', 'version']}
|
||||
initialValue='1.0'
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
>
|
||||
<Input autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
help={t`Language`}
|
||||
name={['authData', 'language']}
|
||||
initialValue='en'
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
>
|
||||
<Input autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t`Server`}
|
||||
name={['authData', 'hostname']}
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}>
|
||||
<Input prefix={<DatabaseOutlined/>} addonAfter={
|
||||
<Form.Item
|
||||
name={['authData', 'port']}
|
||||
hasFeedback
|
||||
noStyle
|
||||
initialValue={700}
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
>
|
||||
<InputNumber autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
} placeholder='ssl://epp.nic.tld' autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
|
||||
<Card size="small" title={t`Authentication`} bordered={false}>
|
||||
<Form.Item label={t`Credentials`}>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['authData', 'auth', 'username']}
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
>
|
||||
<Input prefix={<UserOutlined/>} placeholder={t`Username`} autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['authData', 'auth', 'password']}
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
>
|
||||
<Input prefix={<LockOutlined/>} placeholder={t`Password`} autoComplete='off' required/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t`TLS client certificate`}>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
name={['authData', 'certificate_pem']}>
|
||||
<Input.TextArea rows={5} placeholder={`-----BEGIN CERTIFICATE-----
|
||||
...
|
||||
-----END CERTIFICATE-----`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name={['authData', 'certificate_key']}>
|
||||
<Input.TextArea rows={5} placeholder={`-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
...
|
||||
-----END ENCRYPTED PRIVATE KEY-----`}/>
|
||||
</Form.Item>
|
||||
<Form.Item name={['authData', 'auth', 'ssl', 'passphrase']}>
|
||||
<Input prefix={<KeyOutlined/>} placeholder={t`Private key passphrase (optional)`}
|
||||
autoComplete='off'/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t`TLS configuration`}>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
initialValue={true}
|
||||
help={t`Verify peer`}
|
||||
name={['authData', 'auth', 'ssl', 'verify_peer']}
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
initialValue={true}
|
||||
help={t`Verify peer name`}
|
||||
name={['authData', 'auth', 'ssl', 'verify_peer_name']}
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
initialValue={false}
|
||||
help={t`Allow self-signed certificates`}
|
||||
name={['authData', 'auth', 'ssl', 'allow_self_signed']}
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
<Card size="small" title={t`Domain configuration`} bordered={false}>
|
||||
<Form.Item
|
||||
label={t`Registration period`}
|
||||
initialValue={1}
|
||||
hasFeedback
|
||||
rules={[{
|
||||
required: true,
|
||||
message: t`Required`,
|
||||
validator: (_, v: number) => v > 0 && v < 100 ? Promise.resolve() : Promise.reject()
|
||||
}]}
|
||||
name={['authData', 'domain', 'period']}
|
||||
>
|
||||
<InputNumber prefix={<FieldTimeOutlined/>} addonAfter={
|
||||
<Form.Item name={['authData', 'domain', 'unit']} noStyle initialValue={'y'}>
|
||||
<Select style={{width: 100}}>
|
||||
<Select.Option value="y">{t`Year`}</Select.Option>
|
||||
<Select.Option value="m">{t`Month`}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
} required/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t`Auth-Info Code`}
|
||||
hasFeedback
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
name={['authData', 'domain', 'password']}
|
||||
>
|
||||
<Input prefix={<LockOutlined/>} required/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t`NIC Handle`}>
|
||||
<Row gutter={16}>
|
||||
<Col span={4}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
required
|
||||
rules={[{required: true, message: t`Required`}]}
|
||||
name={['authData', 'domain', 'registrant']}>
|
||||
<Input prefix={<SignatureOutlined/>} placeholder={t`Registrant`} required/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name={['authData', 'domain', 'contacts', 'admin']}>
|
||||
<Input prefix={<IdcardOutlined/>} placeholder={t`Administrative`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name={['authData', 'domain', 'contacts', 'tech']}>
|
||||
<Input prefix={<ToolOutlined/>} placeholder={t`Technical`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name={['authData', 'domain', 'contacts', 'billing']}>
|
||||
<Input prefix={<DollarOutlined/>} placeholder={t`Billing`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name={['authData', 'domain', 'contacts', 'onsite']}>
|
||||
<Input prefix={<ShopOutlined />} placeholder={t`Onsite`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name={['authData', 'domain', 'contacts', 'reseller']}>
|
||||
<Input prefix={<ShoppingOutlined />} placeholder={t`Reseller`}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
<Card size="small" title={t`Protocol configuration`} bordered={false}>
|
||||
<DynamicKeyValueList name={['objURI']} label={t`Services`} initialValue={[
|
||||
{key: 'urn:ietf:params:xml:ns:host-1.0', value: 'host'},
|
||||
{key: 'urn:ietf:params:xml:ns:contact-1.0', value: 'contact'},
|
||||
{key: 'urn:ietf:params:xml:ns:domain-1.0', value: 'domain'}
|
||||
]} keyPlaceholder='urn:ietf:params:xml:ns:host-1.0' valuePlaceholder='host'/>
|
||||
|
||||
<DynamicKeyValueList name={['extURI']} label={t`Extensions`} initialValue={[
|
||||
{key: 'urn:ietf:params:xml:ns:rgp-1.0', value: 'rgp'}
|
||||
]} keyPlaceholder='urn:ietf:params:xml:ns:rgp-1.0' valuePlaceholder='rgp'/>
|
||||
|
||||
</Card>
|
||||
</>
|
||||
}
|
||||
@ -6,6 +6,7 @@ import GandiConnectorForm from "./forms/GandiConnectorForm"
|
||||
import NamecheapConnectorForm from "./forms/NamecheapConnectorForm"
|
||||
import AutoDnsConnectorForm from "./forms/AutoDnsConnectorForm"
|
||||
import NamecomConnectorForm from "./forms/NamecomConnectorForm"
|
||||
import EppConnectorForm from "./forms/EppConnectorForm"
|
||||
|
||||
export const formItemLayoutWithOutLabel = {
|
||||
wrapperCol: {
|
||||
@ -15,7 +16,7 @@ export const formItemLayoutWithOutLabel = {
|
||||
}
|
||||
|
||||
export type ProviderConfig = {
|
||||
tosLink: string
|
||||
tosLink?: string
|
||||
form: ({form}: { form: FormInstance }) => React.ReactElement
|
||||
}
|
||||
|
||||
@ -39,5 +40,8 @@ export const providersConfig: Record<ConnectorProvider, ProviderConfig> = {
|
||||
[ConnectorProvider["Name.com"]]: {
|
||||
tosLink: 'https://www.name.com/policies/',
|
||||
form: NamecomConnectorForm
|
||||
},
|
||||
[ConnectorProvider.EPP]: {
|
||||
form: EppConnectorForm
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
"influxdata/influxdb-client-php": "^3.6",
|
||||
"knpuniversity/oauth2-client-bundle": "^2.18",
|
||||
"lexik/jwt-authentication-bundle": "^3.1",
|
||||
"metaregistrar/php-epp-client": "^1.0",
|
||||
"nelmio/cors-bundle": "^2.5",
|
||||
"ovh/ovh": "^3.3",
|
||||
"phpdocumentor/reflection-docblock": "^5.4",
|
||||
|
||||
967
composer.lock
generated
967
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
||||
namespace App\Config;
|
||||
|
||||
use App\Service\Connector\AutodnsProvider;
|
||||
use App\Service\Connector\EppClientProvider;
|
||||
use App\Service\Connector\GandiProvider;
|
||||
use App\Service\Connector\NamecheapProvider;
|
||||
use App\Service\Connector\NameComProvider;
|
||||
@ -15,6 +16,7 @@ enum ConnectorProvider: string
|
||||
case AUTODNS = 'autodns';
|
||||
case NAMECHEAP = 'namecheap';
|
||||
case NAMECOM = 'namecom';
|
||||
case EPP = 'epp';
|
||||
|
||||
public function getConnectorProvider(): string
|
||||
{
|
||||
@ -24,6 +26,7 @@ enum ConnectorProvider: string
|
||||
ConnectorProvider::AUTODNS => AutodnsProvider::class,
|
||||
ConnectorProvider::NAMECHEAP => NamecheapProvider::class,
|
||||
ConnectorProvider::NAMECOM => NameComProvider::class,
|
||||
ConnectorProvider::EPP => EppClientProvider::class,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,16 +2,20 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Config\ConnectorProvider;
|
||||
use App\Entity\Connector;
|
||||
use App\Entity\User;
|
||||
use App\Service\Connector\AbstractProvider;
|
||||
use App\Service\Connector\EppClientProvider;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
|
||||
@ -22,6 +26,7 @@ class ConnectorController extends AbstractController
|
||||
private readonly LoggerInterface $logger,
|
||||
#[Autowire(service: 'service_container')]
|
||||
private readonly ContainerInterface $locator,
|
||||
private readonly KernelInterface $kernel,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -43,8 +48,8 @@ class ConnectorController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @throws ExceptionInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
#[Route(
|
||||
path: '/api/connectors',
|
||||
@ -71,24 +76,79 @@ class ConnectorController extends AbstractController
|
||||
if (null === $provider) {
|
||||
throw new BadRequestHttpException('Provider not found');
|
||||
}
|
||||
$authData = $connector->getAuthData();
|
||||
|
||||
/** @var AbstractProvider $providerClient */
|
||||
$providerClient = $this->locator->get($provider->getConnectorProvider());
|
||||
$connector->setAuthData($providerClient->authenticate($connector->getAuthData()));
|
||||
if (ConnectorProvider::EPP === $provider) {
|
||||
$filesystem = new Filesystem();
|
||||
$directory = EppClientProvider::buildEppCertificateFolder($this->kernel->getProjectDir(), $connector->getId());
|
||||
unset($authData['file_certificate_pem'], $authData['file_certificate_key']); // Prevent alteration from user
|
||||
|
||||
if (isset($authData['certificate_pem'], $authData['certificate_key'])) {
|
||||
$pemPath = $directory.'client.pem';
|
||||
$keyPath = $directory.'client.key';
|
||||
|
||||
$filesystem->mkdir($directory, 0755);
|
||||
$filesystem->dumpFile($pemPath, $authData['certificate_pem']);
|
||||
$filesystem->dumpFile($keyPath, $authData['certificate_key']);
|
||||
$connector->setAuthData([...$authData, 'file_certificate_pem' => $pemPath, 'file_certificate_key' => $keyPath]);
|
||||
}
|
||||
|
||||
/** @var AbstractProvider $providerClient */
|
||||
$providerClient = $this->locator->get($provider->getConnectorProvider());
|
||||
|
||||
try {
|
||||
$connector->setAuthData($providerClient->authenticate($authData));
|
||||
} catch (\Throwable $exception) {
|
||||
$filesystem->remove($directory);
|
||||
throw $exception;
|
||||
}
|
||||
} else {
|
||||
/** @var AbstractProvider $providerClient */
|
||||
$providerClient = $this->locator->get($provider->getConnectorProvider());
|
||||
$connector->setAuthData($providerClient->authenticate($authData));
|
||||
}
|
||||
|
||||
$this->logger->info('User {username} authentication data with the {provider} provider has been validated.', [
|
||||
'username' => $user->getUserIdentifier(),
|
||||
'provider' => $provider->value,
|
||||
]);
|
||||
|
||||
$this->logger->info('The new API connector requested by {username} has been successfully registered.', [
|
||||
'username' => $user->getUserIdentifier(),
|
||||
]);
|
||||
|
||||
$connector->setCreatedAt(new \DateTimeImmutable('now'));
|
||||
$this->em->persist($connector);
|
||||
$this->em->flush();
|
||||
|
||||
return $connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[Route(
|
||||
path: '/api/connectors/{id}',
|
||||
name: 'connector_delete',
|
||||
defaults: [
|
||||
'_api_resource_class' => Connector::class,
|
||||
'_api_operation_name' => 'delete',
|
||||
],
|
||||
methods: ['DELETE']
|
||||
)]
|
||||
public function deleteConnector(Connector $connector): void
|
||||
{
|
||||
foreach ($connector->getWatchLists()->getIterator() as $watchlist) {
|
||||
$watchlist->setConnector(null);
|
||||
}
|
||||
|
||||
$provider = $connector->getProvider();
|
||||
|
||||
if (null === $provider) {
|
||||
throw new BadRequestHttpException('Provider not found');
|
||||
}
|
||||
|
||||
if (ConnectorProvider::EPP === $provider) {
|
||||
(new Filesystem())->remove(EppClientProvider::buildEppCertificateFolder($this->kernel->getProjectDir(), $connector->getId()));
|
||||
}
|
||||
|
||||
$this->em->remove($connector);
|
||||
$this->em->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,12 +6,15 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class DefaultProviderDto
|
||||
{
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\IsTrue]
|
||||
public bool $ownerLegalAge;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\IsTrue]
|
||||
public bool $acceptConditions;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\IsTrue]
|
||||
public bool $waiveRetractationPeriod;
|
||||
}
|
||||
|
||||
17
src/Dto/Connector/EppClientProviderAuthDto.php
Normal file
17
src/Dto/Connector/EppClientProviderAuthDto.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Connector;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final class EppClientProviderAuthDto
|
||||
{
|
||||
#[Assert\NotBlank]
|
||||
public string $username;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public string $password;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public EppClientProviderAuthSSLDto $ssl;
|
||||
}
|
||||
20
src/Dto/Connector/EppClientProviderAuthSSLDto.php
Normal file
20
src/Dto/Connector/EppClientProviderAuthSSLDto.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Connector;
|
||||
|
||||
final class EppClientProviderAuthSSLDto
|
||||
{
|
||||
public ?string $peer_name = null;
|
||||
|
||||
public bool $verify_peer = true;
|
||||
|
||||
public bool $verify_peer_name = true;
|
||||
|
||||
public bool $allow_self_signed = false;
|
||||
|
||||
public ?int $verify_depth = null;
|
||||
|
||||
public ?string $passphrase = null;
|
||||
|
||||
public bool $disable_compression = false;
|
||||
}
|
||||
27
src/Dto/Connector/EppClientProviderDomainDto.php
Normal file
27
src/Dto/Connector/EppClientProviderDomainDto.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Connector;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final class EppClientProviderDomainDto
|
||||
{
|
||||
#[Assert\NotBlank]
|
||||
public int $period;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public string $unit;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public string $registrant;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public string $password;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\All([
|
||||
new Assert\NotBlank(),
|
||||
new Assert\Type('string'),
|
||||
])]
|
||||
public array $contacts;
|
||||
}
|
||||
44
src/Dto/Connector/EppClientProviderDto.php
Normal file
44
src/Dto/Connector/EppClientProviderDto.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Connector;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final class EppClientProviderDto extends DefaultProviderDto
|
||||
{
|
||||
#[Assert\NotBlank]
|
||||
public string $version;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Language]
|
||||
public string $language;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Url(protocols: ['ssl', 'tls', 'http', 'https'], requireTld: true)]
|
||||
public string $hostname;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public int $port;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public EppClientProviderAuthDto $auth;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public EppClientProviderDomainDto $domain;
|
||||
|
||||
#[Assert\All([
|
||||
new Assert\NotBlank(),
|
||||
new Assert\Type('string'),
|
||||
])]
|
||||
public array $extURI = [];
|
||||
|
||||
#[Assert\All([
|
||||
new Assert\NotBlank(),
|
||||
new Assert\Type('string'),
|
||||
])]
|
||||
public array $objURI = [];
|
||||
|
||||
public ?string $file_certificate_pem = null;
|
||||
|
||||
public ?string $file_certificate_key = null;
|
||||
}
|
||||
@ -18,6 +18,7 @@ final class OvhProviderDto extends DefaultProviderDto
|
||||
#[Assert\NotBlank]
|
||||
public string $consumerKey;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Choice(['create-default', 'create-premium'])]
|
||||
public string $pricingMode;
|
||||
|
||||
|
||||
@ -29,11 +29,14 @@ use Symfony\Component\Uid\Uuid;
|
||||
),
|
||||
new Post(
|
||||
routeName: 'connector_create',
|
||||
normalizationContext: ['groups' => ['connector:create', 'connector:list']], denormalizationContext: ['groups' => 'connector:create'],
|
||||
normalizationContext: ['groups' => ['connector:create', 'connector:list']],
|
||||
denormalizationContext: ['groups' => 'connector:create'],
|
||||
name: 'create'
|
||||
),
|
||||
new Delete(
|
||||
security: 'object.user == user'
|
||||
routeName: 'connector_delete',
|
||||
security: 'object.user == user',
|
||||
name: 'delete'
|
||||
),
|
||||
]
|
||||
)]
|
||||
|
||||
@ -7,7 +7,6 @@ final class OrderDomain
|
||||
public function __construct(
|
||||
public string $watchListToken,
|
||||
public string $ldhName,
|
||||
public \DateTimeImmutable $updatedAt,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,8 +94,6 @@ final readonly class OrderDomainHandler
|
||||
* If no errors occur, the purchase is attempted.
|
||||
*/
|
||||
|
||||
$connectorProvider->authenticate($connector->getAuthData());
|
||||
|
||||
$connectorProvider->orderDomain($domain, $this->kernel->isDebug());
|
||||
|
||||
/*
|
||||
|
||||
@ -11,20 +11,18 @@ use App\Notifier\DomainDeletedNotification;
|
||||
use App\Notifier\DomainUpdateErrorNotification;
|
||||
use App\Repository\WatchListRepository;
|
||||
use App\Service\ChatNotificationService;
|
||||
use App\Service\Connector\AbstractProvider;
|
||||
use App\Service\Connector\CheckDomainProviderInterface;
|
||||
use App\Service\RDAPService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
use Symfony\Component\Messenger\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Notifier\Recipient\Recipient;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
|
||||
#[AsMessageHandler]
|
||||
final readonly class UpdateDomainsFromWatchlistHandler
|
||||
@ -40,18 +38,13 @@ final readonly class UpdateDomainsFromWatchlistHandler
|
||||
private MessageBusInterface $bus,
|
||||
private WatchListRepository $watchListRepository,
|
||||
private LoggerInterface $logger,
|
||||
#[Autowire(service: 'service_container')]
|
||||
private ContainerInterface $locator,
|
||||
) {
|
||||
$this->sender = new Address($mailerSenderEmail, $mailerSenderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function __invoke(UpdateDomainsFromWatchlist $message): void
|
||||
@ -63,6 +56,34 @@ final readonly class UpdateDomainsFromWatchlistHandler
|
||||
'token' => $message->watchListToken,
|
||||
]);
|
||||
|
||||
/** @var AbstractProvider $connectorProvider */
|
||||
$connectorProvider = $this->getConnectorProvider($watchList);
|
||||
|
||||
if ($connectorProvider instanceof CheckDomainProviderInterface) {
|
||||
$this->logger->notice('Watchlist {watchlist} linked to connector {connector}.', [
|
||||
'watchlist' => $watchList->getToken(),
|
||||
'connector' => $watchList->getConnector()->getId(),
|
||||
]);
|
||||
|
||||
try {
|
||||
$checkedDomains = $connectorProvider->checkDomains(
|
||||
...array_unique(array_map(fn (Domain $d) => $d->getLdhName(), $watchList->getDomains()->toArray()))
|
||||
);
|
||||
} catch (\Throwable $exception) {
|
||||
$this->logger->warning('Unable to check domain names availability with connector {connector}.', [
|
||||
'connector' => $watchList->getConnector()->getId(),
|
||||
]);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
foreach ($checkedDomains as $domain) {
|
||||
$this->bus->dispatch(new OrderDomain($watchList->getToken(), $domain));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A domain name is updated if one or more of these conditions are met:
|
||||
* - was updated more than 7 days ago
|
||||
@ -84,17 +105,17 @@ final readonly class UpdateDomainsFromWatchlistHandler
|
||||
$this->bus->dispatch(new SendDomainEventNotif($watchList->getToken(), $domain->getLdhName(), $updatedAt));
|
||||
} catch (NotFoundHttpException) {
|
||||
if (!$domain->getDeleted()) {
|
||||
$notification = (new DomainDeletedNotification($this->sender, $domain));
|
||||
$notification = new DomainDeletedNotification($this->sender, $domain);
|
||||
$this->mailer->send($notification->asEmailMessage(new Recipient($watchList->getUser()->getEmail()))->getMessage());
|
||||
$this->chatNotificationService->sendChatNotification($watchList, $notification);
|
||||
}
|
||||
|
||||
if (null !== $watchList->getConnector()) {
|
||||
if ($watchList->getConnector()) {
|
||||
/*
|
||||
* If the domain name no longer appears in the WHOIS AND a connector is associated with this Watchlist,
|
||||
* this connector is used to purchase the domain name.
|
||||
*/
|
||||
$this->bus->dispatch(new OrderDomain($watchList->getToken(), $domain->getLdhName(), $updatedAt));
|
||||
$this->bus->dispatch(new OrderDomain($watchList->getToken(), $domain->getLdhName()));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
/*
|
||||
@ -113,4 +134,16 @@ final readonly class UpdateDomainsFromWatchlistHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getConnectorProvider(WatchList $watchList): ?object
|
||||
{
|
||||
$connector = $watchList->getConnector();
|
||||
if (null === $connector || null === $connector->getProvider()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$providerClass = $connector->getProvider()->getConnectorProvider();
|
||||
|
||||
return $this->locator->get($providerClass);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ abstract class AbstractProvider
|
||||
|
||||
public function __construct(
|
||||
protected CacheItemPoolInterface $cacheItemPool,
|
||||
private readonly DenormalizerInterface&NormalizerInterface $serializer,
|
||||
protected readonly DenormalizerInterface&NormalizerInterface $serializer,
|
||||
private readonly ValidatorInterface $validator,
|
||||
) {
|
||||
}
|
||||
|
||||
8
src/Service/Connector/CheckDomainProviderInterface.php
Normal file
8
src/Service/Connector/CheckDomainProviderInterface.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Connector;
|
||||
|
||||
interface CheckDomainProviderInterface
|
||||
{
|
||||
public function checkDomains(string ...$domains): array;
|
||||
}
|
||||
202
src/Service/Connector/EppClientProvider.php
Normal file
202
src/Service/Connector/EppClientProvider.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Connector;
|
||||
|
||||
use App\Dto\Connector\DefaultProviderDto;
|
||||
use App\Dto\Connector\EppClientProviderDto;
|
||||
use App\Entity\Domain;
|
||||
use Metaregistrar\EPP\eppCheckContactRequest;
|
||||
use Metaregistrar\EPP\eppCheckContactResponse;
|
||||
use Metaregistrar\EPP\eppCheckDomainRequest;
|
||||
use Metaregistrar\EPP\eppCheckDomainResponse;
|
||||
use Metaregistrar\EPP\eppConnection;
|
||||
use Metaregistrar\EPP\eppContactHandle;
|
||||
use Metaregistrar\EPP\eppCreateDomainRequest;
|
||||
use Metaregistrar\EPP\eppDomain;
|
||||
use Metaregistrar\EPP\eppException;
|
||||
use Metaregistrar\EPP\eppHelloRequest;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class EppClientProvider extends AbstractProvider implements CheckDomainProviderInterface
|
||||
{
|
||||
protected string $dtoClass = EppClientProviderDto::class;
|
||||
|
||||
/** @var EppClientProviderDto */
|
||||
protected DefaultProviderDto $authData;
|
||||
|
||||
private ?eppConnection $eppClient = null;
|
||||
|
||||
public function __construct(
|
||||
CacheItemPoolInterface $cacheItemPool,
|
||||
DenormalizerInterface&NormalizerInterface $serializer,
|
||||
ValidatorInterface $validator,
|
||||
) {
|
||||
parent::__construct($cacheItemPool, $serializer, $validator);
|
||||
}
|
||||
|
||||
protected function assertAuthentication(): void
|
||||
{
|
||||
$this->connect();
|
||||
$this->eppClient->login();
|
||||
|
||||
$this->eppClient->request(new eppHelloRequest());
|
||||
|
||||
$contacts = [new eppContactHandle($this->authData->domain->registrant, eppContactHandle::CONTACT_TYPE_REGISTRANT)];
|
||||
foreach ($this->authData->domain->contacts as $role => $roid) {
|
||||
$contacts[] = new eppContactHandle($roid, $role);
|
||||
}
|
||||
|
||||
/** @var eppCheckContactResponse $resp */
|
||||
$resp = $this->eppClient->request(new eppCheckContactRequest($contacts));
|
||||
foreach ($resp->getCheckedContacts() as $contact => $available) {
|
||||
if ($available) {
|
||||
throw new BadRequestHttpException("At least one of the entered contacts cannot be used because it is indicated as available ($contact).");
|
||||
}
|
||||
}
|
||||
|
||||
$this->eppClient->logout();
|
||||
$this->eppClient->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws eppException
|
||||
*/
|
||||
public function orderDomain(Domain $domain, bool $dryRun): void
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$d = new eppDomain($domain->getLdhName());
|
||||
$d->setRegistrant($this->authData->domain->registrant);
|
||||
$d->setPeriodUnit($this->authData->domain->unit);
|
||||
$d->setPeriod($this->authData->domain->period);
|
||||
$d->setAuthorisationCode($this->authData->domain->password);
|
||||
|
||||
foreach ($this->authData->domain->contacts as $type => $contact) {
|
||||
$d->addContact(new eppContactHandle($contact, $type));
|
||||
}
|
||||
|
||||
if (!$dryRun) {
|
||||
$this->eppClient->request(new eppCreateDomainRequest($d));
|
||||
}
|
||||
|
||||
$this->eppClient->logout();
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function getCachedTldList(): CacheItemInterface
|
||||
{
|
||||
return $this->cacheItemPool->getItem('app.provider.epp.supported-tld');
|
||||
}
|
||||
|
||||
protected function getSupportedTldList(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function isSupported(Domain ...$domainList): bool
|
||||
{
|
||||
if (0 === count($domainList)) {
|
||||
return true;
|
||||
}
|
||||
$tld = $domainList[0]->getTld();
|
||||
|
||||
foreach ($domainList as $domain) {
|
||||
if ($domain->getTld() !== $tld) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*
|
||||
* @throws eppException
|
||||
*/
|
||||
public function checkDomains(string ...$domains): array
|
||||
{
|
||||
$this->connect();
|
||||
$this->eppClient->login();
|
||||
|
||||
$check = new eppCheckDomainRequest($domains);
|
||||
|
||||
/** @var eppCheckDomainResponse $response */
|
||||
$response = $this->eppClient->request($check);
|
||||
$checkedDomains = $response->getCheckedDomains();
|
||||
|
||||
$return = array_map(
|
||||
fn (array $d) => $d['domainname'],
|
||||
array_filter($checkedDomains, fn (array $d) => true === $d['available'])
|
||||
);
|
||||
|
||||
$this->eppClient->logout();
|
||||
$this->eppClient->disconnect();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws eppException
|
||||
* @throws ExceptionInterface
|
||||
*/
|
||||
private function connect(): void
|
||||
{
|
||||
if ($this->eppClient && $this->eppClient->isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conn = new eppConnection(false, null);
|
||||
$conn->setHostname($this->authData->hostname);
|
||||
$conn->setVersion($this->authData->version);
|
||||
$conn->setLanguage($this->authData->language);
|
||||
$conn->setPort($this->authData->port);
|
||||
|
||||
$conn->setUsername($this->authData->auth->username);
|
||||
$conn->setPassword($this->authData->auth->password);
|
||||
|
||||
$ssl = (array) $this->serializer->normalize($this->authData->auth->ssl, 'json');
|
||||
|
||||
if (isset($this->authData->file_certificate_pem, $this->authData->file_certificate_key)) {
|
||||
$conn->setSslContext(stream_context_create(['ssl' => [
|
||||
...$ssl,
|
||||
'local_cert' => $this->authData->file_certificate_pem,
|
||||
'local_pk' => $this->authData->file_certificate_key,
|
||||
]]));
|
||||
} else {
|
||||
unset($ssl['local_cert'], $ssl['local_pk']);
|
||||
$conn->setSslContext(stream_context_create(['ssl' => $ssl]));
|
||||
}
|
||||
|
||||
$conn->setExtensions($this->authData->extURI);
|
||||
$conn->setServices($this->authData->objURI);
|
||||
|
||||
$conn->connect();
|
||||
$this->eppClient = $conn;
|
||||
}
|
||||
|
||||
private function disconnect(): void
|
||||
{
|
||||
$this->eppClient->disconnect();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
public static function buildEppCertificateFolder(string $projectDir, string $connectorId): string
|
||||
{
|
||||
return sprintf('%s/%s/%s/', $projectDir, 'var/epp-certificates', $connectorId);
|
||||
}
|
||||
}
|
||||
@ -46,25 +46,35 @@ msgstr ""
|
||||
#: assets/components/search/DomainSearchBar.tsx:28
|
||||
#: assets/components/tracking/watchlist/WatchlistForm.tsx:119
|
||||
#: assets/components/tracking/watchlist/WatchlistForm.tsx:222
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:20
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:31
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:46
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:69
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:21
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:32
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:47
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:70
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:11
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:26
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:37
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:14
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:36
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:50
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:58
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:66
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:73
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:81
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:24
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:34
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:80
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:92
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:104
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:111
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:127
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:136
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:209
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:227
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:239
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:15
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:37
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:51
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:59
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:67
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:74
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:82
|
||||
msgid "Required"
|
||||
msgstr ""
|
||||
|
||||
#: assets/components/LoginForm.tsx:56
|
||||
#: assets/components/RegisterForm.tsx:45
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:138
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
@ -226,6 +236,7 @@ msgid "Registrar"
|
||||
msgstr ""
|
||||
|
||||
#: assets/components/tracking/connector/ConnectorForm.tsx:40
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:120
|
||||
msgid "Authentication"
|
||||
msgstr ""
|
||||
|
||||
@ -640,8 +651,9 @@ msgid "Create a Watchlist"
|
||||
msgstr ""
|
||||
|
||||
#: assets/pages/UserPage.tsx:19
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:9
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:15
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:129
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:10
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:16
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
@ -650,14 +662,17 @@ msgid "Roles"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/functions/rdapTranslation.ts:7
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:241
|
||||
msgid "Registrant"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/functions/rdapTranslation.ts:8
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:251
|
||||
msgid "Technical"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/functions/rdapTranslation.ts:9
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:246
|
||||
msgid "Administrative"
|
||||
msgstr ""
|
||||
|
||||
@ -666,10 +681,12 @@ msgid "Abuse"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/functions/rdapTranslation.ts:11
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:256
|
||||
msgid "Billing"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/functions/rdapTranslation.ts:13
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:266
|
||||
msgid "Reseller"
|
||||
msgstr ""
|
||||
|
||||
@ -1099,58 +1116,58 @@ msgstr ""
|
||||
msgid "An error occurred"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:9
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:10
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:10
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:11
|
||||
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/utils/providers/forms/AutoDnsConnectorForm.tsx:15
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:16
|
||||
msgid "AutoDNS Username"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:18
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:19
|
||||
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/forms/AutoDnsConnectorForm.tsx:25
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:26
|
||||
msgid "AutoDNS Password"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:29
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:30
|
||||
msgid ""
|
||||
"Attention: AutoDNS do not support 2-Factor Authentication on API Users for "
|
||||
"automated systems"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:37
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:38
|
||||
msgid "Owner nic-handle"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:41
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:42
|
||||
msgid "The nic-handle of the domain name owner"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:43
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:44
|
||||
msgid "You can get it from this page"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:53
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:54
|
||||
msgid "Context Value"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:57
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:58
|
||||
msgid "If you not sure, use the default value 4"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:66
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:67
|
||||
msgid "Owner confirmation"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:73
|
||||
#: assets/utils/providers/forms/AutoDnsConnectorForm.tsx:74
|
||||
msgid "Owner confirms his consent of domain order jobs"
|
||||
msgstr ""
|
||||
|
||||
@ -1158,121 +1175,219 @@ msgstr ""
|
||||
msgid "API Terms of Service"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:18
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:16
|
||||
msgid ""
|
||||
"I have read and accepted the conditions of use of the Provider API, "
|
||||
"accessible from this hyperlink"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:24
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:22
|
||||
msgid "Legal age"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:30
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:27
|
||||
msgid "I am of the minimum age required to consent to these conditions"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:35
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:32
|
||||
msgid "Withdrawal period"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:41
|
||||
#: assets/utils/providers/forms/DefaultConnectorFormItems.tsx:37
|
||||
msgid ""
|
||||
"I waive my right of withdrawal regarding the purchase of domain names via "
|
||||
"the Provider's API"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:9
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:61
|
||||
msgid ""
|
||||
"This connector is experimental and its implementation has not been fully "
|
||||
"tested. Please submit an issue for any issues related to it."
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:66
|
||||
msgid "The EPP connector is a special type of connector. Be careful."
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:71
|
||||
msgid "Server configuration"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:72
|
||||
msgid "Protocol"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:76
|
||||
msgid "Version"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:88
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:101
|
||||
msgid "Server"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:121
|
||||
msgid "Credentials"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:144
|
||||
msgid "TLS client certificate"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:162
|
||||
msgid "Private key passphrase (optional)"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:169
|
||||
msgid "TLS configuration"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:174
|
||||
msgid "Verify peer"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:183
|
||||
msgid "Verify peer name"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:192
|
||||
msgid "Allow self-signed certificates"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:202
|
||||
msgid "Domain configuration"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:204
|
||||
msgid "Registration period"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:217
|
||||
msgid "Year"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:218
|
||||
msgid "Month"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:225
|
||||
msgid "Auth-Info Code"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:233
|
||||
msgid "NIC Handle"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:261
|
||||
msgid "Onsite"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:273
|
||||
msgid "Protocol configuration"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:274
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/EppConnectorForm.tsx:280
|
||||
msgid "Extensions"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:10
|
||||
msgid "Personal Access Token (PAT)"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:12
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:13
|
||||
msgid ""
|
||||
"Retrieve a Personal Access Token from your customer account on the "
|
||||
"Provider's website"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:19
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:20
|
||||
msgid "Organization sharing ID"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:23
|
||||
#: assets/utils/providers/forms/GandiConnectorForm.tsx:24
|
||||
msgid "It indicates the organization that will pay for the ordered product"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:12
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:13
|
||||
msgid ""
|
||||
"Retreive an API key and whitelist this instance's IP address on Namecheap's "
|
||||
"website"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:18
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:24
|
||||
#: assets/utils/providers/forms/NamecheapConnectorForm.tsx:19
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:25
|
||||
msgid "API key"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:18
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:41
|
||||
#: assets/utils/providers/forms/NamecomConnectorForm.tsx:19
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:42
|
||||
msgid ""
|
||||
"Retrieve a set of tokens from your customer account on the Provider's "
|
||||
"website"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:14
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:15
|
||||
msgid "European Region"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:15
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:16
|
||||
msgid "United States Region"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:16
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:17
|
||||
msgid "Canada Region"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:21
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:22
|
||||
msgid "Europe"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:24
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:25
|
||||
msgid "The domain is free and at the standard price"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:27
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:28
|
||||
msgid ""
|
||||
"The domain is free but can be premium. Its price varies from one domain to "
|
||||
"another"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:34
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:35
|
||||
msgid "Application key"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:48
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:49
|
||||
msgid "Application secret"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:56
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:57
|
||||
msgid "Consumer key"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:64
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:65
|
||||
msgid "OVH Endpoint"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:71
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:72
|
||||
msgid "OVH subsidiary"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:79
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:80
|
||||
msgid "OVH pricing mode"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:84
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:85
|
||||
msgid "Confirm pricing mode"
|
||||
msgstr ""
|
||||
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:85
|
||||
#: assets/utils/providers/forms/OvhCloudConnectorForm.tsx:86
|
||||
msgid ""
|
||||
"Are you sure about this setting? This may result in additional charges from "
|
||||
"the API Provider"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user