mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 09:45:29 +00:00
refactor: search domain by registrant name
This commit is contained in:
parent
d769c48955
commit
8b03c54a16
@ -42,6 +42,7 @@
|
|||||||
"protonlabs/vobject": "^4.31",
|
"protonlabs/vobject": "^4.31",
|
||||||
"psr/http-client": "^1.0",
|
"psr/http-client": "^1.0",
|
||||||
"runtime/frankenphp-symfony": "^0.2.0",
|
"runtime/frankenphp-symfony": "^0.2.0",
|
||||||
|
"scienta/doctrine-json-functions": "^6.3",
|
||||||
"symfony/asset": "7.3.*",
|
"symfony/asset": "7.3.*",
|
||||||
"symfony/asset-mapper": "7.3.*",
|
"symfony/asset-mapper": "7.3.*",
|
||||||
"symfony/cache": "7.3.*",
|
"symfony/cache": "7.3.*",
|
||||||
|
|||||||
76
composer.lock
generated
76
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "49fd7fad6776160ff572f3fb9201a8a2",
|
"content-hash": "1db291e0d108c6bb06d447134f1250c6",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "api-platform/core",
|
"name": "api-platform/core",
|
||||||
@ -4189,6 +4189,78 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-09-06T08:00:55+00:00"
|
"time": "2024-09-06T08:00:55+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "scienta/doctrine-json-functions",
|
||||||
|
"version": "6.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ScientaNL/DoctrineJsonFunctions.git",
|
||||||
|
"reference": "554b2fd281e976a791501fc4753ffd4c5891ec62"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ScientaNL/DoctrineJsonFunctions/zipball/554b2fd281e976a791501fc4753ffd4c5891ec62",
|
||||||
|
"reference": "554b2fd281e976a791501fc4753ffd4c5891ec62",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"doctrine/dbal": "^3.2 || ^4",
|
||||||
|
"doctrine/lexer": "^2.0 || ^3.0",
|
||||||
|
"doctrine/orm": "^2.19 || ^3",
|
||||||
|
"ext-pdo": "*",
|
||||||
|
"php": "^8.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/coding-standard": "^9.0 || ^10.0 || ^11.0 || ^12.0",
|
||||||
|
"phpstan/extension-installer": "^1.4",
|
||||||
|
"phpstan/phpstan": "^1.12",
|
||||||
|
"phpstan/phpstan-doctrine": "^1.4",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.4",
|
||||||
|
"phpunit/phpunit": "^10.1",
|
||||||
|
"psalm/plugin-phpunit": "^0.18",
|
||||||
|
"slevomat/coding-standard": "~8",
|
||||||
|
"symfony/cache": "^5.4 || ^6.4 || ^7",
|
||||||
|
"vimeo/psalm": "^5.2",
|
||||||
|
"webmozart/assert": "^1.11"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"dunglas/doctrine-json-odm": "To serialize / deserialize objects as JSON documents."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Scienta\\DoctrineJsonFunctions\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Doctrine Json Functions Contributors",
|
||||||
|
"homepage": "https://github.com/ScientaNL/DoctrineJsonFunctions/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A set of extensions to Doctrine that add support for json query functions.",
|
||||||
|
"keywords": [
|
||||||
|
"database",
|
||||||
|
"doctrine",
|
||||||
|
"dql",
|
||||||
|
"json",
|
||||||
|
"mariadb",
|
||||||
|
"mysql",
|
||||||
|
"orm",
|
||||||
|
"postgres",
|
||||||
|
"postgresql",
|
||||||
|
"sqlite"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/ScientaNL/DoctrineJsonFunctions/issues",
|
||||||
|
"source": "https://github.com/ScientaNL/DoctrineJsonFunctions/tree/6.3.0"
|
||||||
|
},
|
||||||
|
"time": "2024-11-08T12:33:19+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/asset",
|
"name": "symfony/asset",
|
||||||
"version": "v7.3.0",
|
"version": "v7.3.0",
|
||||||
@ -15319,7 +15391,7 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": ">=8.2",
|
"php": ">=8.4",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"ext-simplexml": "*"
|
"ext-simplexml": "*"
|
||||||
|
|||||||
@ -24,6 +24,9 @@ doctrine:
|
|||||||
alias: App
|
alias: App
|
||||||
controller_resolver:
|
controller_resolver:
|
||||||
auto_mapping: false
|
auto_mapping: false
|
||||||
|
dql:
|
||||||
|
string_functions:
|
||||||
|
JSONB_CONTAINS: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbContains
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
doctrine:
|
doctrine:
|
||||||
|
|||||||
35
migrations/Version20251108171723.php
Normal file
35
migrations/Version20251108171723.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?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 Version20251108171723 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add j_card_fn and j_card_org';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE entity ADD j_card_fn VARCHAR(255) GENERATED ALWAYS AS (UPPER(jsonb_path_query_first(j_card, \'$[1]?(@[0] == "fn")[3]\') #>> \'{}\')) STORED');
|
||||||
|
$this->addSql('ALTER TABLE entity ADD j_card_org VARCHAR(255) GENERATED ALWAYS AS (UPPER(jsonb_path_query_first(j_card, \'$[1]?(@[0] == "org")[3]\') #>> \'{}\')) STORED');
|
||||||
|
$this->addSql('CREATE INDEX entity_j_card_fn_idx ON entity (j_card_fn)');
|
||||||
|
$this->addSql('CREATE INDEX entity_j_card_org_idx ON entity (j_card_org)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('DROP INDEX entity_j_card_fn_idx');
|
||||||
|
$this->addSql('DROP INDEX entity_j_card_org_idx');
|
||||||
|
$this->addSql('ALTER TABLE entity DROP j_card_fn');
|
||||||
|
$this->addSql('ALTER TABLE entity DROP j_card_org');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,8 @@ use Symfony\Component\Serializer\Attribute\SerializedName;
|
|||||||
#[ORM\UniqueConstraint(
|
#[ORM\UniqueConstraint(
|
||||||
columns: ['tld_id', 'handle']
|
columns: ['tld_id', 'handle']
|
||||||
)]
|
)]
|
||||||
|
#[ORM\Index(name: 'entity_j_card_fn_idx', columns: ['j_card_fn'])]
|
||||||
|
#[ORM\Index(name: 'entity_j_card_org_idx', columns: ['j_card_org'])]
|
||||||
class Entity
|
class Entity
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
@ -63,6 +65,24 @@ class Entity
|
|||||||
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
||||||
private array $jCard = [];
|
private array $jCard = [];
|
||||||
|
|
||||||
|
#[ORM\Column(
|
||||||
|
type: 'string',
|
||||||
|
insertable: false,
|
||||||
|
updatable: false,
|
||||||
|
columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (LOWER(jsonb_path_query_first(j_card, '$[1]?(@[0] == \"fn\")[3]') #>> '{}')) STORED",
|
||||||
|
generated: 'ALWAYS',
|
||||||
|
)]
|
||||||
|
private ?string $jCardFn;
|
||||||
|
|
||||||
|
#[ORM\Column(
|
||||||
|
type: 'string',
|
||||||
|
insertable: false,
|
||||||
|
updatable: false,
|
||||||
|
columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (LOWER(jsonb_path_query_first(j_card, '$[1]?(@[0] == \"org\")[3]') #>> '{}')) STORED",
|
||||||
|
generated: 'ALWAYS',
|
||||||
|
)]
|
||||||
|
private ?string $jCardOrg;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
||||||
private ?array $remarks = null;
|
private ?array $remarks = null;
|
||||||
@ -239,4 +259,28 @@ class Entity
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getJCardFn(): ?string
|
||||||
|
{
|
||||||
|
return $this->jCardFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJCardOrg(): ?string
|
||||||
|
{
|
||||||
|
return $this->jCardOrg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setJCardFn(?string $jCardFn): Entity
|
||||||
|
{
|
||||||
|
$this->jCardFn = $jCardFn;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setJCardOrg(?string $jCardOrg): Entity
|
||||||
|
{
|
||||||
|
$this->jCardOrg = $jCardOrg;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,79 +4,63 @@ namespace App\State;
|
|||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
use App\Entity\Domain;
|
use App\Repository\DomainRepository;
|
||||||
|
use App\Repository\EntityRepository;
|
||||||
use App\Service\RDAPService;
|
use App\Service\RDAPService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Doctrine\ORM\Query\ResultSetMapping;
|
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
readonly class FindDomainCollectionFromEntityProvider implements ProviderInterface
|
readonly class FindDomainCollectionFromEntityProvider implements ProviderInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private RequestStack $requestStack,
|
private RequestStack $requestStack,
|
||||||
private EntityManagerInterface $em,
|
private EntityRepository $entityRepository,
|
||||||
|
private DomainRepository $domainRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
|
||||||
{
|
{
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
$rsm = (new ResultSetMapping())
|
$registrant = trim((string) $request->get('registrant'));
|
||||||
->addScalarResult('domain_ids', 'domain_ids');
|
|
||||||
|
|
||||||
$handleBlacklist = join(',', array_map(fn (string $s) => "'$s'", RDAPService::ENTITY_HANDLE_BLACKLIST));
|
$forbidden = [
|
||||||
|
'redacted',
|
||||||
|
'privacy',
|
||||||
|
'registration private',
|
||||||
|
'domain administrator',
|
||||||
|
'registry super user account',
|
||||||
|
'ano nymous',
|
||||||
|
'by proxy',
|
||||||
|
];
|
||||||
|
|
||||||
$sql = <<<SQL
|
foreach ($forbidden as $word) {
|
||||||
SELECT
|
if (str_contains(strtolower($registrant), $word)) {
|
||||||
array_agg(DISTINCT de.domain_id) AS domain_ids
|
throw new HttpException(403, 'Forbidden search term');
|
||||||
FROM (
|
}
|
||||||
SELECT
|
|
||||||
e.handle AS handle,
|
|
||||||
e.id,
|
|
||||||
e.tld_id,
|
|
||||||
jsonb_path_query_first(
|
|
||||||
e.j_card,
|
|
||||||
'$[1] ? (@[0] == "fn")[3]'
|
|
||||||
) #>> '{}' AS fn,
|
|
||||||
jsonb_path_query_first(
|
|
||||||
e.j_card,
|
|
||||||
'$[1] ? (@[0] == "org")[3]'
|
|
||||||
) #>> '{}' AS org
|
|
||||||
FROM entity e
|
|
||||||
) sub
|
|
||||||
JOIN domain_entity de ON de.entity_uid = sub.id
|
|
||||||
WHERE LOWER(org||fn) NOT LIKE '%redacted for privacy%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%data protected%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%registration private%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%domain administrator%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%registry super user account%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%ano nymous%'
|
|
||||||
AND LOWER(org||fn) NOT LIKE '%redacted%'
|
|
||||||
AND LOWER(org||fn) NOT IN ('na', 'n/a', '-', 'none', 'not applicable')
|
|
||||||
AND handle NOT IN ($handleBlacklist)
|
|
||||||
AND de.roles @> '["registrant"]'
|
|
||||||
AND sub.tld_id IS NOT NULL
|
|
||||||
AND (LOWER(org) = LOWER(:registrant) OR LOWER(fn) = LOWER(:registrant));
|
|
||||||
SQL;
|
|
||||||
$result = $this->em->createNativeQuery($sql, $rsm)
|
|
||||||
->setParameter('registrant', trim($request->get('registrant')))
|
|
||||||
->getOneOrNullResult();
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$domainList = array_filter(explode(',', trim($result['domain_ids'], '{}')));
|
$entities = $this->entityRepository->createQueryBuilder('e')
|
||||||
|
->where('e.tld IS NOT NULL')
|
||||||
|
->andWhere('e.handle NOT IN (:blacklist)')
|
||||||
|
->andWhere('UPPER(e.jCardOrg) = UPPER(:registrant) OR UPPER(e.jCardFn) = UPPER(:registrant)')
|
||||||
|
->setParameter('registrant', $registrant)
|
||||||
|
->setParameter('blacklist', RDAPService::ENTITY_HANDLE_BLACKLIST)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
if (empty($domainList)) {
|
if (empty($entities)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->em->getRepository(Domain::class)
|
return $this->domainRepository->createQueryBuilder('d')
|
||||||
->createQueryBuilder('d')
|
->select('DISTINCT d')
|
||||||
->where('d.ldhName IN (:list)')
|
->join('d.domainEntities', 'de')
|
||||||
->setParameter('list', $domainList)
|
->where('de.entity IN (:entityIds)')
|
||||||
->getQuery()
|
->andWhere('JSONB_CONTAINS(de.roles, :role) = true')
|
||||||
->getResult();
|
->andWhere('de.deletedAt IS NULL')
|
||||||
|
->setParameter('entityIds', array_map(fn ($e) => $e->getId(), $entities))
|
||||||
|
->setParameter('role', '"registrant"')
|
||||||
|
->getQuery()->getResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user