feat: add instance config endpoint

This commit is contained in:
Maël Gangloff
2024-08-04 15:49:38 +02:00
parent e413bfedc0
commit 7cf2375952
13 changed files with 115 additions and 23 deletions

5
.env
View File

@@ -38,7 +38,6 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###> symfony/mailer ### ###> symfony/mailer ###
# MAILER_DSN=null://null # MAILER_DSN=null://null
MAILER_SENDER_EMAIL=notifications@example.com
###< symfony/mailer ### ###< symfony/mailer ###
###> nelmio/cors-bundle ### ###> nelmio/cors-bundle ###
@@ -57,6 +56,10 @@ JWT_PASSPHRASE=827c9f8cce8bb82e75b2aec4a14a61f572ac28c7a8531f08dcdf1652573a7049
LOCK_DSN=flock LOCK_DSN=flock
###< symfony/lock ### ###< symfony/lock ###
MAILER_SENDER_EMAIL=notifications@example.com
LIMITED_FEATURES=false
OAUTH_ENABLED=false
OAUTH_CLIENT_ID= OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET= OAUTH_CLIENT_SECRET=
OAUTH_AUTHORIZATION_URL= OAUTH_AUTHORIZATION_URL=

View File

@@ -54,4 +54,4 @@ Contributions are welcome as long as they do not contravene the Code of Conduct.
[^1]: RFC 3912 : WHOIS Protocol Specification. (2004). IETF Datatracker. https://datatracker.ietf.org/doc/html/rfc3912 [^1]: RFC 3912 : WHOIS Protocol Specification. (2004). IETF Datatracker. https://datatracker.ietf.org/doc/html/rfc3912
[^2]: 2023 Global Amendments to the Base gTLD Registry Agreement (RA), Specification 13, and 2013 Registrar [^2]: 2023 Global Amendments to the Base gTLD Registry Agreement (RA), Specification 13, and 2013 Registrar
Accreditation Agreement (RAA) - ICANN. (s. d.). https://www.icann.org/resources/pages/global-amendment-2023-en Accreditation Agreement (RAA) - ICANN. (2023). https://www.icann.org/resources/pages/global-amendment-2023-en

View File

@@ -48,7 +48,7 @@ export default function App() {
if (location.pathname === '/login') navigate('/home') if (location.pathname === '/login') navigate('/home')
}).catch(() => { }).catch(() => {
setIsAuthenticated(false) setIsAuthenticated(false)
navigate('/home') if (location.pathname !== '/login') navigate('/home')
}) })
}, []); }, []);

View File

@@ -1,6 +1,6 @@
import React, {createContext, useContext, useEffect, useState} from "react"; import React, {createContext, useContext, useEffect, useState} from "react";
import {Alert, Button, Card, Flex, Form, Input} from "antd"; import {Alert, Button, Card, Flex, Form, Input} from "antd";
import {getUser, login} from "../utils/api"; import {getConfiguration, getUser, InstanceConfig, login} from "../utils/api";
import {useNavigate} from "react-router-dom"; import {useNavigate} from "react-router-dom";
import {t} from 'ttag' import {t} from 'ttag'
@@ -13,7 +13,8 @@ export const AuthenticatedContext = createContext<any>(null)
export default function LoginPage() { export default function LoginPage() {
const [error, setError] = useState() const [error, setError] = useState<string>()
const [configuration, setConfiguration] = useState<InstanceConfig>()
const navigate = useNavigate() const navigate = useNavigate()
const {setIsAuthenticated} = useContext(AuthenticatedContext) const {setIsAuthenticated} = useContext(AuthenticatedContext)
@@ -32,6 +33,7 @@ export default function LoginPage() {
setIsAuthenticated(true) setIsAuthenticated(true)
navigate('/home') navigate('/home')
}) })
getConfiguration().then(setConfiguration)
}, []) }, [])
return <Flex gap="middle" align="center" justify="center" vertical><Card return <Flex gap="middle" align="center" justify="center" vertical><Card
@@ -75,11 +77,11 @@ export default function LoginPage() {
{t`Submit`} {t`Submit`}
</Button> </Button>
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{offset: 8, span: 16}}> {configuration?.ssoLogin && <Form.Item wrapperCol={{offset: 8, span: 16}}>
<Button type="primary" htmlType="button" href="/login/oauth"> <Button type="primary" htmlType="button" href="/login/oauth">
{t`Log in with SSO`} {t`Log in with SSO`}
</Button> </Button>
</Form.Item> </Form.Item>}
</Form> </Form>
</Card> </Card>
</Flex> </Flex>

