temp client
This commit is contained in:
parent
d9ce5bbb3f
commit
485b7b7712
|
@ -11,6 +11,7 @@
|
|||
"doctrine/doctrine-bundle": "^2.13",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||
"doctrine/orm": "^3.3",
|
||||
"knpuniversity/oauth2-client-bundle": "^2.18",
|
||||
"phpdocumentor/reflection-docblock": "^5.6",
|
||||
"phpstan/phpdoc-parser": "^2.1",
|
||||
"symfony/asset": "7.2.*",
|
||||
|
@ -56,7 +57,8 @@
|
|||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
"App\\": "src/",
|
||||
"Sudalys\\OAuth2\\Client\\": "libs/sudalys/oauth2-client/src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
|
|
@ -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": "eaa614d3c00b5654c4cc228dcad4b8c8",
|
||||
"content-hash": "a2e7a54b8d79cc97db7598ed43170f6c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
|
@ -1368,6 +1368,455 @@
|
|||
],
|
||||
"time": "2025-03-06T22:45:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.9.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||
"guzzlehttp/psr7": "^2.7.0",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"ext-curl": "*",
|
||||
"guzzle/client-integration-tests": "3.0.2",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Required for CURL handler support",
|
||||
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Lindblom",
|
||||
"email": "jeremeamia@gmail.com",
|
||||
"homepage": "https://github.com/jeremeamia"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"psr-18",
|
||||
"psr-7",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-24T11:22:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-17T10:06:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"ralouphie/getallheaders": "^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"http-interop/http-factory-tests": "0.9.0",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://sagikazarmark.hu"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-18T11:15:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "knpuniversity/oauth2-client-bundle",
|
||||
"version": "v2.18.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/knpuniversity/oauth2-client-bundle.git",
|
||||
"reference": "c38ca88a70aae3694ca346a41b13b9a8f6e33ed4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/knpuniversity/oauth2-client-bundle/zipball/c38ca88a70aae3694ca346a41b13b9a8f6e33ed4",
|
||||
"reference": "c38ca88a70aae3694ca346a41b13b9a8f6e33ed4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/oauth2-client": "^2.0",
|
||||
"php": ">=8.1",
|
||||
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
|
||||
"symfony/framework-bundle": "^5.4|^6.0|^7.0",
|
||||
"symfony/http-foundation": "^5.4|^6.0|^7.0",
|
||||
"symfony/routing": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"league/oauth2-facebook": "^1.1|^2.0",
|
||||
"symfony/phpunit-bridge": "^5.4|^6.0|^7.0",
|
||||
"symfony/security-guard": "^5.4",
|
||||
"symfony/yaml": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/security-guard": "For integration with Symfony's Guard Security layer"
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"KnpU\\OAuth2ClientBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ryan Weaver",
|
||||
"email": "ryan@symfonycasts.com"
|
||||
}
|
||||
],
|
||||
"description": "Integration with league/oauth2-client to provide services",
|
||||
"homepage": "https://symfonycasts.com",
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/knpuniversity/oauth2-client-bundle/issues",
|
||||
"source": "https://github.com/knpuniversity/oauth2-client-bundle/tree/v2.18.3"
|
||||
},
|
||||
"time": "2024-10-02T14:26:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-client",
|
||||
"version": "2.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-client.git",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
|
||||
"php": "^7.1 || >=8.0.0 <8.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.5",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.4",
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "^3.11"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"homepage": "https://github.com/shadowhand",
|
||||
"role": "Contributor"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 2.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"single sign on"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth2-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
|
||||
},
|
||||
"time": "2025-02-26T04:37:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.8.1",
|
||||
|
@ -1893,6 +2342,166 @@
|
|||
},
|
||||
"time": "2019-01-08T18:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/link",
|
||||
"version": "2.0.1",
|
||||
|
@ -1999,6 +2608,50 @@
|
|||
},
|
||||
"time": "2024-09-11T13:17:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^5 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"support": {
|
||||
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/asset",
|
||||
"version": "v7.2.0",
|
||||
|
|
|
@ -13,4 +13,5 @@ return [
|
|||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
knpu_oauth2_client:
|
||||
clients:
|
||||
sudalys:
|
||||
type: generic
|
||||
provider_class: Sudalys\OAuth2\Client\Provider\Sudalys
|
||||
client_id: '%env(SSO_CLIENT_ID)%'
|
||||
client_secret: '%env(SSO_CLIENT_SECRET)%'
|
||||
redirect_route: app_home
|
||||
redirect_params: {}
|
||||
provider_options:
|
||||
domain: 'https://portail.solutions-easy.moi'
|
||||
use_state: false
|
|
@ -4,24 +4,34 @@ security:
|
|||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||
providers:
|
||||
users_in_memory: { memory: null }
|
||||
sudalys:
|
||||
entity:
|
||||
class: App\Entity\User
|
||||
property: email
|
||||
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: users_in_memory
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
provider: sudalys
|
||||
custom_authenticators:
|
||||
- App\Security\OAuthAuthenticator
|
||||
entry_point: App\Security\OAuthAuthenticator
|
||||
logout:
|
||||
path: app_logout
|
||||
target: app_home
|
||||
invalidate_session: true
|
||||
clear_site_data:
|
||||
- cookies
|
||||
- storage
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
- { path: ^/dashboard, roles: ROLE_USER }
|
||||
- { path: ^/user-info, roles: ROLE_USER }
|
||||
- { path: ^/connect, roles: PUBLIC_ACCESS }
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
|
||||
|
|
|
@ -20,5 +20,12 @@ services:
|
|||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
|
||||
# Register our custom OAuth user provider
|
||||
knpu.oauth2.client.user_provider:
|
||||
class: App\Security\OAuthUserProvider
|
||||
arguments:
|
||||
- '@doctrine.orm.entity_manager'
|
||||
- '@knpu.oauth2.registry'
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/build
|
||||
/log
|
||||
/vendor
|
||||
.*.cache
|
||||
composer.phar
|
||||
composer.lock
|
||||
infection.json
|
||||
infection.phar*
|
||||
phpunit.xml
|
|
@ -0,0 +1,78 @@
|
|||
# Changelog
|
||||
All notable changes to `oauth2-gitlab` will be documented in this file
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
Nothing yet.
|
||||
|
||||
## [3.4.0] - 2021-02-08
|
||||
### Added
|
||||
- Compatibility with php-gitlab-api v10
|
||||
- Test suite compatible with PHP8
|
||||
|
||||
## [3.3.0] - 2020-02-10
|
||||
### Added
|
||||
- Compatibility with php-gitlab-api v10
|
||||
|
||||
## [3.2.0] - 2020-02-10
|
||||
### Changed
|
||||
- Updated dependencies to those requiring up to date PHP versions
|
||||
|
||||
### Removed
|
||||
- Support for outdated and unsupported PHP versions (<7.2)
|
||||
|
||||
## [3.1.2] - 2018-11-23
|
||||
### Changed
|
||||
- Added conflict with `oauth2-client:2.4.0` due to [breaking change upstream](https://github.com/thephpleague/oauth2-client/issues/752) (#6)
|
||||
|
||||
## [3.1.1] - 2018-10-01
|
||||
### Added
|
||||
- PHP 7.2 and nightly added to test suite
|
||||
- Infection testing added
|
||||
|
||||
### Changed
|
||||
- Test suite upgraded to PHPUnit 5/7 hybrid
|
||||
|
||||
## [3.1.0] - 2017-11-01
|
||||
### Added
|
||||
- Access scope support was implemented
|
||||
|
||||
## [3.0.0] - 2017-05-31
|
||||
### Changed
|
||||
- **Breaking**: Upgrade Gitlab API from v3 to v4
|
||||
- Test suite upgraded from PHPUnit 4 to 5/6 hybrid
|
||||
|
||||
## [2.0.0] - 2017-02-03
|
||||
### Added
|
||||
- PHP 7.1 is now officially supported and tested
|
||||
|
||||
### Changed
|
||||
- **Breaking**: Upgrade league/oauth2-client to major version 2
|
||||
- Included PHP-CS-Fixer
|
||||
|
||||
### Removed
|
||||
- PHP 5.5 is end of life and no longer supported
|
||||
|
||||
## [1.1.0] - 2016-08-28
|
||||
### Added
|
||||
- Added `getApiClient` method on `GitlabResourceOwner` to get an API connector
|
||||
|
||||
## [1.0.0] - 2016-05-20
|
||||
### Changed
|
||||
- Cleaned up everything after definitive testing for stable release
|
||||
|
||||
## 1.0.0-alpha-1 - 2016-05-16
|
||||
### Added
|
||||
- Original fork, feature complete
|
||||
|
||||
[Unreleased]: https://github.com/omines/oauth2-gitlab/compare/3.4.0...master
|
||||
[3.4.0]: https://github.com/omines/oauth2-gitlab/compare/3.3.0...3.2.0
|
||||
[3.3.0]: https://github.com/omines/oauth2-gitlab/compare/3.2.0...3.3.0
|
||||
[3.2.0]: https://github.com/omines/oauth2-gitlab/compare/3.1.2...3.2.0
|
||||
[3.1.2]: https://github.com/omines/oauth2-gitlab/compare/3.1.1...3.1.2
|
||||
[3.1.1]: https://github.com/omines/oauth2-gitlab/compare/3.1.0...3.1.1
|
||||
[3.1.0]: https://github.com/omines/oauth2-gitlab/compare/3.0.0...3.1.0
|
||||
[3.0.0]: https://github.com/omines/oauth2-gitlab/compare/2.0.0...3.0.0
|
||||
[2.0.0]: https://github.com/omines/oauth2-gitlab/compare/1.1.0...2.0.0
|
||||
[1.1.0]: https://github.com/omines/oauth2-gitlab/compare/1.0.0...1.1.0
|
||||
[1.0.0]: https://github.com/omines/oauth2-gitlab/compare/1.0.0-alpha.1...1.0.0
|
|
@ -0,0 +1,7 @@
|
|||
# Contributing
|
||||
|
||||
Contributions are **welcome** and will be fully **credited**.
|
||||
|
||||
We accept contributions via Pull Requests on [Github](https://github.com/omines/oauth2-gitlab). Follow
|
||||
[good standards](http://www.phptherightway.com/), keep code coverage at 100%, and run `vendor/bin/php-cs-fixer fix`
|
||||
before committing.
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Omines Internetbureau B.V. / Steven Maguire
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,121 @@
|
|||
# GitLab Provider for OAuth 2.0 Client
|
||||
[](https://github.com/omines/oauth2-gitlab/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/omines/oauth2-gitlab)
|
||||
[](https://scrutinizer-ci.com/g/omines/oauth2-gitlab/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/omines/oauth2-gitlab)
|
||||
[](https://packagist.org/packages/omines/oauth2-gitlab)
|
||||
|
||||
This package provides GitLab OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client). GitLab 8.17 or later is required.
|
||||
|
||||
## Installation
|
||||
|
||||
To install, use composer:
|
||||
|
||||
```
|
||||
composer require omines/oauth2-gitlab
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Usage is similar to the basic OAuth client, using `\Omines\OAuth2\Client\Provider\Gitlab` as the provider.
|
||||
|
||||
### Authorization Code Flow
|
||||
|
||||
```php
|
||||
$provider = new \Omines\OAuth2\Client\Provider\Sudalys([
|
||||
'clientId' => '{gitlab-client-id}',
|
||||
'clientSecret' => '{gitlab-client-secret}',
|
||||
'redirectUri' => 'https://example.com/callback-url',
|
||||
'domain' => 'https://my.gitlab.example', // Optional base URL for self-hosted
|
||||
]);
|
||||
|
||||
if (!isset($_GET['code'])) {
|
||||
|
||||
// If we don't have an authorization code then get one
|
||||
$authUrl = $provider->getAuthorizationUrl();
|
||||
$_SESSION['oauth2state'] = $provider->getState();
|
||||
header('Location: '.$authUrl);
|
||||
exit;
|
||||
|
||||
// Check given state against previously stored one to mitigate CSRF attack
|
||||
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
|
||||
|
||||
unset($_SESSION['oauth2state']);
|
||||
exit('Invalid state');
|
||||
|
||||
} else {
|
||||
|
||||
// Try to get an access token (using the authorization code grant)
|
||||
$token = $provider->getAccessToken('authorization_code', [
|
||||
'code' => $_GET['code'],
|
||||
]);
|
||||
|
||||
// Optional: Now you have a token you can look up a users profile data
|
||||
try {
|
||||
|
||||
// We got an access token, let's now get the user's details
|
||||
$user = $provider->getResourceOwner($token);
|
||||
|
||||
// Use these details to create a new profile
|
||||
printf('Hello %s!', $user->getName());
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
// Failed to get user details
|
||||
exit('Oh dear...');
|
||||
}
|
||||
|
||||
// Use this to interact with an API on the users behalf
|
||||
echo $token->getToken();
|
||||
}
|
||||
```
|
||||
|
||||
### Managing Scopes
|
||||
|
||||
When creating your GitLab authorization URL, you can specify the state and scopes your application may authorize.
|
||||
|
||||
```php
|
||||
$options = [
|
||||
'state' => 'OPTIONAL_CUSTOM_CONFIGURED_STATE',
|
||||
'scope' => ['read_user','openid'] // array or string
|
||||
];
|
||||
|
||||
$authorizationUrl = $provider->getAuthorizationUrl($options);
|
||||
```
|
||||
If neither are defined, the provider will utilize internal defaults ```'api'```.
|
||||
|
||||
|
||||
### Performing API calls
|
||||
|
||||
Install [`m4tthumphrey/php-gitlab-api`](https://packagist.org/packages/m4tthumphrey/php-gitlab-api) to interact with the
|
||||
Gitlab API after authentication. Either connect manually:
|
||||
|
||||
```php
|
||||
$client = new \Gitlab\Client();
|
||||
$client->setUrl('https://my.gitlab.url/api/v4/');
|
||||
$client->authenticate($token->getToken(), \Gitlab\Client::AUTH_OAUTH_TOKEN);
|
||||
```
|
||||
Or call the `getApiClient` method on `GitlabResourceOwner` which does the same implicitly.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
$ ./vendor/bin/phpunit
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](https://github.com/omines/oauth2-gitlab/blob/master/CONTRIBUTING.md) for details.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
This code is a modified fork from the [official Github provider](https://github.com/thephpleague/oauth2-github) adapted
|
||||
for Gitlab use, so many credits go to [Steven Maguire](https://github.com/stevenmaguire).
|
||||
|
||||
## Legal
|
||||
|
||||
This software was developed for internal use at [Omines Full Service Internetbureau](https://www.omines.nl/)
|
||||
in Eindhoven, the Netherlands. It is shared with the general public under the permissive MIT license, without
|
||||
any guarantee of fitness for any particular purpose. Refer to the included `LICENSE` file for more details.
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "sudalys/oauth2-sudalys",
|
||||
"description": "GitLab OAuth 2.0 Client Provider for The PHP League OAuth2-Client",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "JB LOPEZ",
|
||||
"email": "contact@jblopez.fr",
|
||||
"homepage": "https://www.jblopez.fr"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"client",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"gitlab"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"league/oauth2-client": "^2.4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.0",
|
||||
"guzzlehttp/psr7": "^1.6",
|
||||
"http-interop/http-factory-guzzle": "^1.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"m4tthumphrey/php-gitlab-api": "^10.0|^11.0",
|
||||
"php-http/guzzle7-adapter": "^0.1",
|
||||
"phpunit/phpunit": "^8.0|^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sudalys\\OAuth2Sudalys\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Sudalys OAuth2 Provider
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Sudalys\OAuth2\Client\Provider\Exception;
|
||||
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* GitlabIdentityProviderException.
|
||||
*
|
||||
* @author Niels Keurentjes <niels.keurentjes@omines.com>
|
||||
*/
|
||||
class SudalysIdentityProviderException extends IdentityProviderException
|
||||
{
|
||||
/**
|
||||
* Creates client exception from response.
|
||||
*
|
||||
* @param mixed $data Parsed response data
|
||||
*/
|
||||
public static function clientException(ResponseInterface $response, $data): IdentityProviderException
|
||||
{
|
||||
return static::fromResponse(
|
||||
$response,
|
||||
isset($data['message']) ? $data['message'] : $response->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates oauth exception from response.
|
||||
*
|
||||
* @param ResponseInterface $response Response received from upstream
|
||||
* @param array $data Parsed response data
|
||||
*/
|
||||
public static function oauthException(ResponseInterface $response, $data): IdentityProviderException
|
||||
{
|
||||
return static::fromResponse(
|
||||
$response,
|
||||
isset($data['error']) ? $data['error'] : $response->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates identity exception from response.
|
||||
*
|
||||
* @param ResponseInterface $response Response received from upstream
|
||||
* @param string|null $message Parsed message
|
||||
*/
|
||||
protected static function fromResponse(ResponseInterface $response, $message = null): IdentityProviderException
|
||||
{
|
||||
return new static($message, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Sudalys OAuth2 Provider
|
||||
*/
|
||||
|
||||
namespace Sudalys\OAuth2\Client\Provider;
|
||||
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
use http\Exception\UnexpectedValueException;
|
||||
use League\OAuth2\Client\Provider\AbstractProvider;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Sudalys\OAuth2\Client\Provider\SudalysResourceOwner;
|
||||
use Sudalys\OAuth2\Client\Provider\Exception\SudalysIdentityProviderException;
|
||||
|
||||
class Sudalys extends AbstractProvider
|
||||
{
|
||||
use BearerAuthorizationTrait;
|
||||
|
||||
const PATH_API_USER = '/oauth/api/user';
|
||||
const PATH_AUTHORIZE = '/authorize';
|
||||
const PATH_TOKEN = '/token';
|
||||
const DEFAULT_SCOPE = 'email';
|
||||
const SCOPE_SEPARATOR = ' ';
|
||||
|
||||
/** @var string */
|
||||
public $domain = 'http://localhost:8000';
|
||||
|
||||
/**
|
||||
* Sudalys constructor.
|
||||
*/
|
||||
public function __construct(array $options, array $collaborators = [])
|
||||
{
|
||||
if (isset($options['domain'])) {
|
||||
$this->domain = $options['domain'];
|
||||
}
|
||||
parent::__construct($options, $collaborators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authorization url to begin OAuth flow.
|
||||
*/
|
||||
public function getBaseAuthorizationUrl(): string
|
||||
{
|
||||
return $this->domain . self::PATH_AUTHORIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests resource owner details.
|
||||
*
|
||||
* @param AccessToken $token
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchResourceOwnerDetails(AccessToken $token)
|
||||
{
|
||||
$url = $this->getResourceOwnerDetailsUrl($token);
|
||||
$request = $this->getAuthenticatedRequest(self::METHOD_GET, $url, $token);
|
||||
$response = $this->getParsedResponse($request);
|
||||
if (false === is_array($response)) {
|
||||
throw new UnexpectedValueException(
|
||||
'Invalid response received from Authorization Server. Expected JSON.'
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request and returns the parsed response.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @throws IdentityProviderException
|
||||
* @return mixed
|
||||
*/
|
||||
/* public function getParsedResponse(RequestInterface $request)
|
||||
{
|
||||
try {
|
||||
$response = $this->getResponse($request);
|
||||
} catch (BadResponseException $e) {
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
|
||||
$parsed = $this->parseResponse($response);
|
||||
|
||||
echo "<br>----------------------------- PARSED RESPONSE -------------------------<br>";
|
||||
print_r($parsed);
|
||||
|
||||
$this->checkResponse($response, $parsed);
|
||||
|
||||
return $parsed;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Get access token url to retrieve token.
|
||||
*/
|
||||
public function getBaseAccessTokenUrl(array $params): string
|
||||
{
|
||||
return $this->domain . self::PATH_TOKEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get provider url to fetch user details.
|
||||
*/
|
||||
public function getResourceOwnerDetailsUrl(AccessToken $token): string
|
||||
{
|
||||
//DEBUG echo '<br> '.$this->domain . self::PATH_API_USER;
|
||||
return $this->domain . self::PATH_API_USER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default scopes used by GitLab.
|
||||
* Current scopes are 'api', 'read_user', 'openid'.
|
||||
*
|
||||
* This returns an array with 'api' scope as default.
|
||||
*/
|
||||
protected function getDefaultScopes(): array
|
||||
{
|
||||
return [self::DEFAULT_SCOPE];
|
||||
}
|
||||
|
||||
/**
|
||||
* GitLab uses a space to separate scopes.
|
||||
*/
|
||||
protected function getScopeSeparator(): string
|
||||
{
|
||||
return self::SCOPE_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a provider response for errors.
|
||||
*
|
||||
* @param ResponseInterface $response Parsed response data
|
||||
* @throws IdentityProviderException
|
||||
*/
|
||||
protected function checkResponse(ResponseInterface $response, $data)
|
||||
{
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
throw SudalysIdentityProviderException::clientException($response, $data);
|
||||
} elseif (isset($data['error'])) {
|
||||
throw SudalysIdentityProviderException::oauthException($response, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a user object from a successful user details request.
|
||||
*/
|
||||
protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
|
||||
{
|
||||
$user = new SudalysResourceOwner($response, $token);
|
||||
|
||||
return $user->setDomain($this->domain);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Sudalys OAuth2 Provider
|
||||
*/
|
||||
|
||||
namespace Sudalys\OAuth2\Client\Provider;
|
||||
|
||||
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
|
||||
class SudalysResourceOwner implements ResourceOwnerInterface
|
||||
{
|
||||
const PATH_API = '/api/v4/';
|
||||
|
||||
/** @var array */
|
||||
private $data;
|
||||
|
||||
/** @var string */
|
||||
private $domain;
|
||||
|
||||
/** @var AccessToken */
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Creates new resource owner.
|
||||
*/
|
||||
public function __construct(array $response, AccessToken $token)
|
||||
{
|
||||
$this->data = $response;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the authorized resource owner.
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return (int) $this->get('id');
|
||||
}
|
||||
|
||||
|
||||
public function getDomain(): string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves ful token resource owner.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIdToken()
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setDomain(string $domain): self
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full name of the owner.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->get('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Username of the owner.
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->get('username');
|
||||
}
|
||||
|
||||
/**
|
||||
* Email address of the owner.
|
||||
*/
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->get('email');
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's avatar.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAvatarUrl(): string
|
||||
{
|
||||
return $this->get('avatar_url');
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's profile page.
|
||||
*/
|
||||
public function getProfileUrl(): string
|
||||
{
|
||||
return $this->get('web_url');
|
||||
}
|
||||
|
||||
public function getToken(): AccessToken
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is active.
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
return 'active' === $this->get('state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is an admin.
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return (bool) $this->get('is_admin', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is external.
|
||||
*/
|
||||
public function isExternal(): bool
|
||||
{
|
||||
return (bool) $this->get('external', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the owner details available as an array.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $default
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function get(string $key, $default = null)
|
||||
{
|
||||
return isset($this->data[$key]) ? $this->data[$key] : $default;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250326093406 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE "user" (id SERIAL NOT NULL, username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, roles JSON NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649F85E0677 ON "user" (username)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649E7927C74 ON "user" (email)');
|
||||
$this->addSql('CREATE TABLE messenger_messages (id BIGSERIAL NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)');
|
||||
$this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)');
|
||||
$this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)');
|
||||
$this->addSql('COMMENT ON COLUMN messenger_messages.created_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN messenger_messages.available_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN messenger_messages.delivered_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('CREATE OR REPLACE FUNCTION notify_messenger_messages() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify(\'messenger_messages\', NEW.queue_name::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;');
|
||||
$this->addSql('DROP TRIGGER IF EXISTS notify_trigger ON messenger_messages;');
|
||||
$this->addSql('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON messenger_messages FOR EACH ROW EXECUTE PROCEDURE notify_messenger_messages();');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP TABLE "user"');
|
||||
$this->addSql('DROP TABLE messenger_messages');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
class ClientTestController extends AbstractController
|
||||
{
|
||||
#[Route('/client/test', name: 'app_client_test')]
|
||||
public function index(): Response
|
||||
{
|
||||
// Get OAuth configuration
|
||||
$clientId = $_ENV['SSO_CLIENT_ID'] ?? 'not set';
|
||||
$clientSecret = $_ENV['SSO_CLIENT_SECRET'] ?? 'not set';
|
||||
$authorizeUrl = $_ENV['SSO_AUTHORIZE_URL'] ?? 'not set';
|
||||
$tokenUrl = $_ENV['SSO_TOKEN_URL'] ?? 'not set';
|
||||
$userInfoUrl = $_ENV['SSO_USERINFO_URL'] ?? 'not set';
|
||||
|
||||
// Test 1: Check if URLs are accessible
|
||||
$urlTests = [];
|
||||
$client = HttpClient::create(['verify_peer' => false, 'verify_host' => false]);
|
||||
|
||||
foreach ([$authorizeUrl, $tokenUrl, $userInfoUrl] as $url) {
|
||||
try {
|
||||
$response = $client->request('GET', $url, ['timeout' => 3]);
|
||||
$status = $response->getStatusCode();
|
||||
$urlTests[$url] = "Status: $status";
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$urlTests[$url] = "Error: " . $e->getMessage();
|
||||
} catch (\Exception $e) {
|
||||
$urlTests[$url] = "Exception: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2: Try to get a token with client credentials
|
||||
$tokenTest = "Not tested";
|
||||
try {
|
||||
$guzzleClient = new Client([
|
||||
'verify' => false,
|
||||
'timeout' => 5,
|
||||
]);
|
||||
|
||||
$response = $guzzleClient->post($tokenUrl, [
|
||||
'form_params' => [
|
||||
'grant_type' => 'client_credentials',
|
||||
'client_id' => $clientId,
|
||||
'client_secret' => $clientSecret,
|
||||
],
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
$tokenTest = $response->getBody()->getContents();
|
||||
} catch (RequestException $e) {
|
||||
$response = $e->getResponse();
|
||||
if ($response) {
|
||||
$tokenTest = "Error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents();
|
||||
} else {
|
||||
$tokenTest = "Error: " . $e->getMessage();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$tokenTest = "Exception: " . $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->render('client_test/index.html.twig', [
|
||||
'client_id' => $clientId,
|
||||
'client_secret' => substr($clientSecret, 0, 3) . '***',
|
||||
'authorize_url' => $authorizeUrl,
|
||||
'token_url' => $tokenUrl,
|
||||
'userinfo_url' => $userInfoUrl,
|
||||
'url_tests' => $urlTests,
|
||||
'token_test' => $tokenTest,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
final class DashboardController extends AbstractController
|
||||
{
|
||||
#[Route('/', name: 'app_home')]
|
||||
public function home(): Response
|
||||
{
|
||||
return $this->render('dashboard/home.html.twig', [
|
||||
'controller_name' => 'DashboardController',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/dashboard', name: 'app_dashboard')]
|
||||
public function index(): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
|
||||
return $this->render('dashboard/index.html.twig', [
|
||||
'controller_name' => 'DashboardController',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/user-info', name: 'app_user_info')]
|
||||
public function userInfo(LoggerInterface $logger): Response
|
||||
{
|
||||
if (!$this->getUser()) {
|
||||
throw new AccessDeniedException('You must be logged in to view this page');
|
||||
}
|
||||
|
||||
$user = $this->getUser();
|
||||
$logger->info('User info accessed', [
|
||||
'user' => $user->getUserIdentifier()
|
||||
]);
|
||||
|
||||
return $this->render('dashboard/user_info.html.twig', [
|
||||
'user' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/connect/sso', name: 'connect_sso_start')]
|
||||
public function connectAction(ClientRegistry $clientRegistry, LoggerInterface $logger): Response
|
||||
{
|
||||
try {
|
||||
$client = $clientRegistry->getClient('sudalys');
|
||||
|
||||
$logger->info('Redirecting to OAuth authorization endpoint');
|
||||
|
||||
// Don't pass any options - use defaults configured in knpu_oauth2_client.yaml
|
||||
return $client->redirect();
|
||||
} catch (\Exception $e) {
|
||||
$logger->error('OAuth connection error: ' . $e->getMessage(), [
|
||||
'exception' => get_class($e),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
]);
|
||||
|
||||
return new JsonResponse([
|
||||
'error' => 'OAuth connection failed',
|
||||
'message' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'environment' => [
|
||||
'SSO_CLIENT_ID' => $_ENV['SSO_CLIENT_ID'] ?? 'not set',
|
||||
'SSO_CLIENT_SECRET' => substr($_ENV['SSO_CLIENT_SECRET'] ?? 'not set', 0, 3) . '***',
|
||||
'SSO_AUTHORIZE_URL' => $_ENV['SSO_AUTHORIZE_URL'] ?? 'not set',
|
||||
'SSO_TOKEN_URL' => $_ENV['SSO_TOKEN_URL'] ?? 'not set',
|
||||
'SSO_USERINFO_URL' => $_ENV['SSO_USERINFO_URL'] ?? 'not set',
|
||||
]
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
#[Route('/connect/sso/check', name: 'connect_sso_check')]
|
||||
public function connectCheckAction(LoggerInterface $logger): Response
|
||||
{
|
||||
$logger->info('OAuth callback called');
|
||||
// This method will be intercepted by the OAuth authenticator
|
||||
return new JsonResponse(['status' => 'This should not be displayed if OAuth is working properly']);
|
||||
}
|
||||
|
||||
#[Route('/logout', name: 'app_logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
// This method can be empty - it will be intercepted by the logout key on your firewall
|
||||
throw new \Exception('This should never be reached!');
|
||||
}
|
||||
|
||||
#[Route('/oauth-debug', name: 'oauth_debug')]
|
||||
public function oauthDebug(): Response
|
||||
{
|
||||
return new JsonResponse([
|
||||
'SSO_CLIENT_ID' => $_ENV['SSO_CLIENT_ID'] ?? 'not set',
|
||||
'SSO_CLIENT_SECRET' => substr($_ENV['SSO_CLIENT_SECRET'] ?? 'not set', 0, 3) . '***',
|
||||
'SSO_AUTHORIZE_URL' => $_ENV['SSO_AUTHORIZE_URL'] ?? 'not set',
|
||||
'SSO_TOKEN_URL' => $_ENV['SSO_TOKEN_URL'] ?? 'not set',
|
||||
'SSO_USERINFO_URL' => $_ENV['SSO_USERINFO_URL'] ?? 'not set',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/login-status', name: 'app_login_status')]
|
||||
public function loginStatus(): JsonResponse
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
return new JsonResponse([
|
||||
'logged_in' => $user !== null,
|
||||
'user' => $user ? [
|
||||
'identifier' => $user->getUserIdentifier(),
|
||||
'roles' => $user->getRoles(),
|
||||
] : null,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, unique: true)]
|
||||
private ?string $username = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $password = null;
|
||||
|
||||
#[ORM\Column(length: 255, unique: true)]
|
||||
private ?string $email = null;
|
||||
|
||||
#[ORM\Column(type: 'json')]
|
||||
private array $roles = [];
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): static
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): static
|
||||
{
|
||||
$this->email = $email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A visual identifier that represents this user.
|
||||
*
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return (string) $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
// guarantee every user at least has ROLE_USER
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
public function setRoles(array $roles): static
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<User>
|
||||
*/
|
||||
class UserRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return User[] Returns an array of User objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('u')
|
||||
// ->andWhere('u.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('u.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?User
|
||||
// {
|
||||
// return $this->createQueryBuilder('u')
|
||||
// ->andWhere('u.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
||||
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class OAuthAuthenticator extends OAuth2Authenticator implements AuthenticationEntryPointInterface
|
||||
{
|
||||
private $clientRegistry;
|
||||
private $router;
|
||||
private $logger;
|
||||
|
||||
public function __construct(ClientRegistry $clientRegistry, RouterInterface $router, LoggerInterface $logger)
|
||||
{
|
||||
$this->clientRegistry = $clientRegistry;
|
||||
$this->router = $router;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function supports(Request $request): ?bool
|
||||
{
|
||||
return $request->attributes->get('_route') === 'connect_sso_check' && $request->query->has('code');
|
||||
}
|
||||
|
||||
public function authenticate(Request $request): Passport
|
||||
{
|
||||
$client = $this->getSudalysClient();
|
||||
$accessToken = $this->fetchAccessToken($client);
|
||||
|
||||
return new SelfValidatingPassport(
|
||||
new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
|
||||
$sudalysSsoUser = $this->getSudalysClient()->fetchUserFromToken($accessToken);
|
||||
|
||||
$email = $sudalysSsoUser->getEmail();
|
||||
|
||||
/*
|
||||
* On regarde si l'utilisateur est deja connecté
|
||||
*/
|
||||
$connectedUser = $this->em->getRepository(User::class)->findOneBy(['ssoToken' => $sudalysSsoUser->getIdToken()]);
|
||||
if ($connectedUser) {
|
||||
return $connectedUser;
|
||||
}
|
||||
|
||||
// On reggarde si user existe en bdd
|
||||
/** @var User $userInDatabase */
|
||||
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
|
||||
|
||||
|
||||
/**
|
||||
* A commenter si on ne veut pas creer l utilisateur s il n existe pas en bdd locale
|
||||
* **/
|
||||
if (!$user) {
|
||||
$user = new User();
|
||||
$user->setEmail($sudalysSsoUser->getEmail());
|
||||
$user->setPassword("createAPasswordMethod");
|
||||
$this->em->persist($user);
|
||||
}
|
||||
|
||||
// On met a jour le token
|
||||
$user->setSsoToken($sudalysSsoUser->getIdToken());
|
||||
$this->em->flush();
|
||||
|
||||
return $user;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private function getUserIdentifier(ResourceOwnerInterface $resourceOwner): string
|
||||
{
|
||||
// Get the unique identifier from the resource owner
|
||||
// This depends on your SSO server's response format
|
||||
// For standard OAuth servers, this might be the 'sub' field
|
||||
$data = $resourceOwner->toArray();
|
||||
|
||||
$this->logger->debug('Resource owner data', [
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
// Try to get a unique identifier from the data
|
||||
if (isset($data['sub'])) {
|
||||
return $data['sub'];
|
||||
} elseif (isset($data['id'])) {
|
||||
return $data['id'];
|
||||
} elseif (isset($data['email'])) {
|
||||
return $data['email'];
|
||||
}
|
||||
|
||||
// Fallback to a hash of the entire data if no good identifier is found
|
||||
return md5(json_encode($data));
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||
{
|
||||
$this->logger->info('OAuth authentication successful', [
|
||||
'user' => $token->getUserIdentifier()
|
||||
]);
|
||||
|
||||
return new RedirectResponse($this->router->generate('app_home'));
|
||||
}
|
||||
|
||||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
||||
{
|
||||
$this->logger->error('OAuth authentication failure', [
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode()
|
||||
]);
|
||||
|
||||
return new JsonResponse([
|
||||
'error' => 'Authentication failed',
|
||||
'message' => $exception->getMessage(),
|
||||
'query_params' => $request->query->all()
|
||||
], Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
public function start(Request $request, AuthenticationException $authException = null): Response
|
||||
{
|
||||
return new RedirectResponse(
|
||||
$this->router->generate('connect_sso_start'),
|
||||
Response::HTTP_TEMPORARY_REDIRECT
|
||||
);
|
||||
}
|
||||
|
||||
private function getSudalysClient()
|
||||
{
|
||||
return $this->clientRegistry->getClient('sudalys');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use KnpU\OAuth2ClientBundle\Security\User\OAuthUserProvider as BaseOAuthUserProvider;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
// TODO : delete this file
|
||||
class OAuthUserProvider extends BaseOAuthUserProvider
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private ClientRegistry $clientRegistry;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, ClientRegistry $clientRegistry)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->clientRegistry = $clientRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user from the given token.
|
||||
*/
|
||||
public function loadUserByIdentifier(string $identifier): UserInterface
|
||||
{
|
||||
// Try to find an existing user with this identifier
|
||||
$userRepository = $this->entityManager->getRepository(User::class);
|
||||
$user = $userRepository->findOneBy(['username' => $identifier]);
|
||||
|
||||
// If the user doesn't exist, create one
|
||||
if (!$user) {
|
||||
$user = new User();
|
||||
$user->setUsername($identifier);
|
||||
$user->setPassword(''); // No password for OAuth users
|
||||
|
||||
// Try to get an email from the current client's token data if available
|
||||
try {
|
||||
$client = $this->clientRegistry->getClient('sso_server');
|
||||
$accessToken = $client->getAccessToken();
|
||||
if ($accessToken) {
|
||||
$resourceOwner = $client->fetchUserFromToken($accessToken);
|
||||
$userData = $resourceOwner->toArray();
|
||||
|
||||
// Set email if available in the response
|
||||
if (isset($userData['email'])) {
|
||||
$user->setEmail($userData['email']);
|
||||
} else {
|
||||
$user->setEmail($identifier . '@example.com');
|
||||
}
|
||||
} else {
|
||||
$user->setEmail($identifier . '@example.com');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Fallback if we can't get the email
|
||||
$user->setEmail($identifier . '@example.com');
|
||||
}
|
||||
|
||||
$user->setRoles(['ROLE_USER']);
|
||||
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this provider supports the given user class.
|
||||
*/
|
||||
public function supportsClass($class): bool
|
||||
{
|
||||
return $class === User::class || is_subclass_of($class, User::class);
|
||||
}
|
||||
}
|
12
symfony.lock
12
symfony.lock
|
@ -26,6 +26,18 @@
|
|||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"knpuniversity/oauth2-client-bundle": {
|
||||
"version": "2.18",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "1.20",
|
||||
"ref": "1ff300d8c030f55c99219cc55050b97a695af3f6"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/knpu_oauth2_client.yaml"
|
||||
]
|
||||
},
|
||||
"phpunit/phpunit": {
|
||||
"version": "9.6",
|
||||
"recipe": {
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}OAuth Client Test{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
<h1>OAuth Client Test</h1>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>OAuth Configuration</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"><strong>Client ID:</strong> {{ client_id }}</li>
|
||||
<li class="list-group-item"><strong>Client Secret:</strong> {{ client_secret }}</li>
|
||||
<li class="list-group-item"><strong>Authorize URL:</strong> {{ authorize_url }}</li>
|
||||
<li class="list-group-item"><strong>Token URL:</strong> {{ token_url }}</li>
|
||||
<li class="list-group-item"><strong>UserInfo URL:</strong> {{ userinfo_url }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>URL Connectivity Tests</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group">
|
||||
{% for url, result in url_tests %}
|
||||
<li class="list-group-item">
|
||||
<strong>{{ url }}:</strong>
|
||||
<pre>{{ result }}</pre>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Token Request Test</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre>{{ token_test }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{{ path('app_home') }}" class="btn btn-primary">Back to Home</a>
|
||||
<a href="{{ path('connect_sso_start') }}" class="btn btn-success">Try SSO Login</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,43 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Welcome{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h1 class="h3 mb-0">Welcome to the OAuth SSO Client</h1>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if app.user %}
|
||||
<div class="alert alert-success">
|
||||
<h4>✅ You are successfully logged in!</h4>
|
||||
<p>Your identifier: <strong>{{ app.user.userIdentifier }}</strong></p>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 mt-4">
|
||||
<a href="{{ path('app_user_info') }}" class="btn btn-info">View User Details</a>
|
||||
<a href="{{ path('app_dashboard') }}" class="btn btn-primary">Go to Dashboard</a>
|
||||
<a href="{{ path('app_logout') }}" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<h4>⚠️ You are not logged in</h4>
|
||||
<p>Click the button below to login with SSO</p>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 mt-4">
|
||||
<a href="{{ path('connect_sso_start') }}" class="btn btn-primary btn-lg">Login with SSO</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
<small>Powered by Symfony SSO Client</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,53 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Dashboard - Protected Area{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h1 class="h3 mb-0">Dashboard - Protected Area</h1>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<h4>✅ Secure Dashboard</h4>
|
||||
<p>This page is only accessible to authenticated users with <code>ROLE_USER</code>.</p>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
User Information
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>You are logged in as: <strong>{{ app.user.userIdentifier }}</strong></p>
|
||||
<p>Roles:
|
||||
{% for role in app.user.roles %}
|
||||
<span class="badge bg-secondary">{{ role }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Protected Content
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Welcome to the protected area of the application</h5>
|
||||
<p>This is sensitive information that only authenticated users can see.</p>
|
||||
<p>Your SSO authentication has successfully granted you access to this protected resource.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ path('app_home') }}" class="btn btn-secondary">Back to Home</a>
|
||||
<a href="{{ path('app_user_info') }}" class="btn btn-info">User Details</a>
|
||||
<a href="{{ path('app_logout') }}" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,69 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}User Information{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h1>User Information</h1>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<h3>✅ You are successfully logged in!</h3>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">User Details</h4>
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Identifier:</th>
|
||||
<td>{{ user.userIdentifier }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Username:</th>
|
||||
<td>{{ user.username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Email:</th>
|
||||
<td>{{ user.email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Roles:</th>
|
||||
<td>
|
||||
<ul class="list-unstyled">
|
||||
{% for role in user.roles %}
|
||||
<li><span class="badge bg-secondary">{{ role }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="mt-4">User Object</h4>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body bg-light">
|
||||
<pre>{{ dump(user) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">Session Information</h4>
|
||||
<div class="card">
|
||||
<div class="card-body bg-light">
|
||||
<pre>{{ dump(app.session) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ path('app_home') }}" class="btn btn-secondary">Back to Home</a>
|
||||
<a href="{{ path('app_dashboard') }}" class="btn btn-primary">Go to Dashboard</a>
|
||||
<a href="{{ path('app_logout') }}" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue