mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 17:55:42 +00:00
Merge branch 'feat/domain-by-registrant' into develop
This commit is contained in:
commit
3b7cd91d43
@ -42,6 +42,7 @@
|
||||
"protonlabs/vobject": "^4.31",
|
||||
"psr/http-client": "^1.0",
|
||||
"runtime/frankenphp-symfony": "^0.2.0",
|
||||
"scienta/doctrine-json-functions": "^6.3",
|
||||
"symfony/asset": "7.3.*",
|
||||
"symfony/asset-mapper": "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",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "49fd7fad6776160ff572f3fb9201a8a2",
|
||||
"content-hash": "1db291e0d108c6bb06d447134f1250c6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "api-platform/core",
|
||||
@ -4189,6 +4189,78 @@
|
||||
},
|
||||
"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",
|
||||
"version": "v7.3.0",
|
||||
@ -15319,7 +15391,7 @@
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=8.2",
|
||||
"php": ">=8.4",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-simplexml": "*"
|
||||
|
||||
@ -24,6 +24,9 @@ doctrine:
|
||||
alias: App
|
||||
controller_resolver:
|
||||
auto_mapping: false
|
||||
dql:
|
||||
string_functions:
|
||||
JSONB_CONTAINS: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbContains
|
||||
|
||||
when@test:
|
||||
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(
|
||||
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
|
||||
{
|
||||
#[ORM\Id]
|
||||
@ -63,6 +65,24 @@ class Entity
|
||||
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
||||
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)]
|
||||
#[Groups(['entity:item', 'domain:item', 'watchlist:item'])]
|
||||
private ?array $remarks = null;
|
||||
@ -239,4 +259,28 @@ class Entity
|
||||
|
||||
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,53 @@ namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Entity\Domain;
|
||||
use App\Repository\DomainRepository;
|
||||
use App\Service\RDAPService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
readonly class FindDomainCollectionFromEntityProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private RequestStack $requestStack,
|
||||
private EntityManagerInterface $em,
|
||||
private DomainRepository $domainRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
$rsm = (new ResultSetMapping())
|
||||
->addScalarResult('domain_ids', 'domain_ids');
|
||||
$registrant = trim((string) $request->get('registrant'));
|
||||
|
||||
$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
|
||||
SELECT
|
||||
array_agg(DISTINCT de.domain_id) AS domain_ids
|
||||
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'], '{}')));
|
||||
|
||||
if (empty($domainList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->em->getRepository(Domain::class)
|
||||
->createQueryBuilder('d')
|
||||
->where('d.ldhName IN (:list)')
|
||||
->setParameter('list', $domainList)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
foreach ($forbidden as $word) {
|
||||
if (str_contains(strtolower($registrant), $word)) {
|
||||
throw new BadRequestHttpException('Forbidden search term');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->domainRepository->createQueryBuilder('d')
|
||||
->select('DISTINCT d')
|
||||
->join('d.domainEntities', 'de', Join::WITH, 'de.deletedAt IS NULL AND JSONB_CONTAINS(de.roles, :role) = true')
|
||||
->join(
|
||||
'de.entity',
|
||||
'e',
|
||||
Join::WITH,
|
||||
'e.tld IS NOT NULL AND e.handle NOT IN (:blacklist) AND (UPPER(e.jCardOrg) = UPPER(:registrant) OR UPPER(e.jCardFn) = UPPER(:registrant))'
|
||||
)
|
||||
->setParameter('registrant', $registrant)
|
||||
->setParameter('blacklist', RDAPService::ENTITY_HANDLE_BLACKLIST)
|
||||
->setParameter('role', '"registrant"')
|
||||
->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user