View File

@@ -71,6 +71,11 @@ export interface Watchlist {
connector?: string connector?: string
} }
export interface InstanceConfig {
ssoLogin: boolean
limtedFeatures: boolean
}
export async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> { export async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> {
const axiosConfig: AxiosRequestConfig = { const axiosConfig: AxiosRequestConfig = {
...config, ...config,
@@ -84,6 +89,7 @@ export async function request<T = any, R = AxiosResponse<T>, D = any>(config: Ax
return await axios.request<T, R, D>(axiosConfig) return await axios.request<T, R, D>(axiosConfig)
} }
export * from './domain' export * from './domain'
export * from './tld' export * from './tld'
export * from './user' export * from './user'

View File

@@ -1,4 +1,4 @@
import {request, User} from "./index"; import {InstanceConfig, request, User} from "./index";
export async function login(email: string, password: string): Promise<boolean> { export async function login(email: string, password: string): Promise<boolean> {
@@ -16,3 +16,10 @@ export async function getUser(): Promise<User> {
}) })
return response.data return response.data
} }
export async function getConfiguration(): Promise<InstanceConfig> {
const response = await request<InstanceConfig>({
url: 'config'
})
return response.data
}

View File

@@ -70,6 +70,7 @@ security:
- { path: ^/api$, roles: PUBLIC_ACCESS } - { path: ^/api$, roles: PUBLIC_ACCESS }
- { path: ^/api/docs, roles: PUBLIC_ACCESS } - { path: ^/api/docs, roles: PUBLIC_ACCESS }
- { path: "^/api/watchlists/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/calendar$", roles: PUBLIC_ACCESS } - { path: "^/api/watchlists/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/calendar$", roles: PUBLIC_ACCESS }
- { path: "^/api/config$", roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY } - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
when@test: when@test:

View File

@@ -4,7 +4,9 @@
# Put parameters here that don't need to change on each machine where the app is deployed # Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters: parameters:
mailer_sender_email: '%env(MAILER_SENDER_EMAIL)%' mailer_sender_email: '%env(string:MAILER_SENDER_EMAIL)%'
oauth_enabled: '%env(bool:OAUTH_ENABLED)%'
limited_features: '%env(bool:LIMITED_FEATURES)%'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Controller;
use App\Entity\Instance;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class InstanceController extends AbstractController
{
public function __invoke(): Instance
{
$instance = new Instance();
$instance
->setLimitedFeatures($this->getParameter('limited_features') ?? false)
->setOauthEnabled($this->getParameter('oauth_enabled'));
return $instance;
}
}

View File

@@ -24,11 +24,13 @@ use Symfony\Component\Uid\Uuid;
name: 'get_all_mine', name: 'get_all_mine',
), ),
new Get( new Get(
normalizationContext: ['groups' => 'connector:list'] normalizationContext: ['groups' => 'connector:list'],
security: 'object.user == user'
), ),
new Post( new Post(
routeName: 'connector_create', normalizationContext: ['groups' => ['connector:create', 'connector:list']], routeName: 'connector_create',
denormalizationContext: ['groups' => 'connector:create'], normalizationContext: ['groups' => ['connector:create', 'connector:list']], denormalizationContext: ['groups' => 'connector:create'],
security: 'object.user == user',
name: 'create' name: 'create'
), ),
new Delete(), new Delete(),
@@ -44,7 +46,7 @@ class Connector
#[ORM\ManyToOne(inversedBy: 'connectors')] #[ORM\ManyToOne(inversedBy: 'connectors')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private ?User $user = null; public ?User $user = null;
#[Groups(['connector:list', 'connector:create', 'watchlist:list'])] #[Groups(['connector:list', 'connector:create', 'watchlist:list'])]
#[ORM\Column(enumType: ConnectorProvider::class)] #[ORM\Column(enumType: ConnectorProvider::class)]

