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_ENCRYPTION_KEY=f1b7c279f7992205a0df45e295d07066
|
||||
###< 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": {
|
||||
"turbo-core": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"fetch": "eager"
|
||||
},
|
||||
"mercure-turbo-stream": {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||
"doctrine/orm": "^3.3",
|
||||
"league/oauth2-server-bundle": "^0.11.0",
|
||||
"nelmio/cors-bundle": "^2.5",
|
||||
"phpdocumentor/reflection-docblock": "^5.6",
|
||||
"phpstan/phpdoc-parser": "^2.1",
|
||||
"symfony/asset": "7.2.*",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2848578c0a7baf9e3d125fcc61becab2",
|
||||
"content-hash": "685b46acb679219276b83724c15afb9d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
|
@ -2084,6 +2084,68 @@
|
|||
],
|
||||
"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",
|
||||
"version": "1.8.2",
|
||||
|
|
|
@ -16,4 +16,5 @@ return [
|
|||
Symfony\UX\TogglePassword\TogglePasswordBundle::class => ['all' => true],
|
||||
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
||||
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||
];
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIo7xCOI7GcgECAggA
|
||||
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECJMiC+tT9+rhBIIEyB04+Jb/N38i
|
||||
ZyG37vEEL7wX2l8VFZt5qCwEYMJj3WvdiiYZNRw1mOA1ZdlDVnwjNxDn/svzFdnv
|
||||
NXDTCeuKQOade02ySh6DFsI4IGWPhIq56kIGNolxMoqW1VlDaJbrW6qgeUcbHqlc
|
||||
V09MIMAE3fs5JeM7qR2b+6exiBrk2L4b/+IdJKlDJztkd6yIo/47VDcmk9a2l0AT
|
||||
wHip0FEvyjEC6UoKLCfDf07gRQp6c9YSYqoLDFnTdUqMEXk2ZRWwO7WnGGWpOCcA
|
||||
0Wurzdrdm0n/i80153tKKkJLFbzKOxZAmcV/pzRrI7iw5yDeDQ+WAU8N6rm8c151
|
||||
tg8ACUX3gg1otkwO6HZ8z6p7ki1UUmIn06wwTz1lTQDewy2Un6jzpKsVoZ/JJc7p
|
||||
XVXYGfym1+MRn7nU0gmepVw8o11813qVFAJePmxq+3RPILVZ8YSI0Qe3Pf19NNOS
|
||||
gfEyylKxHOnwxkDOVoU2c58pu921zwJFS+93sXh5uy83FpNqso10m1n/cr8XiMYc
|
||||
WX5qfgUoPgB9poC++9xCT6sISTZWLOLiIGuzBoNNi0kHX/1bco8mxRUk9TbjMuNi
|
||||
Zrx7KARwtY/ddfLD9DPxLYYWHh65zQCrtplY3ILbiXw4mUrJqPhpgw7tWsoDmF/X
|
||||
vQV/ZQQHjbM5UmCq1zCYq2meoeqV5e1ixyNpfe4xIgCAfwEw9UytQ+uQ5L/XAcGM
|
||||
AE3diuQMSw8UKMcslqKQtDGdQIuD5STRIjKp/L5/Ks5u50cjuvQ5xI6mLmwBB2G2
|
||||
0eMBqSNQFMqAqI1lDSHZSk1tNCqcWYbNaaqPSx4VMW99sWy+gNJK4vSGD99RRDWV
|
||||
VI9nmjB8/FsY81lDaHBFjq8VyLglu6eEzij3j5dDUFeedYb4OqnUZtIg2H+TSXnj
|
||||
mxwbImsucCUVHOrCc6JOvXZOnTCK4qum4pGpDxzp2xtYuPOlOVSsCwysXNcr77wD
|
||||
4i+3fSh0M3iB0dsrRwVqZ9ZLS2+5zgaLxoem6mR5Gg4OesK7Xf6mtgBrpD5mOAGp
|
||||
zTuj9wwQUajh3kRPhKzfzr2XqtsGiZsSjBUtOvV5PimhUdpPMYcRT7odcnxcJOhU
|
||||
Xde4/DGoxgJWmtei4BwMMLUexP94bGKA5w318PJAZ5qV2gY4MXhIgDn+HLEJ1tK7
|
||||
EBuuvGk+PRQElwVHTuOhGWvE7hyDA5Z2jnxGNtyntFWJfFddocTEyx6A/rPrbcBm
|
||||
DFINWQ6JZIY/xTLXVfF7fKx+fQpqe6R2gZrYNJ5G3Z4/nbyuRaq/bENoKbd+O51f
|
||||
LeRsyXLu5FbBFM4S61LZ/BseMHMxf3Q7l9gtp3EUrurIz36KZ2fPUVdqMsp2dvZ4
|
||||
z8aFGQrBcwKS3u9iwrf64w/LEsVIGhmxFuL8KMqG949wgd/CjnvDbzot6A3ioGSd
|
||||
kl62Z3rU1i0Y8T9ubdbuabpKGxpmRAHo0Y4nrnHZTLqvEeW3NCOMmOF6OjBg8Q+s
|
||||
pLbgCIjsr6LapdMzj2GiBL0no69uRO4Si+cFaMyMkowMbqoo+cB6z7jqbsTc++i3
|
||||
y+uJKGrXeqS9Fwj4QaK4NRzWo/wYRmvFyo0hjxeRmXRQR4DZ85zGn+9mNmzQa+uH
|
||||
bqPMXh92TaQXrWxDgzO9Ag==
|
||||
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIf5Dm9gHr5xICAggA
|
||||
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECAmIm56CqfdaBIIEyIeYoi/NL/yC
|
||||
EM3P0ZEesYY08FoEjaAonqDAFB2w8caOfSc2quZSij7zaDAjU5ehg7X/3kWqypUW
|
||||
F1bhMn5v3Lad6XOgXeTigYaLZBXshazuqnbqs9xBU00MPkwetl1CT4ATUzvEPPq1
|
||||
LOQ2FfEUfBZAR51PVYvKJwwctzYW42PNh8940XqskAs6kfWQ3qZPlnCRjQDfuKcI
|
||||
YhKeJz6cCYt5JHqBGC5mRiFkRqDe10gks0/yUCmds9MpKPLYKP9MrUs8QcxWpakf
|
||||
ypUMPM2mSik4U+D7Gdq7CqulqDPLm+8drKgUeq6wTgWwodpGnUe5FeG+cx4hxH5x
|
||||
fAKuP6gscvYk3p5ir138TVW4uEFehkBw7yHpqUqSjN5jIlmX634siYoNhWP6Ek32
|
||||
nl+gGUlITWSC6cewiqpL7IfQr+DDXpTPcN5Lu9+6rmh4P0XGE+J2a/tdXeK3xdMZ
|
||||
3MyFItGNIxklL+yuvRBJ2o4D5JRDhiSwmXdFR5WOJYN0SRKYkEnmjAAHnhK6eWLP
|
||||
bsD0tdUilZvyGmcPMXkM3HjfAQKqDldi8rmdqe8IJHpCKZ5twryy/eb1EArofN0U
|
||||
H9XIs/pdJldg2HTJVIen5iAONqMSB4LyxMUfXWnvg/qPazvgYZvKbTXyp0iVhJ77
|
||||
UcWyIO6xx/3BvnuO/eCgelu+eNQqI9UOMhVr7X0gGpXb88g+Gu8scrlWDVTM6o9P
|
||||
j1rOuAYaZX/69jGOmH3QqUkILcwkfevSuNUBgLOilh/mabR+tOfM2o6/8Z1Zd1PS
|
||||
nplYzvDs/Pib71PEF64DVCRlG2QioqV2MT7gOenShyZhZS4+oxJ3nWf9Z9n5jwDJ
|
||||
XSqkHRD8CDZZS0f1FOET1c+GZtoyfxGHdGyLM8shA6IRnxBoAKWVW8vdTfyPJFcy
|
||||
vq9gPvlrfyuREcRKeSnySzkAQbYLVH631tLcsbsd5yfyYS0o6BY1mNDi1j83Xfmp
|
||||
Or2a137ZKKTfRtSJybj+QOiBXMR8uJnR9HPjRQn8DrMYzcgv/kw48AIUy2+4sZIw
|
||||
cPxrtKFyhWIYSnHil4Ri/cENZApbbo1UQc4ktyjuyxyI20gx21vfaHWbx6GpUNwG
|
||||
H551tioa5P4cte4syOxMi89KRdCYKGgUTibhEfMODD6rA/l3W9PGINqOGu3Z2SxN
|
||||
4HGV4oJx6Nvw1vTq2phSVo3Qp9JyvO3FFE8oA/b+ElL0PoYjsLsNiKBxggPvZgKp
|
||||
nTOFYYUMQl1+6CUf2iSR0b8VLhRM5o9N+Xwp2D2SEjWZvMhIKQxxmwVd3aSn8ebQ
|
||||
CSW0lGFjltwc5FEvw1aNmhx7K1gSCMcBnSEx53ghLU5xCA+50g5brM1joZn3sQ2c
|
||||
WsY/dLNtfqlTnKSeVQKyLEZj6Xl9/KYyVg3yYt43HuSz3FAt6ekGloA9rVR3v7tZ
|
||||
m6tBWyOqQG4tnp8+V/2CMGnpq2VQABqxwZxpm5u+tbfSlonwrXpiy3/fgsYZJE02
|
||||
kVwNb4FPClKjyVZGRrWfITaS4Gxm+hDdfKVipunIzS+7MADwwYyibPVXyuCw1NTg
|
||||
pt89WCTt0csNNGcpd1m/rrH5J080fEHGXWpeobbTbeiYsK0+qeYQXEeMvE7lyFIP
|
||||
SzDJqo7z76xQc3dcBX8MNQ==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwQueIPrQEJyma0oiV2wG
|
||||
9gl4mpjZQx0QDj/HXyi2hqTjd6z9cfcONmlggD7xoLuiNNmTuVNezHMMC4VNq8/G
|
||||
zNQu7Gp18K0uw0WXWWpHtslE3yz9c30FPB4whpz+NMlSXiQEaA2xJxIPxgaMrCG2
|
||||
vc8hMPqiN5pid9ErdkGJLaZd9Q/HqIvVPmw9pVK6HTogfHu61hiaHtA5wDxetFH2
|
||||
l7V0oXcbES7fpTXetlNNpIcQ5j5G04HCPWNl8abCcKNUMoDjAXcvKnXNTBaDSfSZ
|
||||
+JxMjjtVpU8r7sEDmQRlh4CeRqYfimNusm8WO3Yod+PLO33doUhEwBMJOu1s3+oG
|
||||
rQIDAQAB
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwZehUUiAV9vMER+tCpKB
|
||||
lLr53bCV2z34r7Qg4gojciR1n7J6esptuLo/JMIYsU1EFmH8dIJ9guc/IufW7hDj
|
||||
iwB2l75brVJ+EG8RRN2R4/IlUXz01a56W+TgLGyVM9iNegfwrNQjEEr3WNnz878h
|
||||
0KQDsG3+kndh60pIuIiiEqG/yoO9C4enyL5XEDcsyKebb5PeCSTewgGm+DY7vj1F
|
||||
FSZMXxEnOQP/Symz8st1KS7DbbYma/OCkrAsZ+iHC1Ozis0D28uxcdyrPZi36bkV
|
||||
MKNST42uV86CTEan2yaHaynRUFNHC+bZjA+izOALtg3CyguOqrmL1LL5ID8Q1K8f
|
||||
jQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
|
|
@ -3,6 +3,10 @@ league_oauth2_server:
|
|||
private_key: '%env(resolve:OAUTH_PRIVATE_KEY)%'
|
||||
private_key_passphrase: '%env(resolve:OAUTH_PASSPHRASE)%'
|
||||
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:
|
||||
public_key: '%env(resolve:OAUTH_PUBLIC_KEY)%'
|
||||
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: ^/token, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/oauth2/token, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/token, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED }
|
||||
- { path: ^/oauth2/userinfo, roles: IS_AUTHENTICATED_FULLY }
|
||||
- { path: ^/, roles: ROLE_USER }
|
||||
|
|
|
@ -19,6 +19,9 @@ services:
|
|||
- '../src/DependencyInjection/'
|
||||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
App\EventSubscriber\:
|
||||
resource: '../src/EventSubscriber/'
|
||||
tags: ['kernel.event_subscriber']
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Controller;
|
|||
use App\Entity\User;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class OAuth2Controller extends AbstractController
|
||||
|
@ -17,7 +18,7 @@ class OAuth2Controller extends AbstractController
|
|||
return new JsonResponse([
|
||||
'message' => 'Authentification réussie !',
|
||||
'email' => $user->getEmail(),
|
||||
'name' => $user->getUsername(),
|
||||
'name' => $user->getName()
|
||||
]);
|
||||
|
||||
}
|
||||
|
@ -33,7 +34,7 @@ class OAuth2Controller extends AbstractController
|
|||
|
||||
return new JsonResponse([
|
||||
'sub' => $user->getId(),
|
||||
'username' => $user->getUsername(),
|
||||
'username' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
'roles' => $user->getRoles(),
|
||||
]);
|
||||
|
|
|
@ -6,9 +6,18 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use App\Service\CguUserService;
|
||||
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
private CguUserService $cguUserService;
|
||||
|
||||
public function __construct(CguUserService $cguUserService)
|
||||
{
|
||||
$this->cguUserService = $cguUserService;
|
||||
}
|
||||
|
||||
#[Route(path: '/login', name: 'app_login')]
|
||||
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.');
|
||||
}
|
||||
|
||||
#[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\Http\FirewallMapInterface;
|
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
use App\Service\CguUserService;
|
||||
|
||||
class AuthorizationCodeSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
|
@ -20,35 +21,42 @@ class AuthorizationCodeSubscriber implements EventSubscriberInterface
|
|||
private UrlGeneratorInterface $urlGenerator;
|
||||
private RequestStack $requestStack;
|
||||
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->urlGenerator = $urlGenerator;
|
||||
$this->requestStack = $requestStack;
|
||||
$this->firewallName = $firewallMap->getFirewallConfig($requestStack->getCurrentRequest())->getName();
|
||||
$this->cguUserService = $cguUserService;
|
||||
}
|
||||
|
||||
public function onLeagueOauth2ServerEventAuthorizationRequestResolve(AuthorizationRequestResolveEvent $event): void
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
$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);
|
||||
if ($user instanceof UserInterface) {
|
||||
//On approuve le consentement automatiquement
|
||||
$event->resolveAuthorization(true);
|
||||
$request->getSession()->remove('consent_granted');
|
||||
|
||||
if (!$user instanceof UserInterface) {
|
||||
// Save the target path and redirect to login
|
||||
$this->saveTargetPath($request->getSession(), $this->firewallName, $request->getUri());
|
||||
$event->setResponse($response);
|
||||
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
|
||||
|
|
|
@ -16,6 +16,15 @@ class CguRepository extends ServiceEntityRepository
|
|||
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
|
||||
// */
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
},
|
||||
"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": {
|
||||
"version": "1.8",
|
||||
"recipe": {
|
||||
|
|
|
@ -63,12 +63,13 @@
|
|||
<i >{{ ux_icon('bi:menu-up', {height: '20px', width: '20px'}) }}</i>
|
||||
</div>
|
||||
<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>Monithor</options>
|
||||
<option>Check</options>
|
||||
<option>Access</options>
|
||||
</select>
|
||||
</select> #}
|
||||
</div>
|
||||
</div>
|
||||
<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