Merge branch 'fix-disconnect' into 'develop'
Revert "refactor logout handling to retrieve EasyCheck URL dynamically from database" See merge request easy-solutions/apps/easyportal!39
This commit is contained in:
commit
4b89d1a256
2
.env
2
.env
|
|
@ -75,3 +75,5 @@ AWS_S3_PORTAL_URL=https://s3.amazonaws.com/portal
|
||||||
###< aws/aws-sdk-php-symfony ###
|
###< aws/aws-sdk-php-symfony ###
|
||||||
APP_URL='https://example.com'
|
APP_URL='https://example.com'
|
||||||
APP_DOMAIN='example.com'
|
APP_DOMAIN='example.com'
|
||||||
|
|
||||||
|
EASYCHECK_URL='https://testcheck.solutions-easy.com'
|
||||||
|
|
@ -13,6 +13,7 @@ parameters:
|
||||||
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
||||||
oauth_sso_identifier: '%env(OAUTH_SSO_IDENTIFIER)%'
|
oauth_sso_identifier: '%env(OAUTH_SSO_IDENTIFIER)%'
|
||||||
oauth_sso_identifier_login: '%env(OAUTH_SSO_IDENTIFIER_LOGIN)%'
|
oauth_sso_identifier_login: '%env(OAUTH_SSO_IDENTIFIER_LOGIN)%'
|
||||||
|
easycheck_url: '%env(EASYCHECK_URL)%'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
|
|
@ -61,5 +62,7 @@ services:
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|
||||||
App\EventListener\LogoutSubscriber:
|
App\EventListener\LogoutSubscriber:
|
||||||
|
arguments:
|
||||||
|
$easycheckUrl: '%env(EASYCHECK_URL)%'
|
||||||
tags:
|
tags:
|
||||||
- { name: kernel.event_subscriber }
|
- { name: kernel.event_subscriber }
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ Lorsqu'un utilisateur se déconnecte d'une application, il est **automatiquement
|
||||||
└─> Redirection vers EasyCheck
|
└─> Redirection vers EasyCheck
|
||||||
|
|
||||||
6. EasyPortal → Redirection
|
6. EasyPortal → Redirection
|
||||||
└─> GET {{ easycheck_url }}/logout
|
└─> GET https://check.../logout
|
||||||
|
|
||||||
7. EasyCheck → Invalide la session
|
7. EasyCheck → Invalide la session
|
||||||
└─> Session détruite, cookies supprimés
|
└─> Session détruite, cookies supprimés
|
||||||
|
|
@ -133,6 +133,9 @@ OAUTH_CLIENT_SECRET='secret-key'
|
||||||
### EasyPortal (.env)
|
### EasyPortal (.env)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# URL de l'application cliente (EasyCheck)
|
||||||
|
EASYCHECK_URL='https://check.solutions-easy.moi'
|
||||||
|
|
||||||
# Configuration OAuth2 Server
|
# Configuration OAuth2 Server
|
||||||
OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/jwt/private.key
|
OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/jwt/private.key
|
||||||
OAUTH_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.key
|
OAUTH_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.key
|
||||||
|
|
@ -140,8 +143,6 @@ OAUTH_PASSPHRASE='passphrase'
|
||||||
OAUTH_ENCRYPTION_KEY='encryption-key'
|
OAUTH_ENCRYPTION_KEY='encryption-key'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note** : L'URL d'EasyCheck n'est plus stockée dans une variable d'environnement mais récupérée dynamiquement depuis la table `oauth2_client` en base de données.
|
|
||||||
|
|
||||||
## Points importants
|
## Points importants
|
||||||
|
|
||||||
### Sécurité
|
### Sécurité
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Entity;
|
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
|
||||||
|
|
||||||
#[ORM\Entity]
|
|
||||||
#[ORM\Table(name: 'oauth2_client')]
|
|
||||||
class OAuth2Client
|
|
||||||
{
|
|
||||||
#[ORM\Id]
|
|
||||||
#[ORM\Column(type: 'string', length: 32)]
|
|
||||||
private ?string $identifier = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', length: 128)]
|
|
||||||
private ?string $name = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', length: 128, nullable: true)]
|
|
||||||
private ?string $secret = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'text', nullable: true)]
|
|
||||||
private ?string $redirectUris = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'text', nullable: true)]
|
|
||||||
private ?string $grants = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'text', nullable: true)]
|
|
||||||
private ?string $scopes = null;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'boolean')]
|
|
||||||
private bool $active = false;
|
|
||||||
|
|
||||||
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
|
||||||
private bool $allowPlainTextPkce = false;
|
|
||||||
|
|
||||||
public function getIdentifier(): ?string
|
|
||||||
{
|
|
||||||
return $this->identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setIdentifier(string $identifier): self
|
|
||||||
{
|
|
||||||
$this->identifier = $identifier;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName(): ?string
|
|
||||||
{
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setName(string $name): self
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSecret(): ?string
|
|
||||||
{
|
|
||||||
return $this->secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setSecret(?string $secret): self
|
|
||||||
{
|
|
||||||
$this->secret = $secret;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRedirectUris(): ?string
|
|
||||||
{
|
|
||||||
return $this->redirectUris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRedirectUris(?string $redirectUris): self
|
|
||||||
{
|
|
||||||
$this->redirectUris = $redirectUris;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRedirectUrisArray(): array
|
|
||||||
{
|
|
||||||
if (!$this->redirectUris) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decoded = json_decode($this->redirectUris, true);
|
|
||||||
if (is_array($decoded)) {
|
|
||||||
return $decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$this->redirectUris];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGrants(): ?string
|
|
||||||
{
|
|
||||||
return $this->grants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setGrants(?string $grants): self
|
|
||||||
{
|
|
||||||
$this->grants = $grants;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getScopes(): ?string
|
|
||||||
{
|
|
||||||
return $this->scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setScopes(?string $scopes): self
|
|
||||||
{
|
|
||||||
$this->scopes = $scopes;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isActive(): bool
|
|
||||||
{
|
|
||||||
return $this->active;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setActive(bool $active): self
|
|
||||||
{
|
|
||||||
$this->active = $active;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAllowPlainTextPkce(): bool
|
|
||||||
{
|
|
||||||
return $this->allowPlainTextPkce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAllowPlainTextPkce(bool $allowPlainTextPkce): self
|
|
||||||
{
|
|
||||||
$this->allowPlainTextPkce = $allowPlainTextPkce;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBaseUrl(): ?string
|
|
||||||
{
|
|
||||||
$uris = $this->getRedirectUrisArray();
|
|
||||||
if (empty($uris)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$firstUri = $uris[0];
|
|
||||||
$parsed = parse_url($firstUri);
|
|
||||||
|
|
||||||
if (!isset($parsed['scheme'], $parsed['host']) || !$parsed) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$baseUrl = $parsed['scheme'] . '://' . $parsed['host'];
|
|
||||||
|
|
||||||
if (isset($parsed['port']) && !in_array($parsed['port'], [80, 443], true)) {
|
|
||||||
$baseUrl .= ':' . $parsed['port'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $baseUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\EventListener;
|
namespace App\EventListener;
|
||||||
|
|
||||||
use App\Repository\OAuth2ClientRepository;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
|
@ -11,7 +10,7 @@ use Symfony\Component\Security\Http\Event\LogoutEvent;
|
||||||
class LogoutSubscriber implements EventSubscriberInterface
|
class LogoutSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly OAuth2ClientRepository $oauth2ClientRepository,
|
private readonly string $easycheckUrl,
|
||||||
private readonly LoggerInterface $logger
|
private readonly LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
@ -25,21 +24,7 @@ class LogoutSubscriber implements EventSubscriberInterface
|
||||||
|
|
||||||
public function onLogout(LogoutEvent $event): void
|
public function onLogout(LogoutEvent $event): void
|
||||||
{
|
{
|
||||||
$easycheckClient = $this->oauth2ClientRepository->findByName('check');
|
$easycheckLogoutUrl = $this->easycheckUrl . '/logout';
|
||||||
|
|
||||||
if (!$easycheckClient) {
|
|
||||||
$this->logger->error('OAuth2 client "check" not found in database');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$easycheckUrl = $easycheckClient->getBaseUrl();
|
|
||||||
|
|
||||||
if (!$easycheckUrl) {
|
|
||||||
$this->logger->error('Unable to determine base URL for EasyCheck client');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$easycheckLogoutUrl = $easycheckUrl . '/logout';
|
|
||||||
|
|
||||||
$this->logger->info('LogoutSubscriber triggered - redirecting to EasyCheck logout', [
|
$this->logger->info('LogoutSubscriber triggered - redirecting to EasyCheck logout', [
|
||||||
'easycheck_logout_url' => $easycheckLogoutUrl,
|
'easycheck_logout_url' => $easycheckLogoutUrl,
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repository;
|
|
||||||
|
|
||||||
use App\Entity\OAuth2Client;
|
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
|
||||||
|
|
||||||
class OAuth2ClientRepository extends ServiceEntityRepository
|
|
||||||
{
|
|
||||||
public function __construct(ManagerRegistry $registry)
|
|
||||||
{
|
|
||||||
parent::__construct($registry, OAuth2Client::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findByName(string $name): ?OAuth2Client
|
|
||||||
{
|
|
||||||
return $this->findOneBy(['name' => $name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findByIdentifier(string $identifier): ?OAuth2Client
|
|
||||||
{
|
|
||||||
return $this->findOneBy(['identifier' => $identifier]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findActiveClients(): array
|
|
||||||
{
|
|
||||||
return $this->createQueryBuilder('c')
|
|
||||||
->where('c.active = :active')
|
|
||||||
->setParameter('active', true)
|
|
||||||
->getQuery()
|
|
||||||
->getResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue