chore: merge master

This commit is contained in:
Maël Gangloff
2024-08-17 22:05:48 +02:00
14 changed files with 215 additions and 85 deletions

View File

@@ -0,0 +1,47 @@
import React, {useEffect} from "react";
import {Background, Controls, MiniMap, ReactFlow, useEdgesState, useNodesState} from "@xyflow/react";
import {Flex} from "antd";
import {Domain} from "../../utils/api";
import {getLayoutedElements} from "../tracking/watchlist/diagram/getLayoutedElements";
import {domainEntitiesToNode, domainToNode, nsToNode, tldToNode} from "../tracking/watchlist/diagram/watchlistToNodes";
import {domainEntitiesToEdges, domainNSToEdges, tldToEdge} from "../tracking/watchlist/diagram/watchlistToEdges";
export function DomainDiagram({domain}: { domain: Domain }) {
const [nodes, setNodes, onNodesChange] = useNodesState([])
const [edges, setEdges, onEdgesChange] = useEdgesState([])
useEffect(() => {
const e = getLayoutedElements([
domainToNode(domain),
...domainEntitiesToNode(domain),
tldToNode(domain.tld),
...domain.nameservers.map(nsToNode)
].flat(), [
domainEntitiesToEdges(domain),
tldToEdge(domain),
...domainNSToEdges(domain)
].flat())
setNodes(e.nodes)
setEdges(e.edges)
}, [])
return <Flex style={{width: '80vw', height: '80vh'}}>
<ReactFlow
fitView
colorMode='system'
nodesConnectable={false}
edgesReconnectable={false}
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
style={{width: '100%', height: '100%'}}
>
<MiniMap/>
<Controls/>
<Background/>
</ReactFlow>
</Flex>
}

View File

