diff --git a/assets/css/scss/atoms/_divider.scss b/assets/css/scss/atoms/_divider.scss index b61637386a..7857997349 100644 --- a/assets/css/scss/atoms/_divider.scss +++ b/assets/css/scss/atoms/_divider.scss @@ -12,7 +12,7 @@ &[aria-orientation="horizontal"] { @apply before:absolute before:block before:left-0 before:w-full before:top-1/2 before:content-[""] before:border-t before:border-solid before:border-gray-25 - flex w-full relative items-center my-4 px-2; + flex relative items-center my-4 px-2; div { @apply first:px-2; diff --git a/assets/css/scss/index.scss b/assets/css/scss/index.scss index 71fa716aa0..dcda585719 100755 --- a/assets/css/scss/index.scss +++ b/assets/css/scss/index.scss @@ -50,6 +50,7 @@ @import "organisms/course_card"; @import "organisms/datatable"; @import "organisms/dataview"; +@import "organisms/external_logins"; @import "organisms/modals"; @import "organisms/menu"; @import "organisms/sidebar"; diff --git a/assets/css/scss/organisms/_external_logins.scss b/assets/css/scss/organisms/_external_logins.scss new file mode 100644 index 0000000000..3b4aebb1be --- /dev/null +++ b/assets/css/scss/organisms/_external_logins.scss @@ -0,0 +1,15 @@ +.external-logins { + @apply flex flex-col gap-2 items-center; + + &__divider { + @apply w-60 mx-auto uppercase; + } + + &__button-list { + @apply space-y-4; + } + + &__button { + @apply border border-gray-25 bg-white rounded-lg text-gray-90 py-4 px-12 block font-semibold; + } +} diff --git a/assets/vue/components/Login.vue b/assets/vue/components/Login.vue index de150961fd..ed130b2f6d 100644 --- a/assets/vue/components/Login.vue +++ b/assets/vue/components/Login.vue @@ -66,6 +66,8 @@ /> + + @@ -77,6 +79,7 @@ import Password from "primevue/password" import InputSwitch from "primevue/inputswitch" import { useI18n } from "vue-i18n" import { useLogin } from "../composables/auth/login" +import ExternalLoginButtons from "./login/LoginExternalButtons.vue" const { t } = useI18n() diff --git a/assets/vue/components/login/LoginExternalButtons.vue b/assets/vue/components/login/LoginExternalButtons.vue new file mode 100644 index 0000000000..6c1ce2e399 --- /dev/null +++ b/assets/vue/components/login/LoginExternalButtons.vue @@ -0,0 +1,37 @@ + + + diff --git a/assets/vue/store/platformConfig.js b/assets/vue/store/platformConfig.js index 8a0a19def7..ca892783d1 100644 --- a/assets/vue/store/platformConfig.js +++ b/assets/vue/store/platformConfig.js @@ -8,6 +8,7 @@ export const usePlatformConfig = defineStore("platformConfig", () => { const studentView = ref("teacherview") const plugins = ref([]) const visualTheme = ref("chamilo") + const externalAuthentication = ref([]) async function findSettingsRequest() { isLoading.value = true @@ -22,6 +23,8 @@ export const usePlatformConfig = defineStore("platformConfig", () => { studentView.value = data.studentview plugins.value = data.plugins + + externalAuthentication.value = data.external_authentication } catch (e) { console.log(e) } finally { @@ -48,5 +51,6 @@ export const usePlatformConfig = defineStore("platformConfig", () => { getSetting, isStudentViewActive, visualTheme, + externalAuthentication, } }) diff --git a/composer.json b/composer.json index 7890bcba1c..7f15627089 100755 --- a/composer.json +++ b/composer.json @@ -97,6 +97,7 @@ "league/glide-symfony": "^2.0", "league/html-to-markdown": "^5.1", "league/mime-type-detection": "^1.7", + "league/oauth2-facebook": "^2.2", "lexik/jwt-authentication-bundle": "^2.20", "maennchen/zipstream-php": "^2.1", "masterminds/html5": "^2.0", @@ -116,6 +117,7 @@ "sensio/framework-extra-bundle": "~6.1", "simpod/doctrine-utcdatetime": "^0.1.2", "sonata-project/exporter": "^2.2", + "stevenmaguire/oauth2-keycloak": "^5.1", "stof/doctrine-extensions-bundle": "^1.10", "sunra/php-simple-html-dom-parser": "~1.5", "symfony/apache-pack": "^1.0", diff --git a/composer.lock b/composer.lock index 2097873039..b9b36be1c2 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "d7060042570128e9de8be2808d418e6a", + "content-hash": "75f3be9ad1f4415590e7987a31f08940", "packages": [ { "name": "a2lix/auto-form-bundle", @@ -842,23 +842,23 @@ }, { "name": "dasprid/enum", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", "shasum": "" }, "require": { "php": ">=7.1 <9.0" }, "require-dev": { - "phpunit/phpunit": "^7 | ^8 | ^9", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", "squizlabs/php_codesniffer": "*" }, "type": "library", @@ -886,9 +886,9 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" }, - "time": "2023-08-25T16:18:39+00:00" + "time": "2024-08-09T14:30:48+00:00" }, { "name": "doctrine/annotations", @@ -1322,16 +1322,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.6", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1" + "reference": "d8f68ea6cc00912e5313237130b8c8decf4d28c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/b7411825cf7efb7e51f9791dea19d86e43b399a1", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/d8f68ea6cc00912e5313237130b8c8decf4d28c6", + "reference": "d8f68ea6cc00912e5313237130b8c8decf4d28c6", "shasum": "" }, "require": { @@ -1347,12 +1347,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.11.5", + "phpstan/phpstan": "1.11.7", "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "9.6.19", + "phpunit/phpunit": "9.6.20", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.10.1", + "squizlabs/php_codesniffer": "3.10.2", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -1415,7 +1415,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.6" + "source": "https://github.com/doctrine/dbal/tree/3.9.0" }, "funding": [ { @@ -1431,7 +1431,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T10:38:17+00:00" + "time": "2024-08-15T07:34:42+00:00" }, { "name": "doctrine/deprecations", @@ -2212,16 +2212,16 @@ }, { "name": "doctrine/orm", - "version": "2.19.6", + "version": "2.19.7", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073" + "reference": "168ac31084226f94d42e7461a40ff5607a56bd35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", - "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", + "url": "https://api.github.com/repos/doctrine/orm/zipball/168ac31084226f94d42e7461a40ff5607a56bd35", + "reference": "168ac31084226f94d42e7461a40ff5607a56bd35", "shasum": "" }, "require": { @@ -2307,9 +2307,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.19.6" + "source": "https://github.com/doctrine/orm/tree/2.19.7" }, - "time": "2024-06-26T17:24:40+00:00" + "time": "2024-08-23T06:54:57+00:00" }, { "name": "doctrine/persistence", @@ -2410,16 +2410,16 @@ }, { "name": "doctrine/sql-formatter", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc" + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d1ac84aef745c69ea034929eb6d65a6908b675cc", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/7f83911cc5eba870de7ebb11283972483f7e2891", + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891", "shasum": "" }, "require": { @@ -2459,9 +2459,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.4.0" + "source": "https://github.com/doctrine/sql-formatter/tree/1.4.1" }, - "time": "2024-05-08T08:12:09+00:00" + "time": "2024-08-05T20:32:22+00:00" }, { "name": "egulias/email-validator", @@ -3477,22 +3477,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "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" @@ -3503,9 +3503,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -3583,7 +3583,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -3599,20 +3599,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", "shasum": "" }, "require": { @@ -3620,7 +3620,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { @@ -3666,7 +3666,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.0.3" }, "funding": [ { @@ -3682,20 +3682,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2024-07-18T10:29:17+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -3710,8 +3710,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -3782,7 +3782,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -3798,7 +3798,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "intervention/image", @@ -4368,32 +4368,31 @@ }, { "name": "knpuniversity/oauth2-client-bundle", - "version": "v2.18.1", + "version": "v2.18.2", "source": { "type": "git", "url": "https://github.com/knpuniversity/oauth2-client-bundle.git", - "reference": "1d59f49f164805b45f95f92cf743781bc2ba7d2b" + "reference": "0f8db87efa064bc1800315c027a80b53ef935524" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/knpuniversity/oauth2-client-bundle/zipball/1d59f49f164805b45f95f92cf743781bc2ba7d2b", - "reference": "1d59f49f164805b45f95f92cf743781bc2ba7d2b", + "url": "https://api.github.com/repos/knpuniversity/oauth2-client-bundle/zipball/0f8db87efa064bc1800315c027a80b53ef935524", + "reference": "0f8db87efa064bc1800315c027a80b53ef935524", "shasum": "" }, "require": { "league/oauth2-client": "^2.0", "php": ">=8.1", - "symfony/dependency-injection": "^4.4|^5.0|^6.0|^7.0", - "symfony/framework-bundle": "^4.4|^5.0|^6.0|^7.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0|^7.0", - "symfony/routing": "^4.4|^5.0|^6.0|^7.0" + "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", - "phpstan/phpstan": "^1.0", - "symfony/phpunit-bridge": "^5.3.1|^6.0|^7.0", - "symfony/security-guard": "^4.4|^5.0|^6.0|^7.0", - "symfony/yaml": "^4.4|^5.0|^6.0|^7.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" @@ -4422,9 +4421,9 @@ ], "support": { "issues": "https://github.com/knpuniversity/oauth2-client-bundle/issues", - "source": "https://github.com/knpuniversity/oauth2-client-bundle/tree/v2.18.1" + "source": "https://github.com/knpuniversity/oauth2-client-bundle/tree/v2.18.2" }, - "time": "2024-02-14T17:41:28+00:00" + "time": "2024-08-12T15:26:07+00:00" }, { "name": "laminas/laminas-code", @@ -4701,16 +4700,16 @@ }, { "name": "laminas/laminas-filter", - "version": "2.36.0", + "version": "2.37.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-filter.git", - "reference": "307afc21ada0648e84cdcf9e14cd84bd43ee9d13" + "reference": "27dda1e60547bc000b876e24808f47932df2f4ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/307afc21ada0648e84cdcf9e14cd84bd43ee9d13", - "reference": "307afc21ada0648e84cdcf9e14cd84bd43ee9d13", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/27dda1e60547bc000b876e24808f47932df2f4ac", + "reference": "27dda1e60547bc000b876e24808f47932df2f4ac", "shasum": "" }, "require": { @@ -4776,7 +4775,7 @@ "type": "community_bridge" } ], - "time": "2024-06-13T10:31:36+00:00" + "time": "2024-08-12T09:23:23+00:00" }, { "name": "laminas/laminas-permissions-acl", @@ -5675,6 +5674,62 @@ }, "time": "2023-04-16T18:19:15+00:00" }, + { + "name": "league/oauth2-facebook", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-facebook.git", + "reference": "ec6d62a00b548c6cd56d7b734346b9e6befbfbbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-facebook/zipball/ec6d62a00b548c6cd56d7b734346b9e6befbfbbb", + "reference": "ec6d62a00b548c6cd56d7b734346b9e6befbfbbb", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0", + "php": ">=7.3" + }, + "require-dev": { + "ext-json": "*", + "mockery/mockery": "~1.3.0", + "phpunit/phpunit": "^9.4", + "squizlabs/php_codesniffer": "~3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sammy Kaye Powers", + "email": "me@sammyk.me", + "homepage": "http://www.sammyk.me" + } + ], + "description": "Facebook OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "Authentication", + "authorization", + "client", + "facebook", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-facebook/issues", + "source": "https://github.com/thephpleague/oauth2-facebook/tree/2.2.0" + }, + "time": "2022-02-24T18:45:07+00:00" + }, { "name": "league/uri", "version": "7.4.1", @@ -8366,16 +8421,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "79dff0b268932c640297f5208d6298f71855c03e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", + "reference": "79dff0b268932c640297f5208d6298f71855c03e", "shasum": "" }, "require": { @@ -8410,9 +8465,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.1" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-08-21T13:31:24+00:00" }, { "name": "psr/simple-cache", @@ -9123,6 +9178,67 @@ ], "time": "2023-12-25T11:46:58+00:00" }, + { + "name": "stevenmaguire/oauth2-keycloak", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/stevenmaguire/oauth2-keycloak.git", + "reference": "1b690b7377dfe7a23e1590373f37e12cf40a6d75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevenmaguire/oauth2-keycloak/zipball/1b690b7377dfe7a23e1590373f37e12cf40a6d75", + "reference": "1b690b7377dfe7a23e1590373f37e12cf40a6d75", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^6.0", + "league/oauth2-client": "^2.0", + "php": "~7.2 || ~8.0" + }, + "require-dev": { + "mockery/mockery": "~1.5.0", + "phpunit/phpunit": "~9.6.4", + "squizlabs/php_codesniffer": "~3.7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Stevenmaguire\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steven Maguire", + "email": "stevenmaguire@gmail.com", + "homepage": "https://github.com/stevenmaguire" + } + ], + "description": "Keycloak OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "keycloak", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/stevenmaguire/oauth2-keycloak/issues", + "source": "https://github.com/stevenmaguire/oauth2-keycloak/tree/5.1.0" + }, + "time": "2023-10-24T06:10:44+00:00" + }, { "name": "stof/doctrine-extensions-bundle", "version": "v1.12.0", @@ -9352,16 +9468,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "287142df5579ce223c485b3872df3efae8390984" + "reference": "6702d2d777260e6ff3451fee2d7d78ab5f715cdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/287142df5579ce223c485b3872df3efae8390984", - "reference": "287142df5579ce223c485b3872df3efae8390984", + "url": "https://api.github.com/repos/symfony/cache/zipball/6702d2d777260e6ff3451fee2d7d78ab5f715cdc", + "reference": "6702d2d777260e6ff3451fee2d7d78ab5f715cdc", "shasum": "" }, "require": { @@ -9428,7 +9544,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.8" + "source": "https://github.com/symfony/cache/tree/v6.4.10" }, "funding": [ { @@ -9444,7 +9560,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-17T06:05:49+00:00" }, { "name": "symfony/cache-contracts", @@ -9673,16 +9789,16 @@ }, { "name": "symfony/console", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9" + "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", - "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", + "url": "https://api.github.com/repos/symfony/console/zipball/504974cbe43d05f83b201d6498c206f16fc0cdbc", + "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc", "shasum": "" }, "require": { @@ -9747,7 +9863,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.9" + "source": "https://github.com/symfony/console/tree/v6.4.10" }, "funding": [ { @@ -9763,7 +9879,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:49:33+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/css-selector", @@ -9832,16 +9948,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "a4df9dfe5da2d177af6643610c7bee2cb76a9f5e" + "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a4df9dfe5da2d177af6643610c7bee2cb76a9f5e", - "reference": "a4df9dfe5da2d177af6643610c7bee2cb76a9f5e", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5caf9c5f6085f13b27d70a236b776c07e4a1c3eb", + "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb", "shasum": "" }, "require": { @@ -9893,7 +10009,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.9" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.10" }, "funding": [ { @@ -9909,7 +10025,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T10:45:28+00:00" + "time": "2024-07-26T07:32:07+00:00" }, { "name": "symfony/deprecation-contracts", @@ -9980,16 +10096,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "64d024fcb3aa613fe163ecae4aaa836dd6d1c5cd" + "reference": "0de9662441bce4670506d0c371cc819a9d0a7607" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/64d024fcb3aa613fe163ecae4aaa836dd6d1c5cd", - "reference": "64d024fcb3aa613fe163ecae4aaa836dd6d1c5cd", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/0de9662441bce4670506d0c371cc819a9d0a7607", + "reference": "0de9662441bce4670506d0c371cc819a9d0a7607", "shasum": "" }, "require": { @@ -10068,7 +10184,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.9" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.10" }, "funding": [ { @@ -10084,7 +10200,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:25:38+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/doctrine-messenger", @@ -10160,16 +10276,16 @@ }, { "name": "symfony/dotenv", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "55aefa0029adff89ecffdb560820e945c7983f06" + "reference": "2ae0c84cc9be0dc1eeb86016970b63c764d8472e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/55aefa0029adff89ecffdb560820e945c7983f06", - "reference": "55aefa0029adff89ecffdb560820e945c7983f06", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/2ae0c84cc9be0dc1eeb86016970b63c764d8472e", + "reference": "2ae0c84cc9be0dc1eeb86016970b63c764d8472e", "shasum": "" }, "require": { @@ -10214,7 +10330,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v6.4.8" + "source": "https://github.com/symfony/dotenv/tree/v6.4.10" }, "funding": [ { @@ -10230,20 +10346,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-09T18:29:35+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec" + "reference": "231f1b2ee80f72daa1972f7340297d67439224f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/c9b7cc075b3ab484239855622ca05cb0b99c13ec", - "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/231f1b2ee80f72daa1972f7340297d67439224f0", + "reference": "231f1b2ee80f72daa1972f7340297d67439224f0", "shasum": "" }, "require": { @@ -10289,7 +10405,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.9" + "source": "https://github.com/symfony/error-handler/tree/v6.4.10" }, "funding": [ { @@ -10305,7 +10421,7 @@ "type": "tidelift" } ], - "time": "2024-06-21T16:04:15+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/event-dispatcher", @@ -10595,16 +10711,16 @@ }, { "name": "symfony/finder", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" + "reference": "af29198d87112bebdd397bd7735fbd115997824c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", + "url": "https://api.github.com/repos/symfony/finder/zipball/af29198d87112bebdd397bd7735fbd115997824c", + "reference": "af29198d87112bebdd397bd7735fbd115997824c", "shasum": "" }, "require": { @@ -10639,7 +10755,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.8" + "source": "https://github.com/symfony/finder/tree/v6.4.10" }, "funding": [ { @@ -10655,20 +10771,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-24T07:06:38+00:00" }, { "name": "symfony/flex", - "version": "v2.4.5", + "version": "v2.4.6", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "b0a405f40614c9f584b489d54f91091817b0e26e" + "reference": "4dc11919791f81d087a12db2ab4c7e044431ef6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/b0a405f40614c9f584b489d54f91091817b0e26e", - "reference": "b0a405f40614c9f584b489d54f91091817b0e26e", + "url": "https://api.github.com/repos/symfony/flex/zipball/4dc11919791f81d087a12db2ab4c7e044431ef6b", + "reference": "4dc11919791f81d087a12db2ab4c7e044431ef6b", "shasum": "" }, "require": { @@ -10704,7 +10820,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.4.5" + "source": "https://github.com/symfony/flex/tree/v2.4.6" }, "funding": [ { @@ -10720,20 +10836,20 @@ "type": "tidelift" } ], - "time": "2024-03-02T08:16:47+00:00" + "time": "2024-04-27T10:22:22+00:00" }, { "name": "symfony/form", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "196ebc738e59bec2bbf1f49c24cc221b47f77f5d" + "reference": "67dd6a3fd986cae9a90a8c2c526464c06f525863" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/196ebc738e59bec2bbf1f49c24cc221b47f77f5d", - "reference": "196ebc738e59bec2bbf1f49c24cc221b47f77f5d", + "url": "https://api.github.com/repos/symfony/form/zipball/67dd6a3fd986cae9a90a8c2c526464c06f525863", + "reference": "67dd6a3fd986cae9a90a8c2c526464c06f525863", "shasum": "" }, "require": { @@ -10801,7 +10917,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v6.4.8" + "source": "https://github.com/symfony/form/tree/v6.4.10" }, "funding": [ { @@ -10817,20 +10933,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-19T08:21:35+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "c1d1cb0e508e11639283e1e6f8918eef0fa524bd" + "reference": "6cbdb0cc3ddbb63499262cd3036882b08ee2690b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c1d1cb0e508e11639283e1e6f8918eef0fa524bd", - "reference": "c1d1cb0e508e11639283e1e6f8918eef0fa524bd", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6cbdb0cc3ddbb63499262cd3036882b08ee2690b", + "reference": "6cbdb0cc3ddbb63499262cd3036882b08ee2690b", "shasum": "" }, "require": { @@ -10949,7 +11065,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.9" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.10" }, "funding": [ { @@ -10965,7 +11081,7 @@ "type": "tidelift" } ], - "time": "2024-06-26T08:32:27+00:00" + "time": "2024-07-26T13:24:20+00:00" }, { "name": "symfony/html-sanitizer", @@ -11038,16 +11154,16 @@ }, { "name": "symfony/http-client", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "6e9db0025db565bcf8f1d46ed734b549e51e6045" + "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/6e9db0025db565bcf8f1d46ed734b549e51e6045", - "reference": "6e9db0025db565bcf8f1d46ed734b549e51e6045", + "url": "https://api.github.com/repos/symfony/http-client/zipball/b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", + "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", "shasum": "" }, "require": { @@ -11111,7 +11227,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.9" + "source": "https://github.com/symfony/http-client/tree/v6.4.10" }, "funding": [ { @@ -11127,7 +11243,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T07:59:05+00:00" + "time": "2024-07-15T09:26:24+00:00" }, { "name": "symfony/http-client-contracts", @@ -11209,16 +11325,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947" + "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/27de8cc95e11db7a50b027e71caaab9024545947", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/117f1f20a7ade7bcea28b861fb79160a21a1e37b", + "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b", "shasum": "" }, "require": { @@ -11266,7 +11382,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.10" }, "funding": [ { @@ -11282,20 +11398,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:36:27+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005" + "reference": "147e0daf618d7575b5007055340d09aece5cf068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", - "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/147e0daf618d7575b5007055340d09aece5cf068", + "reference": "147e0daf618d7575b5007055340d09aece5cf068", "shasum": "" }, "require": { @@ -11380,7 +11496,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.9" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.10" }, "funding": [ { @@ -11396,7 +11512,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T11:48:06+00:00" + "time": "2024-07-26T14:52:04+00:00" }, { "name": "symfony/intl", @@ -11563,16 +11679,16 @@ }, { "name": "symfony/messenger", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "c211861f5f84860b9663c43dcd440ac8bcb1d250" + "reference": "7985801bc96cd5c130746b422d49e371ba5d66de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/c211861f5f84860b9663c43dcd440ac8bcb1d250", - "reference": "c211861f5f84860b9663c43dcd440ac8bcb1d250", + "url": "https://api.github.com/repos/symfony/messenger/zipball/7985801bc96cd5c130746b422d49e371ba5d66de", + "reference": "7985801bc96cd5c130746b422d49e371ba5d66de", "shasum": "" }, "require": { @@ -11630,7 +11746,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v6.4.9" + "source": "https://github.com/symfony/messenger/tree/v6.4.10" }, "funding": [ { @@ -11646,7 +11762,7 @@ "type": "tidelift" } ], - "time": "2024-06-24T14:04:31+00:00" + "time": "2024-07-09T18:35:14+00:00" }, { "name": "symfony/mime", @@ -12789,16 +12905,16 @@ }, { "name": "symfony/property-info", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "1a0357ed93a6ab09482435a7818defaa85cad69b" + "reference": "edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/1a0357ed93a6ab09482435a7818defaa85cad69b", - "reference": "1a0357ed93a6ab09482435a7818defaa85cad69b", + "url": "https://api.github.com/repos/symfony/property-info/zipball/edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77", + "reference": "edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77", "shasum": "" }, "require": { @@ -12852,7 +12968,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v6.4.9" + "source": "https://github.com/symfony/property-info/tree/v6.4.10" }, "funding": [ { @@ -12868,7 +12984,7 @@ "type": "tidelift" } ], - "time": "2024-06-21T16:04:15+00:00" + "time": "2024-07-26T07:32:07+00:00" }, { "name": "symfony/proxy-manager-bridge", @@ -13005,16 +13121,16 @@ }, { "name": "symfony/routing", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58" + "reference": "aad19fe10753ba842f0d653a8db819c4b3affa87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "url": "https://api.github.com/repos/symfony/routing/zipball/aad19fe10753ba842f0d653a8db819c4b3affa87", + "reference": "aad19fe10753ba842f0d653a8db819c4b3affa87", "shasum": "" }, "require": { @@ -13068,7 +13184,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.8" + "source": "https://github.com/symfony/routing/tree/v6.4.10" }, "funding": [ { @@ -13084,7 +13200,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-15T09:26:24+00:00" }, { "name": "symfony/runtime", @@ -13249,16 +13365,16 @@ }, { "name": "symfony/security-bundle", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "adc34df2fe487a13d4410a237202422294c917b2" + "reference": "50007f4f76632741b62fa9604c5f65807f268b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/adc34df2fe487a13d4410a237202422294c917b2", - "reference": "adc34df2fe487a13d4410a237202422294c917b2", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/50007f4f76632741b62fa9604c5f65807f268b72", + "reference": "50007f4f76632741b62fa9604c5f65807f268b72", "shasum": "" }, "require": { @@ -13341,7 +13457,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v6.4.9" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.10" }, "funding": [ { @@ -13357,20 +13473,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:06:43+00:00" + "time": "2024-07-17T10:49:44+00:00" }, { "name": "symfony/security-core", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "2d58f4c3ff50b1b4eef0a333c2b1e3eef46807f4" + "reference": "432dec55da108c471adcf58c351af01372a52164" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/2d58f4c3ff50b1b4eef0a333c2b1e3eef46807f4", - "reference": "2d58f4c3ff50b1b4eef0a333c2b1e3eef46807f4", + "url": "https://api.github.com/repos/symfony/security-core/zipball/432dec55da108c471adcf58c351af01372a52164", + "reference": "432dec55da108c471adcf58c351af01372a52164", "shasum": "" }, "require": { @@ -13427,7 +13543,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v6.4.9" + "source": "https://github.com/symfony/security-core/tree/v6.4.10" }, "funding": [ { @@ -13443,7 +13559,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T07:59:05+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/security-csrf", @@ -13603,16 +13719,16 @@ }, { "name": "symfony/serializer", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "56ce31d19127e79647ac53387c7555bdcd5730ce" + "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/56ce31d19127e79647ac53387c7555bdcd5730ce", - "reference": "56ce31d19127e79647ac53387c7555bdcd5730ce", + "url": "https://api.github.com/repos/symfony/serializer/zipball/9a67fcf320561e96f94d62bbe0e169ac534a5718", + "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718", "shasum": "" }, "require": { @@ -13681,7 +13797,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.9" + "source": "https://github.com/symfony/serializer/tree/v6.4.10" }, "funding": [ { @@ -13697,7 +13813,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T07:59:05+00:00" + "time": "2024-07-26T13:13:26+00:00" }, { "name": "symfony/service-contracts", @@ -13846,16 +13962,16 @@ }, { "name": "symfony/string", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7" + "reference": "ccf9b30251719567bfd46494138327522b9a9446" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/76792dbd99690a5ebef8050d9206c60c59e681d7", - "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7", + "url": "https://api.github.com/repos/symfony/string/zipball/ccf9b30251719567bfd46494138327522b9a9446", + "reference": "ccf9b30251719567bfd46494138327522b9a9446", "shasum": "" }, "require": { @@ -13912,7 +14028,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.9" + "source": "https://github.com/symfony/string/tree/v6.4.10" }, "funding": [ { @@ -13928,20 +14044,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:25:38+00:00" + "time": "2024-07-22T10:21:14+00:00" }, { "name": "symfony/translation", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a" + "reference": "94041203f8ac200ae9e7c6a18fa6137814ccecc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/a002933b13989fc4bd0b58e04bf7eec5210e438a", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a", + "url": "https://api.github.com/repos/symfony/translation/zipball/94041203f8ac200ae9e7c6a18fa6137814ccecc9", + "reference": "94041203f8ac200ae9e7c6a18fa6137814ccecc9", "shasum": "" }, "require": { @@ -14007,7 +14123,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.8" + "source": "https://github.com/symfony/translation/tree/v6.4.10" }, "funding": [ { @@ -14023,7 +14139,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/translation-contracts", @@ -14372,16 +14488,16 @@ }, { "name": "symfony/validator", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "ee0a4d6a327a963aee094f730da238f7ea18cb01" + "reference": "bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/ee0a4d6a327a963aee094f730da238f7ea18cb01", - "reference": "ee0a4d6a327a963aee094f730da238f7ea18cb01", + "url": "https://api.github.com/repos/symfony/validator/zipball/bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd", + "reference": "bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd", "shasum": "" }, "require": { @@ -14449,7 +14565,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.9" + "source": "https://github.com/symfony/validator/tree/v6.4.10" }, "funding": [ { @@ -14465,20 +14581,20 @@ "type": "tidelift" } ], - "time": "2024-06-22T07:42:41+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172" + "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c31566e4ca944271cc8d8ac6887cbf31b8c6a172", - "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a71cc3374f5fb9759da1961d28c452373b343dd4", + "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4", "shasum": "" }, "require": { @@ -14534,7 +14650,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.9" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.10" }, "funding": [ { @@ -14550,7 +14666,7 @@ "type": "tidelift" } ], - "time": "2024-06-27T13:23:14+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/var-exporter", @@ -15030,16 +15146,16 @@ }, { "name": "twig/cssinliner-extra", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/cssinliner-extra.git", - "reference": "10e88e9a887b646c58e3d670383208f15295dd22" + "reference": "7312a0275812b86918febb4b7a67d0cb084c5d02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/10e88e9a887b646c58e3d670383208f15295dd22", - "reference": "10e88e9a887b646c58e3d670383208f15295dd22", + "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/7312a0275812b86918febb4b7a67d0cb084c5d02", + "reference": "7312a0275812b86918febb4b7a67d0cb084c5d02", "shasum": "" }, "require": { @@ -15083,7 +15199,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.10.0" + "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.11.0" }, "funding": [ { @@ -15095,20 +15211,20 @@ "type": "tidelift" } ], - "time": "2024-05-11T07:35:57+00:00" + "time": "2024-06-21T06:22:31+00:00" }, { "name": "twig/extra-bundle", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "cdc6e23aeb7f4953c1039568c3439aab60c56454" + "reference": "bf8a304eac15838d7724fdf64c345bdefbb75f03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/cdc6e23aeb7f4953c1039568c3439aab60c56454", - "reference": "cdc6e23aeb7f4953c1039568c3439aab60c56454", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/bf8a304eac15838d7724fdf64c345bdefbb75f03", + "reference": "bf8a304eac15838d7724fdf64c345bdefbb75f03", "shasum": "" }, "require": { @@ -15157,7 +15273,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.10.0" + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.11.0" }, "funding": [ { @@ -15169,20 +15285,20 @@ "type": "tidelift" } ], - "time": "2024-05-11T07:35:57+00:00" + "time": "2024-06-21T06:25:01+00:00" }, { "name": "twig/inky-extra", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/inky-extra.git", - "reference": "adfcc3b2becc09e909d30b813cde17351ac82958" + "reference": "f0e6fce7eeab518b5b18b476bc9a5f19a3359354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/inky-extra/zipball/adfcc3b2becc09e909d30b813cde17351ac82958", - "reference": "adfcc3b2becc09e909d30b813cde17351ac82958", + "url": "https://api.github.com/repos/twigphp/inky-extra/zipball/f0e6fce7eeab518b5b18b476bc9a5f19a3359354", + "reference": "f0e6fce7eeab518b5b18b476bc9a5f19a3359354", "shasum": "" }, "require": { @@ -15227,7 +15343,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/inky-extra/tree/v3.10.0" + "source": "https://github.com/twigphp/inky-extra/tree/v3.11.0" }, "funding": [ { @@ -15239,20 +15355,20 @@ "type": "tidelift" } ], - "time": "2024-05-11T07:35:57+00:00" + "time": "2024-06-21T06:22:31+00:00" }, { "name": "twig/intl-extra", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93" + "reference": "e9cadd61342e71e45b2f4f0558122433fd7e4566" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93", - "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/e9cadd61342e71e45b2f4f0558122433fd7e4566", + "reference": "e9cadd61342e71e45b2f4f0558122433fd7e4566", "shasum": "" }, "require": { @@ -15291,7 +15407,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.10.0" + "source": "https://github.com/twigphp/intl-extra/tree/v3.11.0" }, "funding": [ { @@ -15303,20 +15419,20 @@ "type": "tidelift" } ], - "time": "2024-05-11T07:35:57+00:00" + "time": "2024-06-21T06:25:01+00:00" }, { "name": "twig/twig", - "version": "v3.10.3", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", "shasum": "" }, "require": { @@ -15324,7 +15440,8 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php80": "^1.22", + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", @@ -15370,7 +15487,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.10.3" + "source": "https://github.com/twigphp/Twig/tree/v3.11.0" }, "funding": [ { @@ -15382,7 +15499,7 @@ "type": "tidelift" } ], - "time": "2024-05-16T10:04:27+00:00" + "time": "2024-08-08T16:15:16+00:00" }, { "name": "vich/uploader-bundle", @@ -16327,30 +16444,38 @@ }, { "name": "composer/pcre", - "version": "3.1.4", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "04229f163664973f68f38f6f73d917799168ef24" + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", - "reference": "04229f163664973f68f38f6f73d917799168ef24", + "url": "https://api.github.com/repos/composer/pcre/zipball/1637e067347a0c40bbb1e3cd786b20dcab556a81", + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, "require-dev": { - "phpstan/phpstan": "^1.3", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -16378,7 +16503,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.4" + "source": "https://github.com/composer/pcre/tree/3.3.0" }, "funding": [ { @@ -16394,7 +16519,7 @@ "type": "tidelift" } ], - "time": "2024-05-27T13:40:54+00:00" + "time": "2024-08-19T19:43:53+00:00" }, { "name": "composer/semver", @@ -17537,16 +17662,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.7", + "version": "1.11.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d" + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/52d2bbfdcae7f895915629e4694e9497d0f8e28d", - "reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", "shasum": "" }, "require": { @@ -17591,20 +17716,20 @@ "type": "github" } ], - "time": "2024-07-06T11:17:41+00:00" + "time": "2024-08-19T14:37:29+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "1.4.8", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1" + "reference": "4d17bed8a33aa8220c1f2a21a6b14fcdb0e5b02c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1", - "reference": "fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/4d17bed8a33aa8220c1f2a21a6b14fcdb0e5b02c", + "reference": "4d17bed8a33aa8220c1f2a21a6b14fcdb0e5b02c", "shasum": "" }, "require": { @@ -17661,22 +17786,22 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.4.8" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.5.2" }, - "time": "2024-07-16T11:31:01+00:00" + "time": "2024-08-23T11:07:15+00:00" }, { "name": "phpstan/phpstan-symfony", - "version": "1.4.6", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "e909a075d69e0d4db262ac3407350ae2c6b6ab5f" + "reference": "14eec8c011b856eee4d744a2a3f709db1e1858bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/e909a075d69e0d4db262ac3407350ae2c6b6ab5f", - "reference": "e909a075d69e0d4db262ac3407350ae2c6b6ab5f", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/14eec8c011b856eee4d744a2a3f709db1e1858bd", + "reference": "14eec8c011b856eee4d744a2a3f709db1e1858bd", "shasum": "" }, "require": { @@ -17733,41 +17858,41 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.4.6" + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.4.8" }, - "time": "2024-07-16T11:48:54+00:00" + "time": "2024-08-13T19:43:40+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -17776,7 +17901,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -17805,7 +17930,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -17813,7 +17938,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -19318,16 +19443,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.1", + "version": "3.10.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", "shasum": "" }, "require": { @@ -19394,7 +19519,7 @@ "type": "open_collective" } ], - "time": "2024-05-22T21:24:41+00:00" + "time": "2024-07-21T23:26:44+00:00" }, { "name": "symfony/browser-kit", @@ -19699,16 +19824,16 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v6.4.9", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "6e03e4db9696e0cfcda6537177c2c03dc49c45c8" + "reference": "ad510515b11ba5291fdd59b25d70227bfac2d7ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/6e03e4db9696e0cfcda6537177c2c03dc49c45c8", - "reference": "6e03e4db9696e0cfcda6537177c2c03dc49c45c8", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/ad510515b11ba5291fdd59b25d70227bfac2d7ab", + "reference": "ad510515b11ba5291fdd59b25d70227bfac2d7ab", "shasum": "" }, "require": { @@ -19761,7 +19886,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.9" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.10" }, "funding": [ { @@ -19777,20 +19902,20 @@ "type": "tidelift" } ], - "time": "2024-06-21T16:04:15+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "bcc806d1360991de3bf78ac5ca0202db85de9bfc" + "reference": "370c9f1e3567cd4670d44311838e37d16182c3a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/bcc806d1360991de3bf78ac5ca0202db85de9bfc", - "reference": "bcc806d1360991de3bf78ac5ca0202db85de9bfc", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/370c9f1e3567cd4670d44311838e37d16182c3a7", + "reference": "370c9f1e3567cd4670d44311838e37d16182c3a7", "shasum": "" }, "require": { @@ -19843,7 +19968,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.8" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.10" }, "funding": [ { @@ -19859,20 +19984,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-19T07:26:48+00:00" }, { "name": "symplify/easy-coding-standard", - "version": "12.3.1", + "version": "12.3.5", "source": { "type": "git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", - "reference": "bd670feae8d0b6da891d29a3c549bd0f4aa48711" + "reference": "0d7c2cfee3debdf11c12135e90d69d1d9f4eef03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/bd670feae8d0b6da891d29a3c549bd0f4aa48711", - "reference": "bd670feae8d0b6da891d29a3c549bd0f4aa48711", + "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/0d7c2cfee3debdf11c12135e90d69d1d9f4eef03", + "reference": "0d7c2cfee3debdf11c12135e90d69d1d9f4eef03", "shasum": "" }, "require": { @@ -19908,7 +20033,7 @@ ], "support": { "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", - "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.3.1" + "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.3.5" }, "funding": [ { @@ -19920,7 +20045,7 @@ "type": "github" } ], - "time": "2024-07-06T12:33:15+00:00" + "time": "2024-08-08T08:43:50+00:00" }, { "name": "theofidry/alice-data-fixtures", diff --git a/config/authentication.yaml b/config/authentication.yaml new file mode 100644 index 0000000000..b654857695 --- /dev/null +++ b/config/authentication.yaml @@ -0,0 +1,55 @@ +# authentication configuration for each Access URL +# Access URL Id / authentication method / params +parameters: + authentication: + default: + generic: + enabled: false + title: 'External' + client_id: '' + client_secret: '' + provider_options: + urlAuthorize: '' + urlAccessToken: '' + urlResourceOwnerDetails: '' + responseResourceOwnerId: 'sub' + # accessTokenMethod: 'POST' + # responseError: 'error' + # responseCode: '' + # scopeSeparator: ' ' + scopes: + - openid + allow_create_new_users: true + allow_update_user_info: false + resource_owner_username_field: null + resource_owner_firstname_field: null + resource_owner_lastname_field: null + resource_owner_email_field: null + resource_owner_status_field: null + resource_owner_teacher_status_field: null + resource_owner_sessadmin_status_field: null + resource_owner_hr_status_field: null + resource_owner_status_status_field: null + resource_owner_anon_status_field: null + resource_owner_urls_field: null + + facebook: + enabled: false + title: 'Facebook' + client_id: '' + client_secret: '' + graph_api_version: 'v20.0' + redirect_params: { } + + keycloak: + enabled: false + title: 'Keycloak' + client_id: '' + client_secret: '' + auth_server_url: '' + realm: '' + version: '' + encryption_algorithm: null + encryption_key_path: null + encryption_key: null + redirect_params: { } diff --git a/config/packages/knpu_oauth2_client.yaml b/config/packages/knpu_oauth2_client.yaml index 05e8533996..587858bc46 100644 --- a/config/packages/knpu_oauth2_client.yaml +++ b/config/packages/knpu_oauth2_client.yaml @@ -1,3 +1,27 @@ knpu_oauth2_client: clients: + generic: + type: generic + provider_class: League\OAuth2\Client\Provider\GenericProvider + client_id: '' + client_secret: '' + redirect_route: chamilo.oauth2_generic_check + + facebook: + type: facebook + client_id: '' + client_secret: '' + redirect_route: chamilo.oauth2_facebook_check + graph_api_version: '' + redirect_params: { } + + keycloak: + type: keycloak + client_id: '' + client_secret: '' + redirect_route: chamilo.oauth2_keycloak_check + redirect_params: { } + auth_server_url: null + realm: null + # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 4adfcabd9d..17642dff65 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -114,6 +114,11 @@ security: # username_path: security.credentials.login # password_path: security.credentials.password + custom_authenticators: + - Chamilo\CoreBundle\Security\Authenticator\OAuth2\GenericAuthenticator + - Chamilo\CoreBundle\Security\Authenticator\OAuth2\FacebookAuthenticator + - Chamilo\CoreBundle\Security\Authenticator\OAuth2\KeycloakAuthenticator + access_control: - {path: ^/login, roles: PUBLIC_ACCESS} - {path: ^/api/authentication_token, roles: PUBLIC_ACCESS} diff --git a/config/services.yaml b/config/services.yaml index 5f98b8f3e3..c59c8c1d4e 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -117,4 +117,5 @@ cocur_slugify: imports: - {resource: ../src/CoreBundle/Resources/config/services.yml} - {resource: ../src/LtiBundle/Resources/config/services.yml} + - { resource: ./authentication.yaml } - {resource: ./hosting_limits.yml} diff --git a/src/CoreBundle/Controller/OAuth2/AbstractProviderController.php b/src/CoreBundle/Controller/OAuth2/AbstractProviderController.php new file mode 100644 index 0000000000..584994490d --- /dev/null +++ b/src/CoreBundle/Controller/OAuth2/AbstractProviderController.php @@ -0,0 +1,27 @@ +isEnabled($providerName)) { + throw $this->createAccessDeniedException(); + } + + return $clientRegistry->getClient($providerName)->redirect(); + } +} diff --git a/src/CoreBundle/Controller/OAuth2/FacebookProviderController.php b/src/CoreBundle/Controller/OAuth2/FacebookProviderController.php new file mode 100644 index 0000000000..ef70f99215 --- /dev/null +++ b/src/CoreBundle/Controller/OAuth2/FacebookProviderController.php @@ -0,0 +1,26 @@ +getStartResponse('facebook', $clientRegistry, $authenticationConfigHelper); + } + + #[Route('/connect/facebook/check', name: 'chamilo.oauth2_facebook_check')] + public function connectCheck(): void {} +} diff --git a/src/CoreBundle/Controller/OAuth2/GenericProviderController.php b/src/CoreBundle/Controller/OAuth2/GenericProviderController.php new file mode 100644 index 0000000000..8b597231d6 --- /dev/null +++ b/src/CoreBundle/Controller/OAuth2/GenericProviderController.php @@ -0,0 +1,26 @@ +getStartResponse('generic', $clientRegistry, $authenticationConfigHelper); + } + + #[Route('/connect/generic/check', name: 'chamilo.oauth2_generic_check')] + public function connectCheck(): void {} +} diff --git a/src/CoreBundle/Controller/OAuth2/KeycloakProviderController.php b/src/CoreBundle/Controller/OAuth2/KeycloakProviderController.php new file mode 100644 index 0000000000..563f0b15ba --- /dev/null +++ b/src/CoreBundle/Controller/OAuth2/KeycloakProviderController.php @@ -0,0 +1,26 @@ +getStartResponse('keycloak', $clientRegistry, $authenticationConfigHelper); + } + + #[Route('/connect/keycloak/check', name: 'chamilo.oauth2_keycloak_check')] + public function connectCheck(): void {} +} diff --git a/src/CoreBundle/Controller/PlatformConfigurationController.php b/src/CoreBundle/Controller/PlatformConfigurationController.php index a7df6deca8..fe5d94ebe0 100644 --- a/src/CoreBundle/Controller/PlatformConfigurationController.php +++ b/src/CoreBundle/Controller/PlatformConfigurationController.php @@ -8,6 +8,7 @@ namespace Chamilo\CoreBundle\Controller; use bbb; use Chamilo\CoreBundle\Repository\Node\CourseRepository; +use Chamilo\CoreBundle\ServiceHelper\AuthenticationConfigHelper; use Chamilo\CoreBundle\ServiceHelper\ThemeHelper; use Chamilo\CoreBundle\ServiceHelper\TicketProjectHelper; use Chamilo\CoreBundle\ServiceHelper\UserHelper; @@ -29,6 +30,7 @@ class PlatformConfigurationController extends AbstractController private readonly TicketProjectHelper $ticketProjectHelper, private readonly UserHelper $userHelper, private readonly ThemeHelper $themeHelper, + private readonly AuthenticationConfigHelper $authenticationConfigHelper, ) {} #[Route('/list', name: 'platform_config_list', methods: ['GET'])] @@ -41,6 +43,7 @@ class PlatformConfigurationController extends AbstractController 'studentview' => $requestSession->get('studentview'), 'plugins' => [], 'visual_theme' => $this->themeHelper->getVisualTheme(), + 'external_authentication' => $this->authenticationConfigHelper->getEnabledProviders(), ]; $variables = []; diff --git a/src/CoreBundle/Decorator/OAuth2ProviderFactoryDecorator.php b/src/CoreBundle/Decorator/OAuth2ProviderFactoryDecorator.php new file mode 100644 index 0000000000..43727e4d4a --- /dev/null +++ b/src/CoreBundle/Decorator/OAuth2ProviderFactoryDecorator.php @@ -0,0 +1,54 @@ + $this->getProviderOptions('generic'), + Facebook::class => $this->getProviderOptions('facebook'), + Keycloak::class => $this->getProviderOptions('keycloak'), + }; + + return $this->inner->createProvider($class, $options, $redirectUri, $redirectParams, $collaborators); + } + + private function getProviderOptions(string $providerName): array + { + /** @var KnpUOAuth2ClientExtension $extension */ + $extension = (new KnpUOAuth2ClientBundle())->getContainerExtension(); + + $configParams = $this->authenticationConfigHelper->getParams($providerName); + + return $extension->getConfigurator($providerName)->getProviderOptions($configParams); + } +} diff --git a/src/CoreBundle/Repository/ExtraFieldRepository.php b/src/CoreBundle/Repository/ExtraFieldRepository.php index 910db8c868..37a60c5503 100644 --- a/src/CoreBundle/Repository/ExtraFieldRepository.php +++ b/src/CoreBundle/Repository/ExtraFieldRepository.php @@ -74,4 +74,9 @@ class ExtraFieldRepository extends ServiceEntityRepository return $fieldInfo; } + + public function findByVariable(int $itemType, string $variable): ?ExtraField + { + return $this->findOneBy(['variable' => $variable, 'itemType' => $itemType]); + } } diff --git a/src/CoreBundle/Repository/ExtraFieldValuesRepository.php b/src/CoreBundle/Repository/ExtraFieldValuesRepository.php index 899d449f49..1bd78d94bd 100644 --- a/src/CoreBundle/Repository/ExtraFieldValuesRepository.php +++ b/src/CoreBundle/Repository/ExtraFieldValuesRepository.php @@ -10,6 +10,7 @@ use Chamilo\CoreBundle\Entity\ExtraField; use Chamilo\CoreBundle\Entity\ExtraFieldItemInterface; use Chamilo\CoreBundle\Entity\ExtraFieldValues; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\Query\Expr\Join; use Doctrine\Persistence\ManagerRegistry; @@ -141,4 +142,42 @@ class ExtraFieldValuesRepository extends ServiceEntityRepository 'value' => $result->getFieldValue(), ]; } + + /** + * @return ExtraFieldValues|array|null + * + * @throws NonUniqueResultException + */ + public function findByVariableAndValue( + ExtraField $extraField, + string|int $value, + bool $last = false, + bool $all = false, + bool $useLike = false, + ): ExtraFieldValues|array|null { + $qb = $this->createQueryBuilder('s'); + + if ($useLike) { + $qb->andWhere($qb->expr()->like('s.fieldValue', ':value')); + $value = "%$value%"; + } else { + $qb->andWhere($qb->expr()->eq('s.fieldValue', ':value')); + } + + $query = $qb + ->andWhere( + $qb->expr()->eq('s.field', ':f') + ) + ->orderBy('s.itemId', $last ? 'DESC' : 'ASC') + ->setParameter('value', "$value") + ->setParameter('f', $extraField) + ->getQuery() + ; + + if ($all) { + return $query->getResult(); + } + + return $query->getOneOrNullResult(); + } } diff --git a/src/CoreBundle/Repository/Node/AccessUrlRepository.php b/src/CoreBundle/Repository/Node/AccessUrlRepository.php index 4bf3e7d579..717ccb9e20 100644 --- a/src/CoreBundle/Repository/Node/AccessUrlRepository.php +++ b/src/CoreBundle/Repository/Node/AccessUrlRepository.php @@ -7,9 +7,11 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\Repository\Node; use Chamilo\CoreBundle\Entity\AccessUrl; +use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\Repository\ResourceRepository; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; class AccessUrlRepository extends ResourceRepository @@ -36,4 +38,21 @@ class AccessUrlRepository extends ResourceRepository return 0; } } + + /** + * @return array + */ + public function findByUser(User $user): array + { + /** @var QueryBuilder $qb */ + $qb = $this->createQueryBuilder('url'); + + return $qb + ->join('url.users', 'users') + ->where($qb->expr()->eq('users.user', ':user')) + ->setParameter('user', $user->getId()) + ->getQuery() + ->getResult() + ; + } } diff --git a/src/CoreBundle/Security/Authenticator/OAuth2/AbstractAuthenticator.php b/src/CoreBundle/Security/Authenticator/OAuth2/AbstractAuthenticator.php new file mode 100644 index 0000000000..686b50fa98 --- /dev/null +++ b/src/CoreBundle/Security/Authenticator/OAuth2/AbstractAuthenticator.php @@ -0,0 +1,85 @@ +client = $this->clientRegistry->getClient($this->providerName); + } + + public function start(Request $request, ?AuthenticationException $authException = null): Response + { + $targetUrl = $this->router->generate('login'); + + return new RedirectResponse($targetUrl); + } + + abstract public function supports(Request $request): ?bool; + + public function authenticate(Request $request): Passport + { + /** @var AccessToken $accessToken */ + $accessToken = $this->fetchAccessToken($this->client); + + $user = $this->userLoader($accessToken); + + return new SelfValidatingPassport( + new UserBadge( + $user->getUserIdentifier() + ), + ); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + $targetUrl = $this->router->generate('index'); + + return new RedirectResponse($targetUrl); + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response + { + $message = strtr($exception->getMessage(), $exception->getMessageData()); + + return new Response($message, Response::HTTP_FORBIDDEN); + } + + /** + * Find or create and save the new user. + */ + abstract protected function userLoader(AccessToken $accessToken): User; +} diff --git a/src/CoreBundle/Security/Authenticator/OAuth2/FacebookAuthenticator.php b/src/CoreBundle/Security/Authenticator/OAuth2/FacebookAuthenticator.php new file mode 100644 index 0000000000..5ad4c25468 --- /dev/null +++ b/src/CoreBundle/Security/Authenticator/OAuth2/FacebookAuthenticator.php @@ -0,0 +1,83 @@ +attributes->get('_route'); + } + + protected function userLoader(AccessToken $accessToken): User + { + /** @var FacebookUser $resourceOwner */ + $resourceOwner = $this->client->fetchUserFromToken($accessToken); + + $user = $this->userRepository->findOneBy(['email' => $resourceOwner->getEmail()]); + + if (!$user) { + $user = (new User()) + ->setCreatorId($this->userRepository->getRootUser()->getId()) + ; + } + + $user + ->setFirstname($resourceOwner->getFirstName()) + ->setLastname($resourceOwner->getLastName()) + // ->setLocale($resourceOwner->getLocale()) + ->setEmail($resourceOwner->getEmail()) + ->setUsername($this->changeToValidChamiloLogin($resourceOwner->getEmail())) + ->setPlainPassword('facebook') + ->setStatus(STUDENT) + ->setAuthSource('facebook') + ->setRoleFromStatus(STUDENT) + ; + + $this->userRepository->updateUser($user); + + $url = $this->urlHelper->getCurrent(); + $url->addUser($user); + + return $user; + } + + private function changeToValidChamiloLogin(string $email): string + { + return $this->slugify->slugify($email); + } +} diff --git a/src/CoreBundle/Security/Authenticator/OAuth2/GenericAuthenticator.php b/src/CoreBundle/Security/Authenticator/OAuth2/GenericAuthenticator.php new file mode 100644 index 0000000000..28ab0944a1 --- /dev/null +++ b/src/CoreBundle/Security/Authenticator/OAuth2/GenericAuthenticator.php @@ -0,0 +1,273 @@ +attributes->get('_route'); + } + + protected function userLoader(AccessToken $accessToken): User + { + $providerParams = $this->authenticationConfigHelper->getParams('generic'); + + /** @var GenericResourceOwner $resourceOwner */ + $resourceOwner = $this->client->fetchUserFromToken($accessToken); + $resourceOwnerData = $resourceOwner->toArray(); + $resourceOwnerId = $resourceOwner->getId(); + + if (empty($resourceOwnerId)) { + throw new UnexpectedValueException('Value for the resource owner identifier not found at the configured key'); + } + + $fieldType = (int) ExtraField::getExtraFieldTypeFromString('user'); + $extraField = $this->extraFieldRepository->findByVariable($fieldType, self::EXTRA_FIELD_OAUTH2_ID); + + $existingUserExtraFieldValue = $this->extraFieldValuesRepository->findByVariableAndValue( + $extraField, + $resourceOwnerId + ); + + if (null === $existingUserExtraFieldValue) { + $username = $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_username_field'], + "oauth2user_$resourceOwnerId" + ); + + /** @var User $user */ + $user = $this->userRepository->findOneBy(['username' => $username]); + + if (!$user || 'platform' !== $user->getAuthSource()) { + if (!$providerParams['allow_create_new_users']) { + throw new AuthenticationException('This user doesn\'t have an account yet and auto-provisioning is not enabled. Please contact this portal administration team to request access.'); + } + + // set default values, real values are set in self::updateUserInfo method + $user = (new User()) + ->setFirstname('OAuth2 User default firstname') + ->setLastname('OAuth2 User default firstname') + ->setEmail('oauth2user_'.$resourceOwnerId.'@'.(gethostname() ?: 'localhost')) + ->setUsername($username) + ->setPlainPassword($username) + ->setStatus(STUDENT) + ->setCreatorId($this->userRepository->getRootUser()->getId()) + ; + } + + $this->saveUserInfo($user, $resourceOwnerData, $providerParams); + + $this->extraFieldValuesRepository->updateItemData( + $extraField, + $user, + $resourceOwnerId + ); + + $this->updateUrls($user, $resourceOwnerData, $providerParams); + } else { + /** @var User $user */ + $user = $this->userRepository->find( + $existingUserExtraFieldValue->getItemId() + ); + + if ($providerParams['allow_update_user_info']) { + $this->saveUserInfo($user, $resourceOwnerData, $providerParams); + + $this->updateUrls($user, $resourceOwnerData, $providerParams); + } + } + + return $user; + } + + /** + * Set user information from the resource owner's data or the user itself. + */ + public function saveUserInfo(User $user, array $resourceOwnerData, array $providerParams): void + { + $status = $this->getUserStatus($resourceOwnerData, $user->getStatus(), $providerParams); + + $user + ->setFirstname( + $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_firstname_field'], + $user->getFirstname() + ) + ) + ->setLastname( + $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_lastname_field'], + $user->getLastname() + ) + ) + ->setUsername( + $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_username_field'], + $user->getUsername() + ) + ) + ->setEmail( + $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_email_field'], + $user->getEmail() + ) + ) + ->setAuthSource('oauth2') + ->setStatus($status) + ->setRoleFromStatus($status) + ; + + $this->userRepository->updateUser($user); + + $url = $this->urlHelper->getCurrent(); + $url->addUser($user); + } + + private function getUserStatus(array $resourceOwnerData, int $defaultStatus, array $providerParams): int + { + $status = $this->getValueByKey( + $resourceOwnerData, + $providerParams['resource_owner_status_field'], + $defaultStatus + ); + + $responseStatus = []; + + if ($teacherStatus = $providerParams['resource_owner_teacher_status_field']) { + $responseStatus[COURSEMANAGER] = $teacherStatus; + } + + if ($sessAdminStatus = $providerParams['resource_owner_sessadmin_status_field']) { + $responseStatus[SESSIONADMIN] = $sessAdminStatus; + } + + if ($drhStatus = $providerParams['resource_owner_hr_status_field']) { + $responseStatus[DRH] = $drhStatus; + } + + if ($studentStatus = $providerParams['resource_owner_status_status_field']) { + $responseStatus[STUDENT] = $studentStatus; + } + + if ($anonStatus = $providerParams['resource_owner_anon_status_field']) { + $responseStatus[ANONYMOUS] = $anonStatus; + } + + $map = array_flip($responseStatus); + + return $map[$status] ?? $status; + } + + private function updateUrls(User $user, array $resourceOwnerData, array $providerParams): void + { + if (!($urlsField = $providerParams['resource_owner_urls_field'])) { + return; + } + + $availableUrls = []; + + $urls = $this->accessUrlRepository->findAll(); + + /** @var AccessUrl $existingUrl */ + foreach ($urls as $existingUrl) { + $availableUrls[(string) $existingUrl->getId()] = $existingUrl->getId(); + $availableUrls[$existingUrl->getUrl()] = $existingUrl->getId(); + } + + $allowedUrlIds = []; + + foreach ($this->getValueByKey($resourceOwnerData, $urlsField) as $value) { + if (array_key_exists($value, $availableUrls)) { + $allowedUrlIds[] = $availableUrls[$value]; + } else { + $newValue = ($value[-1] === '/') ? substr($value, 0, -1) : $value.'/'; + + if (array_key_exists($newValue, $availableUrls)) { + $allowedUrlIds[] = $availableUrls[$newValue]; + } + } + } + + $grantedUrlIds = []; + + foreach ($this->accessUrlRepository->findByUser($user) as $grantedUrl) { + $grantedUrlIds[] = $grantedUrl->getId(); + } + + $urlRelUserRepo = $this->entityManager->getRepository(AccessUrlRelUser::class); + + foreach (array_diff($grantedUrlIds, $allowedUrlIds) as $extraUrlId) { + $urlRelUser = $urlRelUserRepo->findOneBy(['user' => $user, 'url' => $extraUrlId]); + + if ($urlRelUser) { + $this->entityManager->remove($urlRelUser); + } + } + + $this->entityManager->flush(); + + foreach (array_diff($allowedUrlIds, $grantedUrlIds) as $missingUrlId) { + /** @var AccessUrl $missingUrl */ + $missingUrl = $this->accessUrlRepository->find($missingUrlId); + $missingUrl->addUser($user); + } + + $this->entityManager->flush(); + } +} diff --git a/src/CoreBundle/Security/Authenticator/OAuth2/KeycloakAuthenticator.php b/src/CoreBundle/Security/Authenticator/OAuth2/KeycloakAuthenticator.php new file mode 100644 index 0000000000..35d1ae9ecf --- /dev/null +++ b/src/CoreBundle/Security/Authenticator/OAuth2/KeycloakAuthenticator.php @@ -0,0 +1,58 @@ +attributes->get('_route'); + } + + protected function userLoader(AccessToken $accessToken): User + { + /** @var KeycloakResourceOwner $resourceOwner */ + $resourceOwner = $this->client->fetchUserFromToken($accessToken); + + $user = $this->userRepository->findOneBy(['username' => $resourceOwner->getUsername()]) + ?: + $this->userRepository->findOneBy(['username' => $resourceOwner->getId()]); + + if (!$user) { + $user = (new User()) + ->setCreatorId($this->userRepository->getRootUser()->getId()) + ; + } + + $username = $resourceOwner->getUsername() ?: $resourceOwner->getId(); + + $user + ->setFirstname($resourceOwner->getFirstName()) + ->setLastname($resourceOwner->getLastName()) + ->setEmail($resourceOwner->getEmail()) + ->setUsername($username) + ->setPlainPassword('keycloak') + ->setStatus(STUDENT) + ->setAuthSource('keycloak') + ->setRoleFromStatus(STUDENT) + ; + + $this->userRepository->updateUser($user); + + $url = $this->urlHelper->getCurrent(); + $url->addUser($user); + + return $user; + } +} diff --git a/src/CoreBundle/ServiceHelper/AuthenticationConfigHelper.php b/src/CoreBundle/ServiceHelper/AuthenticationConfigHelper.php new file mode 100644 index 0000000000..8dbc1a09fa --- /dev/null +++ b/src/CoreBundle/ServiceHelper/AuthenticationConfigHelper.php @@ -0,0 +1,77 @@ +getProvidersForUrl($url); + + if (!isset($providers[$providerName])) { + throw new InvalidArgumentException('Invalid authentication provider for access URL'); + } + + return $providers[$providerName]; + } + + public function isEnabled(string $methodName, ?AccessUrl $url = null): bool + { + $configParams = $this->getParams($methodName, $url); + + return $configParams['enabled'] ?? false; + } + + public function getEnabledProviders(?AccessUrl $url = null): array + { + $urlProviders = $this->getProvidersForUrl($url); + + $enabledProviders = []; + + foreach ($urlProviders as $providerName => $providerParams) { + if ($providerParams['enabled'] ?? false) { + $enabledProviders[] = [ + 'name' => $providerName, + 'title' => $providerParams['title'] ?? u($providerName)->title(), + 'url' => $this->urlGenerator->generate("chamilo.oauth2_{$providerName}_start"), + ]; + } + } + + return $enabledProviders; + } + + private function getProvidersForUrl(?AccessUrl $url): array + { + $urlId = $url ? $url->getId() : $this->urlHelper->getCurrent()->getId(); + + $authentication = $this->parameterBag->get('authentication'); + + if (isset($authentication[$urlId])) { + return $authentication[$urlId]; + } + + if (isset($authentication['default'])) { + return $authentication['default']; + } + + throw new InvalidArgumentException('Invalid access URL configuration'); + } +}