From 5476ee7acc5c133d355486ce142e0d70765643f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Mon, 8 Dec 2025 18:18:33 +0100 Subject: [PATCH 1/5] feat: allow unauthenticated users to perform domain name lookups --- assets/App.tsx | 60 ++++++++++++-------- assets/components/LoginForm.tsx | 8 +-- assets/components/Sider.tsx | 10 +++- assets/contexts/index.ts | 24 ++++++++ assets/pages/LoginPage.tsx | 33 +++-------- assets/pages/search/DomainSearchPage.tsx | 19 ++++--- assets/utils/api/index.ts | 1 + config/packages/rate_limiter.yaml | 7 ++- config/packages/security.yaml | 1 + config/services.yaml | 16 ++++++ src/Controller/InstanceController.php | 3 +- src/Entity/Domain.php | 2 +- src/Entity/Instance.php | 14 +++++ src/Security/Voter/RdapLookupVoter.php | 33 +++++++++++ src/State/AutoRegisterDomainProvider.php | 21 ++++--- translations/translations.pot | 72 ++++++++++++------------ 16 files changed, 214 insertions(+), 110 deletions(-) create mode 100644 assets/contexts/index.ts create mode 100644 src/Security/Voter/RdapLookupVoter.php diff --git a/assets/App.tsx b/assets/App.tsx index e7d9abc..ab54632 100644 --- a/assets/App.tsx +++ b/assets/App.tsx @@ -9,8 +9,8 @@ import WatchlistPage from './pages/tracking/WatchlistPage' import UserPage from './pages/UserPage' import type {PropsWithChildren} from 'react' import React, { useCallback, useEffect, useMemo, useState} from 'react' -import {getUser} from './utils/api' -import LoginPage, {AuthenticatedContext} from './pages/LoginPage' +import {getConfiguration, getUser, type InstanceConfig} from './utils/api' +import LoginPage from './pages/LoginPage' import ConnectorPage from './pages/tracking/ConnectorPage' import NotFoundPage from './pages/NotFoundPage' import useBreakpoint from './hooks/useBreakpoint' @@ -19,12 +19,14 @@ import {jt, t} from 'ttag' import {MenuOutlined} from '@ant-design/icons' import TrackedDomainPage from './pages/tracking/TrackedDomainPage' import IcannRegistrarPage from "./pages/infrastructure/IcannRegistrarPage" +import type {AuthContextType} from "./contexts" +import { AuthenticatedContext, ConfigurationContext} from "./contexts" const PROJECT_LINK = 'https://github.com/maelgangloff/domain-watchdog' const LICENSE_LINK = 'https://www.gnu.org/licenses/agpl-3.0.txt' const ProjectLink = Domain Watchdog -const LicenseLink = AGPL-3.0-or-later +const LicenseLink = AGPL-3.0-or-later function SiderWrapper(props: PropsWithChildren<{sidebarCollapsed: boolean, setSidebarCollapsed: (collapsed: boolean) => void}>): React.ReactElement { const {sidebarCollapsed, setSidebarCollapsed, children} = props @@ -68,18 +70,21 @@ export default function App(): React.ReactElement { const [sidebarCollapsed, setSidebarCollapsed] = useState(false) const [isAuthenticated, setIsAuthenticated] = useState(false) - const authenticated = useCallback((authenticated: boolean) => { - setIsAuthenticated(authenticated) - }, []) - - const contextValue = useMemo(() => ({ - authenticated, - setIsAuthenticated - }), [authenticated, setIsAuthenticated]) + const [configuration, setConfiguration] = useState(undefined) const [darkMode, setDarkMode] = useState(false) - const windowQuery = window.matchMedia('(prefers-color-scheme:dark)') + + + const authContextValue: AuthContextType = useMemo(() => ({ + isAuthenticated, + setIsAuthenticated + }), [isAuthenticated]) + + const configContextValue = useMemo(() => ({ + configuration, + }), [configuration]) + const darkModeChange = useCallback((event: MediaQueryListEvent) => { setDarkMode(event.matches) }, []) @@ -93,13 +98,18 @@ export default function App(): React.ReactElement { useEffect(() => { setDarkMode(windowQuery.matches) - getUser().then(() => { - setIsAuthenticated(true) - if (location.pathname === '/login') navigate('/home') - }).catch(() => { - setIsAuthenticated(false) - const pathname = location.pathname - if (!['/login', '/tos', '/faq', '/privacy'].includes(pathname)) navigate('/home') + getConfiguration().then(configuration => { + setConfiguration(configuration) + + getUser().then(() => { + setIsAuthenticated(true) + if (location.pathname === '/login') navigate('/home') + }).catch(() => { + setIsAuthenticated(false) + const pathname = location.pathname + if(configuration.publicRdapLookupEnabled) return navigate('/search/domain') + if (!['/login', '/tos', '/faq', '/privacy'].includes(pathname)) return navigate('/home') + }) }) }, []) @@ -109,10 +119,11 @@ export default function App(): React.ReactElement { algorithm: darkMode ? theme.darkAlgorithm : undefined }} > - + + - + @@ -129,7 +140,7 @@ export default function App(): React.ReactElement { }} > - }/> + }/> }/> }/> @@ -158,8 +169,8 @@ export default function App(): React.ReactElement { - - + + + ) } diff --git a/assets/components/LoginForm.tsx b/assets/components/LoginForm.tsx index 966353f..c78496b 100644 --- a/assets/components/LoginForm.tsx +++ b/assets/components/LoginForm.tsx @@ -2,10 +2,10 @@ import {Button, Flex, Form, Input, message} from 'antd' import {t} from 'ttag' import React, {useContext, useEffect, useState} from 'react' import {getUser, login} from '../utils/api' -import {AuthenticatedContext} from '../pages/LoginPage' import {useNavigate} from 'react-router-dom' import {showErrorAPI} from '../utils/functions/showErrorAPI' +import {AuthenticatedContext} from "../contexts" interface FieldType { username: string @@ -66,9 +66,9 @@ export function LoginForm({ssoLogin}: { ssoLogin?: boolean }) { - + {ssoLogin &&