@@ -9,7 +9,7 @@ import punycode from "punycode/punycode";
import {Connector} from "../../../utils/api/connectors";
import {UpdateWatchlistButton} from "./UpdateWatchlistButton";
import {DeleteWatchlistButton} from "./DeleteWatchlistButton";
import {ViewDiagramWatchlistButton} from "../diagram/ViewDiagramWatchlistButton";
import {ViewDiagramWatchlistButton} from "./diagram/ViewDiagramWatchlistButton";
export function WatchlistsList({watchlists, onDelete, onUpdateWatchlist, connectors}: {
watchlists: Watchlist[],

View File

@@ -2,72 +2,26 @@ import {Button, Flex, Modal, Space, Typography} from "antd"
import {t} from "ttag"
import React, {useEffect, useState} from "react"
import {ApartmentOutlined} from "@ant-design/icons"
import vCard from "vcf";
import '@xyflow/react/dist/style.css'
import {Background, Controls, MiniMap, ReactFlow, useEdgesState, useNodesState} from "@xyflow/react";
import {getWatchlist, Watchlist} from "../../../utils/api";
import {translateRoles} from "../../search/EntitiesList";
import {getWatchlist} from "../../../../utils/api";
import {getLayoutedElements} from "./getLayoutedElements";
import {watchlistToNodes} from "./watchlistToNodes";
import {watchlistToEdges} from "./watchlistToEdges";
function watchlistToNodes(watchlist: Watchlist) {
const domains = watchlist.domains.map(d => ({
id: d.ldhName,
data: {label: <b>{d.ldhName}</b>},
style: {
width: 200
}
}))
const entities = [...new Set(watchlist.domains
.map(d => d.entities
.filter(e => !e.roles.includes('registrar'))
.map(e => e.entity
)
).flat())].map(e => {
const jCard = vCard.fromJSON(e.jCard)
let label = e.handle
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) label = jCard.data.fn.valueOf()
return {
id: e.handle,
data: {label},
style: {
width: 200
}
}
})
return [...domains, ...entities]
}
const rolesToColor = (roles: string[]) => roles.includes('registrant') ? 'green' :
roles.includes('administrative') ? 'blue' :
roles.includes('technical') ? 'orange' : 'violet'
function watchlistToEdges(watchlist: Watchlist) {
const domainRole = translateRoles()
return watchlist.domains
.map(d => d.entities
.filter(e => !e.roles.includes('registrar'))
.map(e => ({
id: `${d.ldhName}-${e.entity.handle}`,
source: e.roles.includes('technical') ? d.ldhName : e.entity.handle,
target: e.roles.includes('technical') ? e.entity.handle : d.ldhName,
style: {stroke: rolesToColor(e.roles), strokeWidth: 3},
label: e.roles.map(r => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', '),
animated: e.roles.includes('registrant'),
}))
).flat(2)
export type DiagramConfig = {
tld?: boolean
nameserver?: boolean
entities?: boolean
}
export function ViewDiagramWatchlistButton({token}: { token: string }) {
const [open, setOpen] = useState(false)
const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [nodes, setNodes, onNodesChange] = useNodesState([])
const [edges, setEdges, onEdgesChange] = useEdgesState([])
useEffect(() => {
if (!open) return

View File

@@ -0,0 +1,47 @@
import {Domain, Watchlist} from "../../../../utils/api";
import {translateRoles} from "../../../search/EntitiesList";
import {t} from "ttag";
const rolesToColor = (roles: string[]) => roles.includes('registrant') ? 'green' :
roles.includes('administrative') ? 'blue' :
roles.includes('technical') ? 'orange' : 'violet'
export function domainEntitiesToEdges(d: Domain) {
const domainRole = translateRoles()
return d.entities
.filter(e => !e.roles.includes('registrar')) //
.map(e => ({
id: `e-${d.ldhName}-${e.entity.handle}`,
source: e.roles.includes('registrant') ? e.entity.handle : d.ldhName,
target: e.roles.includes('registrant') ? d.ldhName : e.entity.handle,
style: {stroke: rolesToColor(e.roles), strokeWidth: 3},
label: e.roles
.map(r => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r)
.join(', '),
animated: e.roles.includes('registrant'),
}))
}
export const domainNSToEdges = (d: Domain) => d.nameservers
.map(ns => ({
id: `ns-${d.ldhName}-${ns.ldhName}`,
source: d.ldhName,
target: ns.ldhName,
style: {stroke: 'grey', strokeWidth: 3},
label: 'DNS'
}))
export const tldToEdge = (d: Domain) => ({
id: `tld-${d.ldhName}-${d.tld.tld}`,
source: d.tld.tld,
target: d.ldhName,
style: {stroke: 'yellow', strokeWidth: 3},
label: t`Registry`
})
export function watchlistToEdges(watchlist: Watchlist) {
const entitiesEdges = watchlist.domains.map(domainEntitiesToEdges).flat()
const nameserversEdges = watchlist.domains.map(domainNSToEdges).flat()
return [...entitiesEdges, ...nameserversEdges]
}

View File

@@ -0,0 +1,54 @@
import {Domain, Nameserver, Tld, Watchlist} from "../../../../utils/api";
import vCard from "vcf";
import React from "react";
import {t} from 'ttag'
export const domainToNode = (d: Domain) => ({
id: d.ldhName,
data: {label: <b>{d.ldhName}</b>},
style: {
width: 200
}
})
export const domainEntitiesToNode = (d: Domain) => d.entities
.filter(e => !e.roles.includes('registrar')) //
.map(e => {
const jCard = vCard.fromJSON(e.entity.jCard)
let label = e.entity.handle
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) label = jCard.data.fn.valueOf()
return {
id: e.entity.handle,
data: {label},
style: {
width: 200
}
}
})
export const tldToNode = (tld: Tld) => ({
id: tld.tld,
data: {label: t`.${tld.tld} Registry`},
style: {
width: 200
}
})
export const nsToNode = (ns: Nameserver) => ({
id: ns.ldhName,
data: {label: ns.ldhName},
style: {
width: 200
}
})
export function watchlistToNodes(watchlist: Watchlist) {
const domains = watchlist.domains.map(domainToNode)
const entities = [...new Set(watchlist.domains.map(domainEntitiesToNode).flat())]
const tlds = [...new Set(watchlist.domains.map(d => d.tld))].map(tldToNode)
const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(nsToNode)
return [...domains, ...entities, ...nameservers]
}

View File

@@ -7,6 +7,7 @@ import {DomainSearchBar, FieldType} from "../../components/search/DomainSearchBa
import {EventTimeline} from "../../components/search/EventTimeline";
import {EntitiesList} from "../../components/search/EntitiesList";
import {showErrorAPI} from "../../utils";
import {DomainDiagram} from "../../components/search/DomainDiagram";
const {Text} = Typography;
@@ -68,6 +69,7 @@ export default function DomainSearchPage() {
}
</Card>
</Badge.Ribbon>
<DomainDiagram domain={domain}/>
</Space>
: <Empty
description={t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}/>)

View File

@@ -1,6 +1,6 @@
import React, {useEffect, useState} from "react";
import {Card, Divider, Flex, Form, message} from "antd";
import {EventAction, getWatchlists, putWatchlist, postWatchlist} from "../../utils/api";
import {EventAction, getWatchlists, postWatchlist, putWatchlist} from "../../utils/api";
import {AxiosError} from "axios";
import {t} from 'ttag'
import {WatchlistForm} from "../../components/tracking/watchlist/WatchlistForm";
@@ -37,9 +37,12 @@ export default function WatchlistPage() {
connector?: string,
dsn?: string[]
}) => {
const domainsURI = values.domains.map(d => '/api/domains/' + d)
let triggers = values.triggers.map(t => ({event: t, action: 'email'}))
if(values.dsn !== undefined) triggers = [...triggers, ...values.triggers.map(t => ({event: t, action: 'chat'}))]
const domainsURI = values.domains.map(d => '/api/domains/' + d.toLowerCase())
let triggers = values.triggers.map(t => ({event: t, action: 'email'}))
if (values.dsn !== undefined) triggers = [...triggers, ...values.triggers.map(t => ({
event: t,
action: 'chat'
}))]
postWatchlist({
name: values.name,
@@ -64,10 +67,12 @@ export default function WatchlistPage() {
connector?: string,
dsn?: string[]
}) => {
const domainsURI = values.domains.map(d => '/api/domains/' + d)
let triggers = values.triggers.map(t => ({event: t, action: 'email'}))
if(values.dsn !== undefined) triggers = [...triggers, ...values.triggers.map(t => ({event: t, action: 'chat'}))]
const domainsURI = values.domains.map(d => '/api/domains/' + d.toLowerCase())
let triggers = values.triggers.map(t => ({event: t, action: 'email'}))
if (values.dsn !== undefined) triggers = [...triggers, ...values.triggers.map(t => ({
event: t,
action: 'chat'
}))]
return putWatchlist({
token: values.token,

View File

@@ -43,7 +43,7 @@ class JWTAuthenticator implements AuthenticationSuccessHandlerInterface
new Cookie(
'BEARER',
$jwt,
time() + 7200, // expiration
time() + 604800, // expiration
'/',
null,
true,

View File

@@ -75,7 +75,7 @@ class OAuthAuthenticator extends OAuth2Authenticator implements AuthenticationEn
new Cookie(
'BEARER',
$token,
time() + 7200, // expiration
time() + 604800, // expiration
'/',
null,
true,

View File

@@ -278,6 +278,8 @@ readonly class RDAPService
}
if (array_key_exists('nameservers', $res) && is_array($res['nameservers'])) {
$domain->getNameservers()->clear();
foreach ($res['nameservers'] as $rdapNameserver) {
$nameserver = $this->nameserverRepository->findOneBy([
'ldhName' => strtolower($rdapNameserver['ldhName']),

View File

@@ -246,11 +246,16 @@ msgstr "Ja"
msgid "No"
msgstr "Nein"
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:86
#: assets/components/tracking/diagram/WatchlistToNodes.tsx:37
#, javascript-format
msgid ".${ tld.tld } Registry"
msgstr ""
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:40
msgid "View the Watchlist Entity Diagram"
msgstr "Sehen Sie sich das Watchlist-Entitätsdiagramm an"
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:91
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:45
msgid "Watchlist Entity Diagram"
msgstr "Watchlist-Entitäten Diagramm"

View File

@@ -246,11 +246,16 @@ msgstr "Oui"
msgid "No"
msgstr "Non"
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:86
#: assets/components/tracking/diagram/WatchlistToNodes.tsx:37
#, javascript-format
msgid ".${ tld.tld } Registry"
msgstr ""
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:40
msgid "View the Watchlist Entity Diagram"
msgstr "Afficher le diagramme d'entités de la Watchlist"
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:91
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:45
msgid "Watchlist Entity Diagram"
msgstr "Diagramme d'entités de la Watchlist"

View File

@@ -236,14 +236,6 @@ msgstr ""
msgid "No"
msgstr ""
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:86
msgid "View the Watchlist Entity Diagram"
msgstr ""
#: assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx:91
msgid "Watchlist Entity Diagram"
msgstr ""
#: assets/components/tracking/watchlist/WatchlistForm.tsx:66
msgid "Name"
msgstr ""
@@ -329,6 +321,23 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:40
msgid "View the Watchlist Entity Diagram"
msgstr ""
#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:45
msgid "Watchlist Entity Diagram"
msgstr ""
#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:39
msgid "Registry"
msgstr ""
#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:32
#, javascript-format
msgid ".${ tld.tld } Registry"
msgstr ""
#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12
#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19
msgid "Delete the Watchlist"
@@ -427,27 +436,27 @@ msgstr ""
msgid "Register"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:21
#: assets/pages/search/DomainSearchPage.tsx:22
msgid "Found !"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:29
#: assets/pages/search/DomainSearchPage.tsx:30
msgid "Domain finder"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:50
#: assets/pages/search/DomainSearchPage.tsx:51
msgid "EPP Status Codes"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:60
#: assets/pages/search/DomainSearchPage.tsx:61
msgid "Timeline"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:65
#: assets/pages/search/DomainSearchPage.tsx:66
msgid "Entities"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:73
#: assets/pages/search/DomainSearchPage.tsx:75
msgid ""
"Although the domain exists in my database, it has been deleted from the "
"WHOIS by its registrar."