48
src/Entity/Instance.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\Controller\InstanceController;
#[ApiResource(
operations: [
new Get(
uriTemplate: '/config',
controller: InstanceController::class,
shortName: 'Configuration',
read: false,
),
]
)]
class Instance
{
private ?bool $oauthEnabled = null;
private ?bool $limitedFeatures = null;
public function isSsoLogin(): ?bool
{
return $this->oauthEnabled;
}
public function setOauthEnabled(bool $oauthEnabled): static
{
$this->oauthEnabled = $oauthEnabled;
return $this;
}
public function isLimitedFeatures(): ?bool
{
return $this->limitedFeatures;
}
public function setLimitedFeatures(bool $limitedFeatures): static
{
$this->limitedFeatures = $limitedFeatures;
return $this;
}
}

View File

@@ -27,7 +27,8 @@ use Symfony\Component\Uid\Uuid;
name: 'get_all_mine', name: 'get_all_mine',
), ),
new Get( new Get(
normalizationContext: ['groups' => 'watchlist:item'] normalizationContext: ['groups' => 'watchlist:item'],
security: 'object.user == user'
), ),
new Get( new Get(
routeName: 'watchlist_calendar', routeName: 'watchlist_calendar',
@@ -73,7 +74,7 @@ class WatchList
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'watchLists')] #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'watchLists')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private ?User $user = null; public ?User $user = null;
/** /**
* @var Collection<int, Domain> * @var Collection<int, Domain>

View File

@@ -57,8 +57,8 @@ msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:110 #: assets/components/tracking/ConnectorForm.tsx:110
#: assets/components/tracking/ConnectorForm.tsx:119 #: assets/components/tracking/ConnectorForm.tsx:119
#: assets/components/tracking/WatchlistForm.tsx:102 #: assets/components/tracking/WatchlistForm.tsx:102
#: assets/pages/LoginPage.tsx:60 #: assets/pages/LoginPage.tsx:62
#: assets/pages/LoginPage.tsx:68 #: assets/pages/LoginPage.tsx:70
msgid "Required" msgid "Required"
msgstr "" msgstr ""
@@ -319,7 +319,7 @@ msgid "Log out"
msgstr "" msgstr ""
#: assets/components/Sider.tsx:120 #: assets/components/Sider.tsx:120
#: assets/pages/LoginPage.tsx:38 #: assets/pages/LoginPage.tsx:40
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
@@ -427,7 +427,7 @@ msgid ""
"their country of origin." "their country of origin."
msgstr "" msgstr ""
#: assets/pages/LoginPage.tsx:58 #: assets/pages/LoginPage.tsx:60
#: assets/pages/watchdog/UserPage.tsx:18 #: assets/pages/watchdog/UserPage.tsx:18
msgid "Username" msgid "Username"
msgstr "" msgstr ""
@@ -456,19 +456,19 @@ msgstr ""
msgid "Sorry, the page you visited does not exist." msgid "Sorry, the page you visited does not exist."
msgstr "" msgstr ""
#: assets/pages/LoginPage.tsx:43 #: assets/pages/LoginPage.tsx:45
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: assets/pages/LoginPage.tsx:66 #: assets/pages/LoginPage.tsx:68
msgid "Password" msgid "Password"
msgstr "" msgstr ""
#: assets/pages/LoginPage.tsx:75 #: assets/pages/LoginPage.tsx:77
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
#: assets/pages/LoginPage.tsx:80 #: assets/pages/LoginPage.tsx:82
msgid "Log in with SSO" msgid "Log in with SSO"
msgstr "" msgstr ""