mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 17:55:42 +00:00
feat: add search for a tld via RDAP
This commit is contained in:
parent
7c606e2697
commit
1dc16d769b
@ -12,16 +12,22 @@ export function DomainDiagram({domain}: { domain: Domain }) {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const e = getLayoutedElements([
|
const nodes = [
|
||||||
domainToNode(domain),
|
domainToNode(domain),
|
||||||
...domainEntitiesToNode(domain, true),
|
...domainEntitiesToNode(domain, true),
|
||||||
tldToNode(domain.tld),
|
|
||||||
...domain.nameservers.map(nsToNode)
|
...domain.nameservers.map(nsToNode)
|
||||||
].flat(), [
|
].flat()
|
||||||
|
const edges = [
|
||||||
domainEntitiesToEdges(domain, true),
|
domainEntitiesToEdges(domain, true),
|
||||||
tldToEdge(domain),
|
|
||||||
...domainNSToEdges(domain)
|
...domainNSToEdges(domain)
|
||||||
].flat())
|
].flat()
|
||||||
|
|
||||||
|
if (domain.tld.tld !== '.') {
|
||||||
|
nodes.push(tldToNode(domain.tld))
|
||||||
|
edges.push(tldToEdge(domain))
|
||||||
|
}
|
||||||
|
|
||||||
|
const e = getLayoutedElements(nodes, edges)
|
||||||
|
|
||||||
setNodes(e.nodes)
|
setNodes(e.nodes)
|
||||||
setEdges(e.edges)
|
setEdges(e.edges)
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export function DomainResult({domain}: { domain: Domain }) {
|
|||||||
<Badge.Ribbon text={
|
<Badge.Ribbon text={
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={tld.type === 'ccTLD' ? regionNames.of(getCountryCode(tld.tld)) : tld.type === 'gTLD' ? tld?.registryOperator : undefined}>
|
title={tld.type === 'ccTLD' ? regionNames.of(getCountryCode(tld.tld)) : tld.type === 'gTLD' ? tld?.registryOperator : undefined}>
|
||||||
{`.${domain.tld.tld.toUpperCase()} (${tld.type})`}
|
{`${(domain.tld.tld === '.' ? '' : '.') + domain.tld.tld.toUpperCase()} (${tld.type})`}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
color={
|
color={
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export function DomainSearchBar({onFinish}: { onFinish: (values: FieldType) => v
|
|||||||
required: true,
|
required: true,
|
||||||
message: t`Required`
|
message: t`Required`
|
||||||
}, {
|
}, {
|
||||||
pattern: /^(?=.*\.)\S*[^.\s]$/,
|
pattern: /^(?=.*\.)?\S*[^.\s]$/,
|
||||||
message: t`This domain name does not appear to be valid`,
|
message: t`This domain name does not appear to be valid`,
|
||||||
max: 63,
|
max: 63,
|
||||||
min: 2
|
min: 2
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export function watchlistToNodes(watchlist: Watchlist, withRegistrar = false, wi
|
|||||||
|
|
||||||
const domains = watchlist.domains.map(domainToNode)
|
const domains = watchlist.domains.map(domainToNode)
|
||||||
const entities = [...new Set(watchlist.domains.map(d => domainEntitiesToNode(d, withRegistrar)).flat())]
|
const entities = [...new Set(watchlist.domains.map(d => domainEntitiesToNode(d, withRegistrar)).flat())]
|
||||||
const tlds = [...new Set(watchlist.domains.map(d => d.tld))].map(tldToNode)
|
const tlds = [...new Set(watchlist.domains.map(d => d.tld))].filter(t => t.tld !== '.').map(tldToNode)
|
||||||
const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(nsToNode, withRegistrar)
|
const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(nsToNode, withRegistrar)
|
||||||
|
|
||||||
return [...domains, ...entities, ...nameservers, ...(withTld ? tlds : [])]
|
return [...domains, ...entities, ...nameservers, ...(withTld ? tlds : [])]
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"publication": "1970-01-01T00:00:00Z",
|
"publication": "1970-01-01T00:00:00Z",
|
||||||
|
|
||||||
"services": [
|
"services": [
|
||||||
|
[ [ "" ], [ "https://rdap.iana.org/" ] ], # If you want to make RDAP queries to get TLD information from IANA
|
||||||
[ [ "ad" ], [ "https://rdap.nic.ad/" ] ],
|
[ [ "ad" ], [ "https://rdap.nic.ad/" ] ],
|
||||||
[ [ "ae" ], [ "https://rdap.nic.ae/" ] ],
|
[ [ "ae" ], [ "https://rdap.nic.ae/" ] ],
|
||||||
[ [ "ki" ], [ "https://rdap.nic.ki/" ] ],
|
[ [ "ki" ], [ "https://rdap.nic.ki/" ] ],
|
||||||
|
|||||||
33
migrations/Version20241220161843.php
Normal file
33
migrations/Version20241220161843.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20241220161843 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE entity ALTER COLUMN j_card TYPE JSONB USING j_card::JSONB;');
|
||||||
|
$this->addSql('ALTER TABLE connector ALTER COLUMN auth_data TYPE JSONB USING auth_data::JSONB;');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE entity ALTER COLUMN j_card TYPE JSON USING j_card::JSON;');
|
||||||
|
$this->addSql('ALTER TABLE connector ALTER COLUMN auth_data TYPE JSON USING auth_data::JSON;');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,4 +9,5 @@ enum TldType: string
|
|||||||
case sTLD = 'sTLD';
|
case sTLD = 'sTLD';
|
||||||
case ccTLD = 'ccTLD';
|
case ccTLD = 'ccTLD';
|
||||||
case tTLD = 'tTLD';
|
case tTLD = 'tTLD';
|
||||||
|
case root = 'root';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ class Tld
|
|||||||
|
|
||||||
public function getTld(): ?string
|
public function getTld(): ?string
|
||||||
{
|
{
|
||||||
return $this->tld;
|
return '' === $this->tld ? '.' : $this->tld;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTld(string $tld): static
|
public function setTld(string $tld): static
|
||||||
@ -194,8 +194,10 @@ class Tld
|
|||||||
return $this->type;
|
return $this->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setType(?TldType $type): void
|
public function setType(?TldType $type): static
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,7 +191,7 @@ readonly class RDAPService
|
|||||||
if (count($addedStatus) > 0 || count($deletedStatus) > 0) {
|
if (count($addedStatus) > 0 || count($deletedStatus) > 0) {
|
||||||
$this->em->persist($domain);
|
$this->em->persist($domain);
|
||||||
|
|
||||||
if ($domain->getUpdatedAt() == $domain->getCreatedAt()) {
|
if ($domain->getUpdatedAt() != $domain->getCreatedAt()) {
|
||||||
$this->em->persist((new DomainStatus())
|
$this->em->persist((new DomainStatus())
|
||||||
->setDomain($domain)
|
->setDomain($domain)
|
||||||
->setDate($domain->getUpdatedAt())
|
->setDate($domain->getUpdatedAt())
|
||||||
@ -252,10 +252,6 @@ readonly class RDAPService
|
|||||||
|
|
||||||
if (array_key_exists('entities', $res) && is_array($res['entities'])) {
|
if (array_key_exists('entities', $res) && is_array($res['entities'])) {
|
||||||
foreach ($res['entities'] as $rdapEntity) {
|
foreach ($res['entities'] as $rdapEntity) {
|
||||||
if (!array_key_exists('handle', $rdapEntity) || '' === $rdapEntity['handle']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$entity = $this->registerEntity($rdapEntity);
|
$entity = $this->registerEntity($rdapEntity);
|
||||||
|
|
||||||
$this->em->persist($entity);
|
$this->em->persist($entity);
|
||||||
@ -274,10 +270,14 @@ readonly class RDAPService
|
|||||||
fn ($e) => $e['roles'],
|
fn ($e) => $e['roles'],
|
||||||
array_filter(
|
array_filter(
|
||||||
$res['entities'],
|
$res['entities'],
|
||||||
fn ($e) => array_key_exists('handle', $e) && $e['handle'] === $rdapEntity['handle']
|
fn ($e) => $rdapEntity === $e
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (0 == count($roles)) {
|
||||||
|
dd($entity);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flatten the array
|
* Flatten the array
|
||||||
*/
|
*/
|
||||||
@ -344,7 +344,7 @@ readonly class RDAPService
|
|||||||
fn (array $e): array => $e['roles'],
|
fn (array $e): array => $e['roles'],
|
||||||
array_filter(
|
array_filter(
|
||||||
$rdapNameserver['entities'],
|
$rdapNameserver['entities'],
|
||||||
fn ($e) => array_key_exists('handle', $e) && $e['handle'] === $rdapEntity['handle']
|
fn ($e) => $rdapEntity === $e
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -374,6 +374,9 @@ readonly class RDAPService
|
|||||||
|
|
||||||
private function getTld($domain): ?object
|
private function getTld($domain): ?object
|
||||||
{
|
{
|
||||||
|
if (!str_contains($domain, '.')) {
|
||||||
|
return $this->tldRepository->findOneBy(['tld' => '']);
|
||||||
|
}
|
||||||
$lastDotPosition = strrpos($domain, '.');
|
$lastDotPosition = strrpos($domain, '.');
|
||||||
if (false === $lastDotPosition) {
|
if (false === $lastDotPosition) {
|
||||||
throw new BadRequestException('Domain must contain at least one dot');
|
throw new BadRequestException('Domain must contain at least one dot');
|
||||||
@ -388,6 +391,21 @@ readonly class RDAPService
|
|||||||
*/
|
*/
|
||||||
private function registerEntity(array $rdapEntity): Entity
|
private function registerEntity(array $rdapEntity): Entity
|
||||||
{
|
{
|
||||||
|
$conn = $this->em->getConnection();
|
||||||
|
$sql = 'SELECT * FROM entity WHERE j_Card @> :data';
|
||||||
|
$stmt = $conn->prepare($sql)->executeQuery(['data' => json_encode($rdapEntity['vcardArray'])]);
|
||||||
|
|
||||||
|
$result = $stmt->fetchAllAssociative();
|
||||||
|
|
||||||
|
$rdapEntity['handle'] = array_key_exists('handle', $rdapEntity) && '' !== $rdapEntity['handle']
|
||||||
|
? $rdapEntity['handle']
|
||||||
|
: (
|
||||||
|
count($result) > 0
|
||||||
|
? $result[0]['handle']
|
||||||
|
: 'DW-NOHANDLE-'.hash('md5', json_encode($rdapEntity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$entity = $this->entityRepository->findOneBy([
|
$entity = $this->entityRepository->findOneBy([
|
||||||
'handle' => $rdapEntity['handle'],
|
'handle' => $rdapEntity['handle'],
|
||||||
]);
|
]);
|
||||||
@ -481,9 +499,13 @@ readonly class RDAPService
|
|||||||
foreach ($dnsRoot['services'] as $service) {
|
foreach ($dnsRoot['services'] as $service) {
|
||||||
foreach ($service[0] as $tld) {
|
foreach ($service[0] as $tld) {
|
||||||
if ('' === $tld) {
|
if ('' === $tld) {
|
||||||
continue;
|
if (null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
||||||
|
$this->em->persist((new Tld())->setTld('.')->setType(TldType::root));
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$tldReference = $this->em->getReference(Tld::class, $tld);
|
$tldReference = $this->em->getReference(Tld::class, $tld);
|
||||||
|
|
||||||
foreach ($service[1] as $rdapServerUrl) {
|
foreach ($service[1] as $rdapServerUrl) {
|
||||||
$server = $this->rdapServerRepository->findOneBy(['tld' => $tldReference, 'url' => $rdapServerUrl]);
|
$server = $this->rdapServerRepository->findOneBy(['tld' => $tldReference, 'url' => $rdapServerUrl]);
|
||||||
if (null === $server) {
|
if (null === $server) {
|
||||||
@ -601,10 +623,10 @@ readonly class RDAPService
|
|||||||
if ('' === $gTld['gTLD']) {
|
if ('' === $gTld['gTLD']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/** @var Tld $gtTldEntity */
|
/** @var Tld|null $gtTldEntity */
|
||||||
$gtTldEntity = $this->tldRepository->findOneBy(['tld' => $gTld['gTLD']]);
|
$gtTldEntity = $this->tldRepository->findOneBy(['tld' => $gTld['gTLD']]);
|
||||||
|
|
||||||
if (null == $gtTldEntity) {
|
if (null === $gtTldEntity) {
|
||||||
$gtTldEntity = new Tld();
|
$gtTldEntity = new Tld();
|
||||||
$gtTldEntity->setTld($gTld['gTLD']);
|
$gtTldEntity->setTld($gTld['gTLD']);
|
||||||
$this->logger->notice('New gTLD detected according to ICANN ({tld}).', [
|
$this->logger->notice('New gTLD detected according to ICANN ({tld}).', [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user