Compare commits
2 commits
37d8413118
...
400253e04b
Author | SHA1 | Date | |
---|---|---|---|
400253e04b | |||
01c60e3027 |
5 changed files with 582 additions and 238 deletions
163
composer.lock
generated
163
composer.lock
generated
|
@ -89,16 +89,16 @@
|
|||
},
|
||||
{
|
||||
"name": "chillerlan/php-settings-container",
|
||||
"version": "3.2.0",
|
||||
"version": "3.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/chillerlan/php-settings-container.git",
|
||||
"reference": "8f93648fac8e6bacac8e00a8d325eba4950295e6"
|
||||
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/8f93648fac8e6bacac8e00a8d325eba4950295e6",
|
||||
"reference": "8f93648fac8e6bacac8e00a8d325eba4950295e6",
|
||||
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
|
||||
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -106,15 +106,16 @@
|
|||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phan/phan": "^5.4",
|
||||
"phpmd/phpmd": "^2.15",
|
||||
"phpstan/phpstan": "^1.11",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.2",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
"squizlabs/php_codesniffer": "^3.10"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"chillerlan\\Settings\\": "src/"
|
||||
"chillerlan\\Settings\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
|
@ -150,7 +151,7 @@
|
|||
"type": "ko_fi"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-02T20:07:15+00:00"
|
||||
"time": "2024-07-16T11:13:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
|
@ -1132,16 +1133,16 @@
|
|||
},
|
||||
{
|
||||
"name": "sentry/sentry",
|
||||
"version": "4.7.0",
|
||||
"version": "4.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-php.git",
|
||||
"reference": "d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f"
|
||||
"reference": "61770efd8b7888e0bdd7d234f0ba67b066e47d04"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f",
|
||||
"reference": "d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/61770efd8b7888e0bdd7d234f0ba67b066e47d04",
|
||||
"reference": "61770efd8b7888e0bdd7d234f0ba67b066e47d04",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1205,7 +1206,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-php/issues",
|
||||
"source": "https://github.com/getsentry/sentry-php/tree/4.7.0"
|
||||
"source": "https://github.com/getsentry/sentry-php/tree/4.8.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1217,7 +1218,7 @@
|
|||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-10T13:22:13+00:00"
|
||||
"time": "2024-07-16T13:45:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
|
@ -1288,16 +1289,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v7.1.0",
|
||||
"version": "v7.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "522d2772d6c7bab843b0c52466dc7844622bacc2"
|
||||
"reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/522d2772d6c7bab843b0c52466dc7844622bacc2",
|
||||
"reference": "522d2772d6c7bab843b0c52466dc7844622bacc2",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
|
||||
"reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1348,7 +1349,7 @@
|
|||
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v7.1.0"
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1364,7 +1365,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
|
@ -1444,16 +1445,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"version": "v6.4.8",
|
||||
"version": "v6.4.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mailer.git",
|
||||
"reference": "76326421d44c07f7824b19487cfbf87870b37efc"
|
||||
"reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/76326421d44c07f7824b19487cfbf87870b37efc",
|
||||
"reference": "76326421d44c07f7824b19487cfbf87870b37efc",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45",
|
||||
"reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1504,7 +1505,7 @@
|
|||
"description": "Helps sending emails",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mailer/tree/v6.4.8"
|
||||
"source": "https://github.com/symfony/mailer/tree/v6.4.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1520,20 +1521,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T14:49:08+00:00"
|
||||
"time": "2024-06-28T07:59:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
"version": "v7.1.0",
|
||||
"version": "v7.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mime.git",
|
||||
"reference": "92d6b9b1217eebff2035577db505b7e1435ca78c"
|
||||
"reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/92d6b9b1217eebff2035577db505b7e1435ca78c",
|
||||
"reference": "92d6b9b1217eebff2035577db505b7e1435ca78c",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/26a00b85477e69a4bab63b66c5dce64f18b0cbfc",
|
||||
"reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1546,7 +1547,7 @@
|
|||
"phpdocumentor/reflection-docblock": "<3.2.2",
|
||||
"phpdocumentor/type-resolver": "<1.4.0",
|
||||
"symfony/mailer": "<6.4",
|
||||
"symfony/serializer": "<6.4"
|
||||
"symfony/serializer": "<6.4.3|>7.0,<7.0.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"egulias/email-validator": "^2.1.10|^3.1|^4",
|
||||
|
@ -1556,7 +1557,7 @@
|
|||
"symfony/process": "^6.4|^7.0",
|
||||
"symfony/property-access": "^6.4|^7.0",
|
||||
"symfony/property-info": "^6.4|^7.0",
|
||||
"symfony/serializer": "^6.4|^7.0"
|
||||
"symfony/serializer": "^6.4.3|^7.0.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -1588,7 +1589,7 @@
|
|||
"mime-type"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mime/tree/v7.1.0"
|
||||
"source": "https://github.com/symfony/mime/tree/v7.1.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1604,20 +1605,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-29T15:16:11+00:00"
|
||||
"time": "2024-06-28T10:03:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v7.1.0",
|
||||
"version": "v7.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "9564f64c16f99e29f252eafc642965e8fcb755ce"
|
||||
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/9564f64c16f99e29f252eafc642965e8fcb755ce",
|
||||
"reference": "9564f64c16f99e29f252eafc642965e8fcb755ce",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
|
||||
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1655,7 +1656,7 @@
|
|||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.1.0"
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1671,20 +1672,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1734,7 +1735,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1750,20 +1751,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919"
|
||||
"reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919",
|
||||
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c",
|
||||
"reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1818,7 +1819,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1834,20 +1835,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
|
||||
"reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
|
||||
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb",
|
||||
"reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1899,7 +1900,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1915,20 +1916,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1979,7 +1980,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1995,20 +1996,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-06-19T12:30:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25"
|
||||
"reference": "10112722600777e02d2745716b70c5db4ca70442"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25",
|
||||
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442",
|
||||
"reference": "10112722600777e02d2745716b70c5db4ca70442",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2052,7 +2053,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2068,20 +2069,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-06-19T12:30:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
|
||||
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2132,7 +2133,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2148,7 +2149,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
|
@ -2384,16 +2385,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.11.3",
|
||||
"version": "1.11.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5"
|
||||
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e64220a05c1209fc856d58e789c3b7a32c0bb9a5",
|
||||
"reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/52d2bbfdcae7f895915629e4694e9497d0f8e28d",
|
||||
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2438,7 +2439,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T13:53:37+00:00"
|
||||
"time": "2024-07-06T11:17:41+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
424
package-lock.json
generated
424
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
src/CSRF.php
12
src/CSRF.php
|
@ -5,9 +5,19 @@ use Index\Security\CSRFP;
|
|||
|
||||
final class CSRF {
|
||||
private static CSRFP $instance;
|
||||
private static string $secretKey = '';
|
||||
|
||||
public static function create(string $identity, ?string $secretKey = null): CSRFP {
|
||||
if($secretKey === null)
|
||||
$secretKey = self::$secretKey;
|
||||
else
|
||||
self::$secretKey = $secretKey;
|
||||
|
||||
return new CSRFP($secretKey, $identity);
|
||||
}
|
||||
|
||||
public static function init(string $secretKey, string $identity): void {
|
||||
self::$instance = new CSRFP($secretKey, $identity);
|
||||
self::$instance = self::create($identity, $secretKey);
|
||||
}
|
||||
|
||||
public static function validate(string $token, int $tolerance = -1): bool {
|
||||
|
|
212
src/Hanyuu/HanyuuRoutes.php
Normal file
212
src/Hanyuu/HanyuuRoutes.php
Normal file
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
namespace Misuzu\Hanyuu;
|
||||
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler};
|
||||
use Index\Serialisation\UriBase64;
|
||||
use Syokuhou\IConfig;
|
||||
use Misuzu\CSRF;
|
||||
use Misuzu\Auth\{AuthContext,AuthInfo,Sessions};
|
||||
use Misuzu\URLs\URLRegistry;
|
||||
use Misuzu\Users\{UsersContext,UserInfo};
|
||||
|
||||
final class HanyuuRoutes extends RouteHandler {
|
||||
public function __construct(
|
||||
private IConfig $config,
|
||||
private IConfig $impersonateConfig, // this sucks lol
|
||||
private URLRegistry $urls,
|
||||
private UsersContext $usersCtx,
|
||||
private AuthContext $authCtx,
|
||||
private AuthInfo $authInfo
|
||||
) {}
|
||||
|
||||
private function getEndpoint(): string {
|
||||
return $this->config->getString('endpoint');
|
||||
}
|
||||
|
||||
private function getSecret(): string {
|
||||
return $this->config->getString('secret');
|
||||
}
|
||||
|
||||
private static function createPayload(string $name, array $attrs = []): object {
|
||||
$payload = new stdClass;
|
||||
$payload->name = $name;
|
||||
$payload->attrs = [];
|
||||
foreach($attrs as $name => $value) {
|
||||
if($value === null)
|
||||
continue;
|
||||
$payload->attrs[(string)$name] = $value;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private static function createErrorPayload(string $code, ?string $text = null): object {
|
||||
$attrs = ['code' => $code];
|
||||
if($text !== null && $text !== '')
|
||||
$attrs['text'] = $text;
|
||||
|
||||
return self::createPayload('error', $attrs);
|
||||
}
|
||||
|
||||
#[HttpMiddleware('/_hanyuu/(.*)')]
|
||||
public function verifyRequest($response, $request, string $action) {
|
||||
$userTime = (int)$request->getHeaderLine('X-Hanyuu-Timestamp');
|
||||
$userHash = UriBase64::decode((string)$request->getHeaderLine('X-Hanyuu-Signature'));
|
||||
|
||||
$currentTime = time();
|
||||
if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60)
|
||||
return self::createErrorPayload('verification', 'Request verification failed.');
|
||||
|
||||
$url = sprintf('%s/_hanyuu/%s', $this->getEndpoint(), $action);
|
||||
$params = $request->getParamString();
|
||||
if($params !== '')
|
||||
$url .= sprintf('?%s', $params);
|
||||
|
||||
if($request->getMethod() === 'POST') {
|
||||
if(!$request->isFormContent())
|
||||
return self::createErrorPayload('request', 'Request body is not in expect format.');
|
||||
|
||||
$body = $request->getContent()->getParamString();
|
||||
} elseif($request->getMethod() !== 'GET') {
|
||||
return self::createErrorPayload('request', 'Only GET and POST methods are allowed.');
|
||||
} else {
|
||||
$body = '';
|
||||
}
|
||||
|
||||
$verifyHash = hash_hmac(
|
||||
'sha256',
|
||||
sprintf('[%s|%s|%s]', $userTime, $url, $body),
|
||||
$this->getSecret(),
|
||||
true
|
||||
);
|
||||
|
||||
if(!hash_equals($verifyHash, $userHash))
|
||||
return self::createErrorPayload('verification', 'Request verification failed.');
|
||||
}
|
||||
|
||||
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
|
||||
if($impersonator->isSuperUser())
|
||||
return true;
|
||||
|
||||
return in_array(
|
||||
$targetId,
|
||||
$this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->getId())),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[HttpPost('/_hanyuu/auth-check')]
|
||||
public function postCheckAuth($response, $request) {
|
||||
$content = $request->getContent();
|
||||
$method = (string)$content->getParam('method');
|
||||
if($method !== 'Misuzu')
|
||||
return self::createErrorPayload('auth:check:method', 'Requested auth method is not supported.');
|
||||
|
||||
$remoteAddr = (string)$content->getParam('remote_addr');
|
||||
if(filter_var($remoteAddr, FILTER_VALIDATE_IP) === false)
|
||||
return self::createErrorPayload('auth:check:remote_addr', 'Provided remote address is not in a valid format.');
|
||||
|
||||
$avatarResolutions = trim((string)$content->getParam('avatars'));
|
||||
if($avatarResolutions === '') {
|
||||
$avatarResolutions = [];
|
||||
} else {
|
||||
$avatarResolutions = explode(',', $avatarResolutions);
|
||||
foreach($avatarResolutions as $key => $avatarRes) {
|
||||
if(!ctype_digit($avatarRes))
|
||||
return self::createErrorPayload('auth:check:avatars', 'Avatar resolution set must be a comma separated set of numbers or empty.');
|
||||
$avatarResolutions[$key] = (int)$avatarRes;
|
||||
}
|
||||
$avatarResolutions = array_unique($avatarResolutions);
|
||||
}
|
||||
|
||||
$loginUrl = $this->getEndpoint() . $this->urls->format('auth-login');
|
||||
$registerUrl = $this->getEndpoint() . $this->urls->format('auth-register');
|
||||
|
||||
$tokenPacker = $this->authCtx->createAuthTokenPacker();
|
||||
$tokenInfo = $tokenPacker->unpack(trim((string)$content->getParam('token')));
|
||||
if($tokenInfo->isEmpty())
|
||||
return self::createPayload('auth:check:fail', ['reason' => 'empty', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
|
||||
|
||||
$sessions = $this->authCtx->getSessions();
|
||||
try {
|
||||
$sessionInfo = $sessions->getSession(sessionToken: $tokenInfo->getSessionToken());
|
||||
} catch(RuntimeException $ex) {
|
||||
return self::createPayload('auth:check:fail', ['reason' => 'session', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
|
||||
}
|
||||
|
||||
if($sessionInfo->hasExpired()) {
|
||||
$sessions->deleteSessions(sessionInfos: $sessionInfo);
|
||||
return self::createPayload('auth:check:fail', ['reason' => 'expired', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
|
||||
}
|
||||
|
||||
$sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
|
||||
|
||||
$users = $this->usersCtx->getUsers();
|
||||
$userInfo = $userInfoReal = $users->getUser($sessionInfo->getUserId(), 'id');
|
||||
if($tokenInfo->hasImpersonatedUserId() && $this->canImpersonateUserId($userInfo, $tokenInfo->getImpersonatedUserId())) {
|
||||
try {
|
||||
$userInfo = $users->getUser($tokenInfo->getImpersonatedUserId(), 'id');
|
||||
} catch(RuntimeException $ex) {
|
||||
$userInfo = $userInfoReal;
|
||||
}
|
||||
}
|
||||
|
||||
$response = [];
|
||||
$response['session'] = [
|
||||
'created_at' => $sessionInfo->getCreatedTime(),
|
||||
'expires_at' => $sessionInfo->getExpiresTime(),
|
||||
'lifetime_extends' => $sessionInfo->shouldBumpExpires(),
|
||||
];
|
||||
|
||||
$banInfo = $this->usersCtx->tryGetActiveBan($userInfo);
|
||||
if($banInfo !== null)
|
||||
$response['ban'] = [
|
||||
'severity' => $banInfo->getSeverity(),
|
||||
'reason' => $banInfo->getPublicReason(),
|
||||
'created_at' => $banInfo->getCreatedTime(),
|
||||
'is_permanent' => $banInfo->isPermanent(),
|
||||
'expires_at' => $banInfo->getExpiresTime(),
|
||||
'duration_str' => $banInfo->getDurationString(),
|
||||
'remaining_str' => $banInfo->getRemainingString(),
|
||||
];
|
||||
|
||||
$gatherRequestedAvatars = function($userInfo) use ($avatarResolutions) {
|
||||
$formatAvatarUrl = fn($res = 0) => (
|
||||
$this->getEndpoint() . $this->urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => $res])
|
||||
);
|
||||
|
||||
$avatars = ['original' => $formatAvatarUrl()];
|
||||
|
||||
foreach($avatarResolutions as $avatarRes)
|
||||
$avatars[sprintf('x%d', $avatarRes)] = $formatAvatarUrl($avatarRes);
|
||||
|
||||
return $avatars;
|
||||
};
|
||||
|
||||
$extractUserInfo = fn($userInfo) => [
|
||||
'id' => $userInfo->getId(),
|
||||
'name' => $userInfo->getName(),
|
||||
'colour' => (string)$users->getUserColour($userInfo),
|
||||
'rank' => $users->getUserRank($userInfo),
|
||||
'is_super' => $userInfo->isSuperUser(),
|
||||
'country_code' => $userInfo->getCountryCode(),
|
||||
'is_deleted' => $userInfo->isDeleted(),
|
||||
'has_totp' => $userInfo->hasTOTPKey(),
|
||||
'profile_url' => $this->getEndpoint() . $this->urls->format('user-profile', ['user' => $userInfo->getId()]),
|
||||
'avatars' => $gatherRequestedAvatars($userInfo),
|
||||
];
|
||||
|
||||
$response['user'] = $extractUserInfo($userInfo);
|
||||
if($userInfo !== $userInfoReal) {
|
||||
$response['guise'] = $extractUserInfo($userInfoReal);
|
||||
|
||||
$csrfp = CSRF::create($sessionInfo->getToken());
|
||||
$response['guise']['revert_url'] = $this->getEndpoint() . $this->urls->format('auth-revert', ['csrf' => $csrfp->createToken()]);
|
||||
}
|
||||
|
||||
return self::createPayload('auth:check:success', $response);
|
||||
}
|
||||
}
|
|
@ -278,6 +278,15 @@ class MisuzuContext {
|
|||
$this->profileFields
|
||||
));
|
||||
|
||||
$routingCtx->register(new \Misuzu\Hanyuu\HanyuuRoutes(
|
||||
$this->config->scopeTo('hanyuu'),
|
||||
$this->config->scopeTo('impersonate'),
|
||||
$this->urls,
|
||||
$this->usersCtx,
|
||||
$this->authCtx,
|
||||
$this->authInfo
|
||||
));
|
||||
|
||||
$routingCtx->register(new LegacyRoutes($this->urls));
|
||||
|
||||
return $routingCtx;
|
||||
|
|
Loading…
Reference in a new issue