set up fonctionnal OAuth2 worklow with consent
This commit is contained in:
parent
fb87a53593
commit
1a30b94863
4
.env
4
.env
|
@ -48,3 +48,7 @@ OAUTH_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
||||||
OAUTH_PASSPHRASE=8170ea18d2e3e05b5c7ae0672a754bf4
|
OAUTH_PASSPHRASE=8170ea18d2e3e05b5c7ae0672a754bf4
|
||||||
OAUTH_ENCRYPTION_KEY=f1b7c279f7992205a0df45e295d07066
|
OAUTH_ENCRYPTION_KEY=f1b7c279f7992205a0df45e295d07066
|
||||||
###< league/oauth2-server-bundle ###
|
###< league/oauth2-server-bundle ###
|
||||||
|
|
||||||
|
###> nelmio/cors-bundle ###
|
||||||
|
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||||
|
###< nelmio/cors-bundle ###
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
},
|
},
|
||||||
"@symfony/ux-turbo": {
|
"@symfony/ux-turbo": {
|
||||||
"turbo-core": {
|
"turbo-core": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"fetch": "eager"
|
"fetch": "eager"
|
||||||
},
|
},
|
||||||
"mercure-turbo-stream": {
|
"mercure-turbo-stream": {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
"league/oauth2-server-bundle": "^0.11.0",
|
"league/oauth2-server-bundle": "^0.11.0",
|
||||||
|
"nelmio/cors-bundle": "^2.5",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6",
|
"phpdocumentor/reflection-docblock": "^5.6",
|
||||||
"phpstan/phpdoc-parser": "^2.1",
|
"phpstan/phpdoc-parser": "^2.1",
|
||||||
"symfony/asset": "7.2.*",
|
"symfony/asset": "7.2.*",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2848578c0a7baf9e3d125fcc61becab2",
|
"content-hash": "685b46acb679219276b83724c15afb9d",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
|
@ -2084,6 +2084,68 @@
|
||||||
],
|
],
|
||||||
"time": "2025-03-24T10:02:05+00:00"
|
"time": "2025-03-24T10:02:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "nelmio/cors-bundle",
|
||||||
|
"version": "2.5.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nelmio/NelmioCorsBundle.git",
|
||||||
|
"reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544",
|
||||||
|
"reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.3.6",
|
||||||
|
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Nelmio\\CorsBundle\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nelmio",
|
||||||
|
"homepage": "http://nelm.io"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application",
|
||||||
|
"keywords": [
|
||||||
|
"api",
|
||||||
|
"cors",
|
||||||
|
"crossdomain"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nelmio/NelmioCorsBundle/issues",
|
||||||
|
"source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0"
|
||||||
|
},
|
||||||
|
"time": "2024-06-24T21:25:28+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nyholm/psr7",
|
"name": "nyholm/psr7",
|
||||||
"version": "1.8.2",
|
"version": "1.8.2",
|
||||||
|
|
|
@ -16,4 +16,5 @@ return [
|
||||||
Symfony\UX\TogglePassword\TogglePasswordBundle::class => ['all' => true],
|
Symfony\UX\TogglePassword\TogglePasswordBundle::class => ['all' => true],
|
||||||
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
||||||
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
||||||
|
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
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
|
||||||
bqPMXh92TaQXrWxDgzO9Ag==
|
SzDJqo7z76xQc3dcBX8MNQ==
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
-----BEGIN PUBLIC KEY-----
|
-----BEGIN PUBLIC KEY-----
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwQueIPrQEJyma0oiV2wG
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwZehUUiAV9vMER+tCpKB
|
||||||
9gl4mpjZQx0QDj/HXyi2hqTjd6z9cfcONmlggD7xoLuiNNmTuVNezHMMC4VNq8/G
|
lLr53bCV2z34r7Qg4gojciR1n7J6esptuLo/JMIYsU1EFmH8dIJ9guc/IufW7hDj
|
||||||
zNQu7Gp18K0uw0WXWWpHtslE3yz9c30FPB4whpz+NMlSXiQEaA2xJxIPxgaMrCG2
|
iwB2l75brVJ+EG8RRN2R4/IlUXz01a56W+TgLGyVM9iNegfwrNQjEEr3WNnz878h
|
||||||
vc8hMPqiN5pid9ErdkGJLaZd9Q/HqIvVPmw9pVK6HTogfHu61hiaHtA5wDxetFH2
|
0KQDsG3+kndh60pIuIiiEqG/yoO9C4enyL5XEDcsyKebb5PeCSTewgGm+DY7vj1F
|
||||||
l7V0oXcbES7fpTXetlNNpIcQ5j5G04HCPWNl8abCcKNUMoDjAXcvKnXNTBaDSfSZ
|
FSZMXxEnOQP/Symz8st1KS7DbbYma/OCkrAsZ+iHC1Ozis0D28uxcdyrPZi36bkV
|
||||||
+JxMjjtVpU8r7sEDmQRlh4CeRqYfimNusm8WO3Yod+PLO33doUhEwBMJOu1s3+oG
|
MKNST42uV86CTEan2yaHaynRUFNHC+bZjA+izOALtg3CyguOqrmL1LL5ID8Q1K8f
|
||||||
rQIDAQAB
|
jQIDAQAB
|
||||||
-----END PUBLIC KEY-----
|
-----END PUBLIC KEY-----
|
||||||
|
|
|
@ -3,6 +3,10 @@ league_oauth2_server:
|
||||||
private_key: '%env(resolve:OAUTH_PRIVATE_KEY)%'
|
private_key: '%env(resolve:OAUTH_PRIVATE_KEY)%'
|
||||||
private_key_passphrase: '%env(resolve:OAUTH_PASSPHRASE)%'
|
private_key_passphrase: '%env(resolve:OAUTH_PASSPHRASE)%'
|
||||||
encryption_key: '%env(resolve:OAUTH_ENCRYPTION_KEY)%'
|
encryption_key: '%env(resolve:OAUTH_ENCRYPTION_KEY)%'
|
||||||
|
access_token_ttl: PT3H # 3 hours
|
||||||
|
refresh_token_ttl: P1M # 1 month
|
||||||
|
auth_code_ttl: PT10M # 10 minutes
|
||||||
|
require_code_challenge_for_public_clients: false
|
||||||
resource_server:
|
resource_server:
|
||||||
public_key: '%env(resolve:OAUTH_PUBLIC_KEY)%'
|
public_key: '%env(resolve:OAUTH_PUBLIC_KEY)%'
|
||||||
scopes:
|
scopes:
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
nelmio_cors:
|
||||||
|
defaults:
|
||||||
|
origin_regex: true
|
||||||
|
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||||
|
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||||
|
allow_headers: ['Content-Type', 'Authorization']
|
||||||
|
expose_headers: ['Link']
|
||||||
|
max_age: 3600
|
||||||
|
paths:
|
||||||
|
'^/token$':
|
||||||
|
origin_regex: true
|
||||||
|
allow_origin: ['*']
|
||||||
|
allow_headers: ['Content-Type', 'Authorization']
|
||||||
|
allow_methods: ['POST', 'OPTIONS']
|
||||||
|
allow_credentials: true
|
||||||
|
max_age: 3600
|
||||||
|
'^/authorize$':
|
||||||
|
origin_regex: true
|
||||||
|
allow_origin: ['*']
|
||||||
|
allow_headers: ['Content-Type', 'Authorization']
|
||||||
|
allow_methods: ['GET', 'POST', 'OPTIONS']
|
||||||
|
allow_credentials: true
|
||||||
|
max_age: 3600
|
||||||
|
'^/login$':
|
||||||
|
origin_regex: true
|
||||||
|
allow_origin: ['*']
|
||||||
|
allow_headers: ['Content-Type', 'Authorization']
|
||||||
|
allow_methods: ['GET', 'POST', 'OPTIONS']
|
||||||
|
allow_credentials: true
|
||||||
|
max_age: 3600
|
|
@ -48,6 +48,7 @@ security:
|
||||||
- { path: ^/login, roles: PUBLIC_ACCESS }
|
- { path: ^/login, roles: PUBLIC_ACCESS }
|
||||||
- { path: ^/token, roles: PUBLIC_ACCESS }
|
- { path: ^/token, roles: PUBLIC_ACCESS }
|
||||||
- { path: ^/oauth2/token, roles: PUBLIC_ACCESS }
|
- { path: ^/oauth2/token, roles: PUBLIC_ACCESS }
|
||||||
|
- { path: ^/token, roles: PUBLIC_ACCESS }
|
||||||
- { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED }
|
- { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED }
|
||||||
- { path: ^/oauth2/userinfo, roles: IS_AUTHENTICATED_FULLY }
|
- { path: ^/oauth2/userinfo, roles: IS_AUTHENTICATED_FULLY }
|
||||||
- { path: ^/, roles: ROLE_USER }
|
- { path: ^/, roles: ROLE_USER }
|
||||||
|
|
|
@ -2,4 +2,4 @@ controllers:
|
||||||
resource:
|
resource:
|
||||||
path: ../src/Controller/
|
path: ../src/Controller/
|
||||||
namespace: App\Controller
|
namespace: App\Controller
|
||||||
type: attribute
|
type: attribute
|
|
@ -19,6 +19,9 @@ services:
|
||||||
- '../src/DependencyInjection/'
|
- '../src/DependencyInjection/'
|
||||||
- '../src/Entity/'
|
- '../src/Entity/'
|
||||||
- '../src/Kernel.php'
|
- '../src/Kernel.php'
|
||||||
|
App\EventSubscriber\:
|
||||||
|
resource: '../src/EventSubscriber/'
|
||||||
|
tags: ['kernel.event_subscriber']
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
# add more service definitions when explicit configuration is needed
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Controller;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
class OAuth2Controller extends AbstractController
|
class OAuth2Controller extends AbstractController
|
||||||
|
@ -17,7 +18,7 @@ class OAuth2Controller extends AbstractController
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'message' => 'Authentification réussie !',
|
'message' => 'Authentification réussie !',
|
||||||
'email' => $user->getEmail(),
|
'email' => $user->getEmail(),
|
||||||
'name' => $user->getUsername(),
|
'name' => $user->getName()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +34,7 @@ class OAuth2Controller extends AbstractController
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'sub' => $user->getId(),
|
'sub' => $user->getId(),
|
||||||
'username' => $user->getUsername(),
|
'username' => $user->getName(),
|
||||||
'email' => $user->getEmail(),
|
'email' => $user->getEmail(),
|
||||||
'roles' => $user->getRoles(),
|
'roles' => $user->getRoles(),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -6,9 +6,18 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use App\Service\CguUserService;
|
||||||
|
|
||||||
class SecurityController extends AbstractController
|
class SecurityController extends AbstractController
|
||||||
{
|
{
|
||||||
|
private CguUserService $cguUserService;
|
||||||
|
|
||||||
|
public function __construct(CguUserService $cguUserService)
|
||||||
|
{
|
||||||
|
$this->cguUserService = $cguUserService;
|
||||||
|
}
|
||||||
|
|
||||||
#[Route(path: '/login', name: 'app_login')]
|
#[Route(path: '/login', name: 'app_login')]
|
||||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||||
{
|
{
|
||||||
|
@ -31,4 +40,23 @@ class SecurityController extends AbstractController
|
||||||
{
|
{
|
||||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route(path: '/consent', name: 'app_consent')]
|
||||||
|
public function consent(Request $request): Response
|
||||||
|
{
|
||||||
|
// Handle form submission
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
// Check if user declined consent
|
||||||
|
if (!$request->request->has('decline')) {
|
||||||
|
// User accepted the CGU, save this in the database
|
||||||
|
$this->cguUserService->acceptLatestCgu($this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect back to the OAuth authorization endpoint with all the query parameters
|
||||||
|
return $this->redirectToRoute('oauth2_authorize', $request->query->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, just show the consent form
|
||||||
|
return $this->render('security/consent.html.twig');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\FirewallMapInterface;
|
use Symfony\Component\Security\Http\FirewallMapInterface;
|
||||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||||
|
use App\Service\CguUserService;
|
||||||
|
|
||||||
class AuthorizationCodeSubscriber implements EventSubscriberInterface
|
class AuthorizationCodeSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
|
@ -20,35 +21,42 @@ class AuthorizationCodeSubscriber implements EventSubscriberInterface
|
||||||
private UrlGeneratorInterface $urlGenerator;
|
private UrlGeneratorInterface $urlGenerator;
|
||||||
private RequestStack $requestStack;
|
private RequestStack $requestStack;
|
||||||
private $firewallName;
|
private $firewallName;
|
||||||
|
private $cguUserService;
|
||||||
|
|
||||||
public function __construct(Security $security, UrlGeneratorInterface $urlGenerator, RequestStack $requestStack, FirewallMapInterface $firewallMap)
|
public function __construct(Security $security, UrlGeneratorInterface $urlGenerator, RequestStack $requestStack, FirewallMapInterface $firewallMap, CguUserService $cguUserService)
|
||||||
{
|
{
|
||||||
$this->security = $security;
|
$this->security = $security;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->requestStack = $requestStack;
|
$this->requestStack = $requestStack;
|
||||||
$this->firewallName = $firewallMap->getFirewallConfig($requestStack->getCurrentRequest())->getName();
|
$this->firewallName = $firewallMap->getFirewallConfig($requestStack->getCurrentRequest())->getName();
|
||||||
|
$this->cguUserService = $cguUserService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onLeagueOauth2ServerEventAuthorizationRequestResolve(AuthorizationRequestResolveEvent $event): void
|
public function onLeagueOauth2ServerEventAuthorizationRequestResolve(AuthorizationRequestResolveEvent $event): void
|
||||||
{
|
{
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
$user = $this->security->getUser();
|
$user = $this->security->getUser();
|
||||||
$this->saveTargetPath($request->getSession(), $this->firewallName, $request->getUri());
|
|
||||||
|
// Default response is to redirect to login if not logged in
|
||||||
$response = new RedirectResponse($this->urlGenerator->generate('app_login'), 307);
|
$response = new RedirectResponse($this->urlGenerator->generate('app_login'), 307);
|
||||||
if ($user instanceof UserInterface) {
|
|
||||||
//On approuve le consentement automatiquement
|
if (!$user instanceof UserInterface) {
|
||||||
$event->resolveAuthorization(true);
|
// Save the target path and redirect to login
|
||||||
$request->getSession()->remove('consent_granted');
|
$this->saveTargetPath($request->getSession(), $this->firewallName, $request->getUri());
|
||||||
|
$event->setResponse($response);
|
||||||
return;
|
return;
|
||||||
//Decommenter et implemeter pour rediriger vers les constentement
|
|
||||||
/*if ($request->getSession()->get('consent_granted') !== null) {
|
|
||||||
$event->resolveAuthorization($request->getSession()->get('consent_granted'));
|
|
||||||
$request->getSession()->remove('consent_granted');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$response = new RedirectResponse($this->urlGenerator->generate('app_consent', $request->query->all()), 307);*/
|
|
||||||
}
|
}
|
||||||
$event->setResponse($response);
|
|
||||||
|
// User is logged in, check if they've accepted the latest CGU
|
||||||
|
if (!$this->cguUserService->isLatestCguAccepted($user)) {
|
||||||
|
// Redirect to consent page with all query parameters
|
||||||
|
$response = new RedirectResponse($this->urlGenerator->generate('app_consent', $request->query->all()), 307);
|
||||||
|
$event->setResponse($response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User has accepted CGU, authorize the request
|
||||||
|
$event->resolveAuthorization(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
|
|
|
@ -16,6 +16,15 @@ class CguRepository extends ServiceEntityRepository
|
||||||
parent::__construct($registry, Cgu::class);
|
parent::__construct($registry, Cgu::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findLatestCgu(): ?Cgu
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('c')
|
||||||
|
->orderBy('c.id', 'DESC')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @return Cgu[] Returns an array of Cgu objects
|
// * @return Cgu[] Returns an array of Cgu objects
|
||||||
// */
|
// */
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use App\Entity\Cgu;
|
||||||
|
use App\Entity\CguUser;
|
||||||
|
|
||||||
|
class CguUserService
|
||||||
|
{
|
||||||
|
public function __construct(private EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLatestCguAccepted(UserInterface $user): bool
|
||||||
|
{
|
||||||
|
$latestCgu = $this->entityManager->getRepository(Cgu::class)->findLatestCgu();
|
||||||
|
if (!$latestCgu) {
|
||||||
|
// If no CGU exists, set to false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cguUser = $this->entityManager->getRepository(CguUser::class)->findOneBy(['users' => $user, 'cgu' => $latestCgu]);
|
||||||
|
if (!$cguUser) {
|
||||||
|
// If the relation doesn't exist, the user hasn't seen or accepted the latest CGU
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cguUser->isAccepted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acceptLatestCgu(UserInterface $user): void
|
||||||
|
{
|
||||||
|
$latestCgu = $this->entityManager->getRepository(Cgu::class)->findLatestCgu();
|
||||||
|
if (!$latestCgu) {
|
||||||
|
// No CGU to accept
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cguUser = $this->entityManager->getRepository(CguUser::class)->findOneBy(['users' => $user, 'cgu' => $latestCgu]);
|
||||||
|
if (!$cguUser) {
|
||||||
|
// Create a new CguUser relation if it doesn't exist
|
||||||
|
$cguUser = new CguUser();
|
||||||
|
$cguUser->setUsers($user);
|
||||||
|
$cguUser->setCgu($latestCgu);
|
||||||
|
$this->entityManager->persist($cguUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cguUser->setIsAccepted(true);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
|
}
|
12
symfony.lock
12
symfony.lock
|
@ -39,6 +39,18 @@
|
||||||
"config/routes/league_oauth2_server.yaml"
|
"config/routes/league_oauth2_server.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"nelmio/cors-bundle": {
|
||||||
|
"version": "2.5",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "1.5",
|
||||||
|
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/nelmio_cors.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"nyholm/psr7": {
|
"nyholm/psr7": {
|
||||||
"version": "1.8",
|
"version": "1.8",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|
|
@ -63,12 +63,13 @@
|
||||||
<i >{{ ux_icon('bi:menu-up', {height: '20px', width: '20px'}) }}</i>
|
<i >{{ ux_icon('bi:menu-up', {height: '20px', width: '20px'}) }}</i>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<select class="form-control">
|
<a href="http://api.solutions-easy.moi"> tyet </a>
|
||||||
|
{# <select class="form-control">
|
||||||
<option>Exploit</options>
|
<option>Exploit</options>
|
||||||
<option>Monithor</options>
|
<option>Monithor</options>
|
||||||
<option>Check</options>
|
<option>Check</options>
|
||||||
<option>Access</options>
|
<option>Access</options>
|
||||||
</select>
|
</select> #}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="dropdown-item" style="padding-left: 8px;" href="#">
|
<a class="dropdown-item" style="padding-left: 8px;" href="#">
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %} Consent {% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
{% if app.user %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h2>Data Usage Consent</h2>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Dear {{ app.user.userIdentifier }},</p>
|
||||||
|
<p>We request your consent to process your personal data. By agreeing, you acknowledge that:</p>
|
||||||
|
<ul>
|
||||||
|
<li>We will store and process your personal information securely</li>
|
||||||
|
<li>Your data will only be used for the purposes specified in our privacy policy</li>
|
||||||
|
<li>You can withdraw your consent at any time</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('app_consent', app.request.query.all) }}" class="mt-4">
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input type="checkbox" class="form-check-input" id="consent" name="consent" value="1" required>
|
||||||
|
<label class="form-check-label" for="consent">
|
||||||
|
I agree to the processing of my personal data
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit Consent</button>
|
||||||
|
<button type="submit" name="decline" value="1" class="btn btn-secondary" formnovalidate>Decline</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue