From ed58b9441904764a9f1e59db981d87ecfa6632bd Mon Sep 17 00:00:00 2001 From: Derek Myers Date: Fri, 25 Nov 2022 13:15:59 -0600 Subject: [PATCH 01/58] Added new ability to override resource query builder (#32) --- src/Http/Controllers/ResourceController.php | 4 +-- src/ResourceType.php | 6 +++++ src/SCIMConfig.php | 3 +++ src/ServiceProvider.php | 30 ++++++++++----------- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 969d015c..fce42203 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -343,7 +343,7 @@ public function wrongVersion(Request $request) public function index(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType) { - $class = $resourceType->getClass(); + $query = $resourceType->getQuery(); // The 1-based index of the first query result. A value less than 1 SHALL be interpreted as 1. $startIndex = max(1, intVal($request->input('startIndex', 0))); @@ -357,7 +357,7 @@ public function index(Request $request, PolicyDecisionPoint $pdp, ResourceType $ $sortBy = Helper::getEloquentSortAttribute($resourceType, $request->input('sortBy')); } - $resourceObjectsBase = $class::when( + $resourceObjectsBase = $query->when( $filter = $request->input('filter'), function ($query) use ($filter, $resourceType) { $parser = new Parser(Mode::FILTER()); diff --git a/src/ResourceType.php b/src/ResourceType.php index 36e6b5a8..e90d0966 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -2,6 +2,7 @@ namespace ArieTimmerman\Laravel\SCIMServer; +use Illuminate\Support\Arr; use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; class ResourceType @@ -40,6 +41,11 @@ public function getClass() return $this->configuration['class']; } + public function getQuery() + { + return Arr::get($this->configuration, 'query', $this->getClass()::query()); + } + public function getValidations() { return $this->configuration['validations']; diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index 5fe66825..ac962c0c 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -25,6 +25,9 @@ public function getUserConfig() // Set to 'null' to make use of auth.providers.users.model (App\User::class) 'class' => Helper::getAuthUserClass(), + // Set to 'null' to make use of $class::query() + 'query' => null, + 'validations' => [ 'urn:ietf:params:scim:schemas:core:2.0:User:userName' => 'required', diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 6f43a3e9..4b9348f6 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -13,7 +13,7 @@ class ServiceProvider extends \Illuminate\Support\ServiceProvider public function boot(\Illuminate\Routing\Router $router) { $this->loadMigrationsFrom(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations'); - + $this->publishes([ __DIR__.'/../config/scim.php' => config_path('scim.php'), ], 'laravel-scim'); @@ -25,53 +25,53 @@ public function boot(\Illuminate\Routing\Router $router) 'resourceType', function ($name, $route) { $config = resolve(SCIMConfig::class)->getConfigForResource($name); - + if ($config == null) { throw (new SCIMException(sprintf('No resource "%s" found.', $name)))->setCode(404); } - + return new ResourceType($name, $config); } ); - + $router->bind( 'resourceObject', function ($id, $route) { $resourceType = $route->parameter('resourceType'); - + if (!$resourceType) { throw (new SCIMException('ResourceType not provided'))->setCode(404); } - - $class = $resourceType->getClass(); - - $resourceObject = $class::with($resourceType->getWithRelations())->find($id); - + + $query = $resourceType->getQuery(); + + $resourceObject = $query->with($resourceType->getWithRelations())->find($id); + if ($resourceObject == null) { throw (new SCIMException(sprintf('Resource "%s" not found', $id)))->setCode(404); } - + if (($matchIf = \request()->header('IF-Match'))) { $versionsAllowed = preg_split('/\s*,\s*/', $matchIf); $currentVersion = Helper::getResourceObjectVersion($resourceObject); - + //if as version is '*' it is always ok if (!in_array($currentVersion, $versionsAllowed) && !in_array('*', $versionsAllowed)) { throw (new SCIMException('Failed to update. Resource changed on the server.'))->setCode(412); } } - + return $resourceObject; } ); - + $router->middleware('SCIMHeaders', 'ArieTimmerman\Laravel\SCIMServer\Middleware\SCIMHeaders'); if (config('scim.publish_routes')) { \ArieTimmerman\Laravel\SCIMServer\RouteProvider::routes(); } } - + /** * Register the service provider. * From a49bcc9fb19501253e52cefe4237f22d25ea6936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon-Harald=20Br=C3=A5then?= Date: Wed, 4 Jan 2023 10:29:31 +0100 Subject: [PATCH 02/58] Update ResourceType.php (#35) Fixed resource default query when not set in config. --- src/ResourceType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ResourceType.php b/src/ResourceType.php index e90d0966..b9a8423f 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -43,7 +43,7 @@ public function getClass() public function getQuery() { - return Arr::get($this->configuration, 'query', $this->getClass()::query()); + return Arr::get($this->configuration, 'query') ?? $this->getClass()::query(); } public function getValidations() From d235efb87bb16494a1fa7102187471fb8e96382e Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 8 Feb 2023 07:34:25 +0100 Subject: [PATCH 03/58] Make defining the schema optional in the mapping (#37) * Defining schema in the mapping is now optional as it is also defined already for the resource type * set phpunit version to 9.5 --- .github/workflows/ci.yml | 1 + src/ResourceType.php | 34 ++++++++++++++++++++-------------- src/SCIMConfig.php | 12 ++++-------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d83f01af..44f6527f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,3 +14,4 @@ jobs: - uses: php-actions/phpunit@v3 with: php_version: 8 + version: 9.5 diff --git a/src/ResourceType.php b/src/ResourceType.php index b9a8423f..159d6116 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -8,34 +8,40 @@ class ResourceType { protected $configuration = null; - + protected $name = null; - + public function __construct($name, $configuration) { $this->configuration = $configuration; } - + public function getConfiguration() { return $this->configuration; } - + public function getMapping() { - return AttributeMapping::object($this->configuration['mapping'] ?? [])->setDefaultSchema($this->configuration['schema']); + + $mapping = $this->configuration['mapping']; + + if (!isset($mapping['schema'])) { + $mapping['schemas'] = AttributeMapping::constant($this->configuration['schema'])->ignoreWrite(); + } + return AttributeMapping::object($mapping ?? [])->setDefaultSchema($this->configuration['schema']); } - + public function getName() { return $this->name; } - + public function getSchema() { return $this->configuration['schema']; } - + public function getClass() { return $this->configuration['class']; @@ -55,32 +61,32 @@ public function getWithRelations() { return $this->configuration['withRelations'] ?? []; } - + public static function user() { return new ResourceType('Users', resolve(SCIMConfig::class)->getUserConfig()); } - + public function getAllAttributeConfigs($mapping = -1) { $result = []; - + if ($mapping == -1) { $mapping = $this->getMapping(); } - + foreach ($mapping as $key => $value) { if ($value instanceof AttributeMapping && $value != null) { $result[] = $value; } elseif (is_array($value)) { $extra = $this->getAllAttributeConfigs($value); - + if (!empty($extra)) { $result = array_merge($result, $extra); } } } - + return $result; } } diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index ac962c0c..d7e70f5b 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -41,7 +41,10 @@ public function getUserConfig() ], 'singular' => 'User', - 'schema' => [Schema::SCHEMA_USER], + 'schema' => [ + Schema::SCHEMA_USER, + 'example:name:space' + ], //eager loading 'withRelations' => [], @@ -75,13 +78,6 @@ function ($object) { 'resourceType' => AttributeMapping::constant("User") ], - 'schemas' => AttributeMapping::constant( - [ - 'urn:ietf:params:scim:schemas:core:2.0:User', - 'example:name:space', - ] - )->ignoreWrite(), - 'example:name:space' => [ 'cityPrefix' => AttributeMapping::eloquent('cityPrefix') ], From a4d1bcf81aaf222af1ef6426e058d50ad3ce950a Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Thu, 2 Mar 2023 23:08:00 +0100 Subject: [PATCH 04/58] Add Laravel x PHP matrix to Github Action (#38) Co-authored-by: Robert Jelen --- .github/workflows/ci.yml | 40 +- composer.json | 2 +- composer.lock | 1490 ++++++++++++++++---------------------- 3 files changed, 647 insertions(+), 885 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44f6527f..50c18565 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,39 @@ name: CI -on: [push] +on: ['push', 'pull_request'] jobs: - build-test: + tests: runs-on: ubuntu-latest + continue-on-error: true + strategy: + matrix: + php: [7.3, 7.4, 8.0, 8.1] + laravel: [6.*, 7.*, 8.*, 9.*] + exclude: + - laravel: 6.* + php: 8.1 + - laravel: 7.* + php: 8.1 + - laravel: 8.* + php: 7.2 + - laravel: 9.* + php: 7.3 + - laravel: 9.* + php: 7.4 + + name: PHP ${{ matrix.php }} on Laravel ${{ matrix.laravel }} steps: - - uses: actions/checkout@v2 - - uses: php-actions/composer@v6 - with: - php_version: 8 - - uses: php-actions/phpunit@v3 + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 with: - php_version: 8 - version: 9.5 + php-version: ${{ matrix.php }} + - name: Install dependencies + run: | + composer require "illuminate/console:${{ matrix.laravel }}" "illuminate/database:${{ matrix.laravel }}" "illuminate/support:${{ matrix.laravel }}" --no-interaction --no-update + composer update + - name: Run tests + run: vendor/bin/phpunit \ No newline at end of file diff --git a/composer.json b/composer.json index 8bb31957..949934a8 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ } }, "require-dev": { - "orchestra/testbench": "^5.0|^6.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", "laravel/legacy-factories": "*" }, diff --git a/composer.lock b/composer.lock index 2a572cd7..581c14e6 100644 --- a/composer.lock +++ b/composer.lock @@ -8,26 +8,26 @@ "packages": [ { "name": "brick/math", - "version": "0.9.3", + "version": "0.10.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", + "url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f", + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f", "shasum": "" }, "require": { "ext-json": "*", - "php": "^7.1 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", - "vimeo/psalm": "4.9.2" + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "4.25.0" }, "type": "library", "autoload": { @@ -52,32 +52,28 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.9.3" + "source": "https://github.com/brick/math/tree/0.10.2" }, "funding": [ { "url": "https://github.com/BenMorel", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/brick/math", - "type": "tidelift" } ], - "time": "2021-08-15T20:50:18+00:00" + "time": "2022-08-10T22:54:19+00:00" }, { "name": "dflydev/dot-access-data", - "version": "v3.0.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "0992cc19268b259a39e86f296da5f0677841f42c" + "reference": "f41715465d65213d644d3141a6a93081be5d3549" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c", - "reference": "0992cc19268b259a39e86f296da5f0677841f42c", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", + "reference": "f41715465d65213d644d3141a6a93081be5d3549", "shasum": "" }, "require": { @@ -88,7 +84,7 @@ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", "scrutinizer/ocular": "1.6.0", "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^3.14" + "vimeo/psalm": "^4.0.0" }, "type": "library", "extra": { @@ -137,34 +133,34 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" }, - "time": "2021-08-13T13:06:58+00:00" + "time": "2022-10-27T11:44:00+00:00" }, { "name": "doctrine/inflector", - "version": "2.0.4", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", - "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "vimeo/psalm": "^4.10" + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25" }, "type": "library", "autoload": { @@ -214,7 +210,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.4" + "source": "https://github.com/doctrine/inflector/tree/2.0.6" }, "funding": [ { @@ -230,7 +226,7 @@ "type": "tidelift" } ], - "time": "2021-10-22T20:16:43+00:00" + "time": "2022-10-20T09:10:12+00:00" }, { "name": "doctrine/lexer", @@ -310,16 +306,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.1", + "version": "v3.3.2", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", "shasum": "" }, "require": { @@ -359,7 +355,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" }, "funding": [ { @@ -367,7 +363,7 @@ "type": "github" } ], - "time": "2022-01-18T15:43:28+00:00" + "time": "2022-09-10T18:51:20+00:00" }, { "name": "egulias/email-validator", @@ -439,24 +435,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.0.4", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "0690bde05318336c7221785f2a932467f98b64ca" + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", - "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "phpoption/phpoption": "^1.8" + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.1" }, "require-dev": { - "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" }, "type": "library", "autoload": { @@ -485,7 +481,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" }, "funding": [ { @@ -497,20 +493,20 @@ "type": "tidelift" } ], - "time": "2021-11-21T21:41:47+00:00" + "time": "2023-02-25T20:23:15+00:00" }, { "name": "laravel/framework", - "version": "v8.83.8", + "version": "v8.83.27", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "cf430301ad17656b3d918995bcdd0454c3c119b9" + "reference": "e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/cf430301ad17656b3d918995bcdd0454c3c119b9", - "reference": "cf430301ad17656b3d918995bcdd0454c3c119b9", + "url": "https://api.github.com/repos/laravel/framework/zipball/e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49", + "reference": "e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49", "shasum": "" }, "require": { @@ -670,29 +666,30 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-04-12T13:49:56+00:00" + "time": "2022-12-08T15:28:55+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.1.1", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e" + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/9e4b005daa20b0c161f3845040046dc9ddc1d74e", - "reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", "shasum": "" }, "require": { "php": "^7.3|^8.0" }, "require-dev": { - "pestphp/pest": "^1.18", - "phpstan/phpstan": "^0.12.98", - "symfony/var-dumper": "^5.3" + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" }, "type": "library", "extra": { @@ -729,20 +726,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2022-02-11T19:23:53+00:00" + "time": "2023-01-30T18:31:20+00:00" }, { "name": "league/commonmark", - "version": "2.3.0", + "version": "2.3.9", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955" + "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/32a49eb2b38fe5e5c417ab748a45d0beaab97955", - "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c1e114f74e518daca2729ea8c4bf1167038fa4b5", + "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5", "shasum": "" }, "require": { @@ -762,15 +759,15 @@ "erusev/parsedown": "^1.0", "ext-json": "*", "github/gfm": "0.29.0", - "michelf/php-markdown": "^1.4", + "michelf/php-markdown": "^1.4 || ^2.0", "nyholm/psr7": "^1.5", - "phpstan/phpstan": "^0.12.88 || ^1.0.0", - "phpunit/phpunit": "^9.5.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3", + "symfony/finder": "^5.3 | ^6.0", "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", - "unleashedtech/php-coding-standard": "^3.1", - "vimeo/psalm": "^4.7.3" + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -835,20 +832,20 @@ "type": "tidelift" } ], - "time": "2022-04-07T22:37:05+00:00" + "time": "2023-02-15T14:07:24+00:00" }, { "name": "league/config", - "version": "v1.1.1", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/config.git", - "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e" + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/config/zipball/a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", - "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", "shasum": "" }, "require": { @@ -857,7 +854,7 @@ "php": "^7.4 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.90", + "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.5", "scrutinizer/ocular": "^1.8.1", "unleashedtech/php-coding-standard": "^3.1", @@ -917,20 +914,20 @@ "type": "github" } ], - "time": "2021-08-14T12:15:32+00:00" + "time": "2022-12-11T20:36:23+00:00" }, { "name": "league/flysystem", - "version": "1.1.9", + "version": "1.1.10", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "094defdb4a7001845300334e7c1ee2335925ef99" + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/094defdb4a7001845300334e7c1ee2335925ef99", - "reference": "094defdb4a7001845300334e7c1ee2335925ef99", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", "shasum": "" }, "require": { @@ -1003,7 +1000,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.9" + "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" }, "funding": [ { @@ -1011,20 +1008,20 @@ "type": "other" } ], - "time": "2021-12-09T09:40:50+00:00" + "time": "2022-10-04T09:16:37+00:00" }, { "name": "league/mime-type-detection", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "3e4a35d756eedc67096f30240a68a3149120dae7" + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3e4a35d756eedc67096f30240a68a3149120dae7", - "reference": "3e4a35d756eedc67096f30240a68a3149120dae7", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", "shasum": "" }, "require": { @@ -1055,7 +1052,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.10.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" }, "funding": [ { @@ -1067,20 +1064,20 @@ "type": "tidelift" } ], - "time": "2022-04-11T12:49:04+00:00" + "time": "2022-04-17T13:12:02+00:00" }, { "name": "monolog/monolog", - "version": "2.5.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "4192345e260f1d51b365536199744b987e160edc" + "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4192345e260f1d51b365536199744b987e160edc", - "reference": "4192345e260f1d51b365536199744b987e160edc", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1", + "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1", "shasum": "" }, "require": { @@ -1093,18 +1090,22 @@ "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7", - "graylog2/gelf-php": "^1.4.2", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.3", - "phpspec/prophecy": "^1.6.1", + "phpspec/prophecy": "^1.15", "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5", - "predis/predis": "^1.1", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", "rollbar/rollbar": "^1.3 || ^2 || ^3", - "ruflin/elastica": ">=0.90@dev", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", @@ -1119,7 +1120,6 @@ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, @@ -1154,7 +1154,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.5.0" + "source": "https://github.com/Seldaek/monolog/tree/2.9.1" }, "funding": [ { @@ -1166,20 +1166,20 @@ "type": "tidelift" } ], - "time": "2022-04-08T15:43:54+00:00" + "time": "2023-02-06T13:44:46+00:00" }, { "name": "nesbot/carbon", - "version": "2.57.0", + "version": "2.66.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "4a54375c21eea4811dbd1149fe6b246517554e78" + "reference": "496712849902241f04902033b0441b269effe001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4a54375c21eea4811dbd1149fe6b246517554e78", - "reference": "4a54375c21eea4811dbd1149fe6b246517554e78", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", + "reference": "496712849902241f04902033b0441b269effe001", "shasum": "" }, "require": { @@ -1190,14 +1190,16 @@ "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.0", + "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", "phpmd/phpmd": "^2.9", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.54 || ^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.14", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", "squizlabs/php_codesniffer": "^3.4" }, "bin": [ @@ -1254,37 +1256,41 @@ }, "funding": [ { - "url": "https://opencollective.com/Carbon", - "type": "open_collective" + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", "type": "tidelift" } ], - "time": "2022-02-13T18:13:33+00:00" + "time": "2023-01-29T18:53:47+00:00" }, { "name": "nette/schema", - "version": "v1.2.2", + "version": "v1.2.3", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df" + "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df", - "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df", + "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", "shasum": "" }, "require": { "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.2" + "php": ">=7.1 <8.3" }, "require-dev": { "nette/tester": "^2.3 || ^2.4", - "phpstan/phpstan-nette": "^0.12", + "phpstan/phpstan-nette": "^1.0", "tracy/tracy": "^2.7" }, "type": "library", @@ -1322,34 +1328,36 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.2" + "source": "https://github.com/nette/schema/tree/v1.2.3" }, - "time": "2021-10-15T11:40:02+00:00" + "time": "2022-10-13T01:24:26+00:00" }, { "name": "nette/utils", - "version": "v3.2.7", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99" + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99", - "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99", + "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", "shasum": "" }, "require": { - "php": ">=7.2 <8.2" + "php": ">=8.0 <8.3" }, "conflict": { - "nette/di": "<3.0.6" + "nette/finder": "<3", + "nette/schema": "<1.2.2" }, "require-dev": { - "nette/tester": "~2.0", + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.4", "phpstan/phpstan": "^1.0", - "tracy/tracy": "^2.3" + "tracy/tracy": "^2.9" }, "suggest": { "ext-gd": "to use Image", @@ -1363,7 +1371,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1407,9 +1415,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v3.2.7" + "source": "https://github.com/nette/utils/tree/v4.0.0" }, - "time": "2022-01-24T11:29:14+00:00" + "time": "2023-02-02T10:41:53+00:00" }, { "name": "opis/closure", @@ -1478,29 +1486,33 @@ }, { "name": "phpoption/phpoption", - "version": "1.8.1", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", - "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -1533,7 +1545,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" }, "funding": [ { @@ -1545,7 +1557,7 @@ "type": "tidelift" } ], - "time": "2021-12-04T23:24:31+00:00" + "time": "2023-02-25T19:38:58+00:00" }, { "name": "psr/container", @@ -1748,42 +1760,52 @@ }, { "name": "ramsey/collection", - "version": "1.2.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", - "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", "shasum": "" }, "require": { - "php": "^7.3 || ^8", - "symfony/polyfill-php81": "^1.23" + "php": "^8.1" }, "require-dev": { - "captainhook/captainhook": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.6", - "fakerphp/faker": "^1.5", - "hamcrest/hamcrest-php": "^2", - "jangregor/phpstan-prophecy": "^0.8", - "mockery/mockery": "^1.3", + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1", - "phpstan/phpstan": "^0.12.32", - "phpstan/phpstan-mockery": "^0.12.5", - "phpstan/phpstan-phpunit": "^0.12.11", - "phpunit/phpunit": "^8.5 || ^9", - "psy/psysh": "^0.10.4", - "slevomat/coding-standard": "^6.3", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.4" + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" }, "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, "autoload": { "psr-4": { "Ramsey\\Collection\\": "src/" @@ -1811,7 +1833,7 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.2.2" + "source": "https://github.com/ramsey/collection/tree/2.0.0" }, "funding": [ { @@ -1823,28 +1845,27 @@ "type": "tidelift" } ], - "time": "2021-10-10T03:01:02+00:00" + "time": "2022-12-31T21:50:55+00:00" }, { "name": "ramsey/uuid", - "version": "4.3.1", + "version": "4.7.3", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28" + "reference": "433b2014e3979047db08a17a205f410ba3869cf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", - "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2", + "reference": "433b2014e3979047db08a17a205f410ba3869cf2", "shasum": "" }, "require": { - "brick/math": "^0.8 || ^0.9", - "ext-ctype": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10", "ext-json": "*", "php": "^8.0", - "ramsey/collection": "^1.0" + "ramsey/collection": "^1.2 || ^2.0" }, "replace": { "rhumsaa/uuid": "self.version" @@ -1856,24 +1877,23 @@ "doctrine/annotations": "^1.8", "ergebnis/composer-normalize": "^2.15", "mockery/mockery": "^1.3", - "moontoast/math": "^1.1", "paragonie/random-lib": "^2", "php-mock/php-mock": "^2.2", "php-mock/php-mock-mockery": "^1.3", "php-parallel-lint/php-parallel-lint": "^1.1", "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-mockery": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", "phpunit/phpunit": "^8.5 || ^9", - "slevomat/coding-standard": "^7.0", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.9" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-ctype": "Enables faster processing of character classification using ctype functions.", "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", @@ -1905,7 +1925,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.3.1" + "source": "https://github.com/ramsey/uuid/tree/4.7.3" }, "funding": [ { @@ -1917,7 +1937,7 @@ "type": "tidelift" } ], - "time": "2022-03-27T21:42:02+00:00" + "time": "2023-01-12T18:13:24+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -1997,16 +2017,16 @@ }, { "name": "symfony/console", - "version": "v5.4.7", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6" + "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/900275254f0a1a2afff1ab0e11abd5587a10e1d6", - "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6", + "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", + "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", "shasum": "" }, "require": { @@ -2076,7 +2096,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.7" + "source": "https://github.com/symfony/console/tree/v5.4.19" }, "funding": [ { @@ -2092,24 +2112,24 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:09:19+00:00" + "time": "2023-01-01T08:32:19+00:00" }, { "name": "symfony/css-selector", - "version": "v6.0.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "1955d595c12c111629cc814d3f2a2ff13580508a" + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1955d595c12c111629cc814d3f2a2ff13580508a", - "reference": "1955d595c12c111629cc814d3f2a2ff13580508a", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -2141,7 +2161,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.0.3" + "source": "https://github.com/symfony/css-selector/tree/v6.2.5" }, "funding": [ { @@ -2157,29 +2177,29 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2208,7 +2228,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -2224,20 +2244,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/error-handler", - "version": "v5.4.7", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a" + "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/060bc01856a1846e3e4385261bc9ed11a1dd7b6a", - "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/438ef3e5e6481244785da3ce8cf8f4e74e7f2822", + "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822", "shasum": "" }, "require": { @@ -2279,7 +2299,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/v5.4.7" + "source": "https://github.com/symfony/error-handler/tree/v5.4.19" }, "funding": [ { @@ -2295,24 +2315,24 @@ "type": "tidelift" } ], - "time": "2022-03-18T16:21:29+00:00" + "time": "2023-01-01T08:32:19+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.0.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934" + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6472ea2dd415e925b90ca82be64b8bc6157f3934", - "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2|^3" }, "conflict": { @@ -2362,7 +2382,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/v6.0.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.5" }, "funding": [ { @@ -2378,24 +2398,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, "suggest": { @@ -2404,7 +2424,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2441,7 +2461,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0" }, "funding": [ { @@ -2457,20 +2477,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/finder", - "version": "v5.4.3", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d" + "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d", + "url": "https://api.github.com/repos/symfony/finder/zipball/6071aebf810ad13fe8200c224f36103abb37cf1f", + "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f", "shasum": "" }, "require": { @@ -2504,7 +2524,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.3" + "source": "https://github.com/symfony/finder/tree/v5.4.19" }, "funding": [ { @@ -2520,20 +2540,20 @@ "type": "tidelift" } ], - "time": "2022-01-26T16:34:36+00:00" + "time": "2023-01-14T19:14:44+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.6", + "version": "v5.4.20", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465" + "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/34e89bc147633c0f9dd6caaaf56da3b806a21465", - "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0435363362a47c14e9cf50663cb8ffbf491875a", + "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a", "shasum": "" }, "require": { @@ -2545,8 +2565,11 @@ "require-dev": { "predis/predis": "~1.0", "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0" + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/rate-limiter": "^5.2|^6.0" }, "suggest": { "symfony/mime": "To use the file extension guesser" @@ -2577,7 +2600,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.6" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.20" }, "funding": [ { @@ -2593,20 +2616,20 @@ "type": "tidelift" } ], - "time": "2022-03-05T21:03:43+00:00" + "time": "2023-01-29T11:11:52+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.7", + "version": "v5.4.20", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "509243b9b3656db966284c45dffce9316c1ecc5c" + "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/509243b9b3656db966284c45dffce9316c1ecc5c", - "reference": "509243b9b3656db966284c45dffce9316c1ecc5c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e", + "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e", "shasum": "" }, "require": { @@ -2689,7 +2712,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/v5.4.7" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.20" }, "funding": [ { @@ -2705,20 +2728,20 @@ "type": "tidelift" } ], - "time": "2022-04-02T06:04:20+00:00" + "time": "2023-02-01T08:18:48+00:00" }, { "name": "symfony/mime", - "version": "v5.4.7", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c" + "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/92d27a34dea2e199fa9b687e3fff3a7d169b7b1c", - "reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c", + "url": "https://api.github.com/repos/symfony/mime/zipball/a858429a9c704edc53fe057228cf9ca282ba48eb", + "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb", "shasum": "" }, "require": { @@ -2732,15 +2755,16 @@ "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<4.4" + "symfony/mailer": "<4.4", + "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6" }, "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1", + "egulias/email-validator": "^2.1.10|^3.1|^4", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/dependency-injection": "^4.4|^5.0|^6.0", "symfony/property-access": "^4.4|^5.1|^6.0", "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.2|^6.0" + "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6" }, "type": "library", "autoload": { @@ -2772,7 +2796,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.7" + "source": "https://github.com/symfony/mime/tree/v5.4.19" }, "funding": [ { @@ -2788,20 +2812,20 @@ "type": "tidelift" } ], - "time": "2022-03-11T16:08:05+00:00" + "time": "2023-01-09T05:43:46+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -2816,7 +2840,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2854,7 +2878,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -2870,20 +2894,20 @@ "type": "tidelift" } ], - "time": "2021-10-20T20:35:02+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "f1aed619e28cb077fc83fac8c4c0383578356e40" + "reference": "927013f3aac555983a5059aada98e1907d842695" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f1aed619e28cb077fc83fac8c4c0383578356e40", - "reference": "f1aed619e28cb077fc83fac8c4c0383578356e40", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/927013f3aac555983a5059aada98e1907d842695", + "reference": "927013f3aac555983a5059aada98e1907d842695", "shasum": "" }, "require": { @@ -2898,7 +2922,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2937,7 +2961,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.27.0" }, "funding": [ { @@ -2953,20 +2977,20 @@ "type": "tidelift" } ], - "time": "2022-01-04T09:04:05+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { @@ -2978,7 +3002,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3018,7 +3042,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -3034,20 +3058,20 @@ "type": "tidelift" } ], - "time": "2021-11-23T21:10:46+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "749045c69efb97c70d25d7463abba812e91f3a44" + "reference": "639084e360537a19f9ee352433b84ce831f3d2da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", - "reference": "749045c69efb97c70d25d7463abba812e91f3a44", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", + "reference": "639084e360537a19f9ee352433b84ce831f3d2da", "shasum": "" }, "require": { @@ -3061,7 +3085,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3105,7 +3129,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" }, "funding": [ { @@ -3121,20 +3145,20 @@ "type": "tidelift" } ], - "time": "2021-09-14T14:02:44+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { @@ -3146,7 +3170,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3189,7 +3213,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -3205,20 +3229,20 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -3233,7 +3257,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3272,7 +3296,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -3288,20 +3312,20 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "9a142215a36a3888e30d0a9eeea9766764e96976" + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", - "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", "shasum": "" }, "require": { @@ -3310,7 +3334,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3348,7 +3372,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" }, "funding": [ { @@ -3364,20 +3388,20 @@ "type": "tidelift" } ], - "time": "2021-05-27T09:17:38+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", "shasum": "" }, "require": { @@ -3386,7 +3410,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3427,7 +3451,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" }, "funding": [ { @@ -3443,20 +3467,20 @@ "type": "tidelift" } ], - "time": "2021-06-05T21:20:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -3465,7 +3489,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3510,7 +3534,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -3526,99 +3550,20 @@ "type": "tidelift" } ], - "time": "2022-03-04T08:16:47+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.25.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-09-13T13:58:11+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", - "version": "v5.4.7", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb" + "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/38a44b2517b470a436e1c944bf9b9ba3961137fb", - "reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb", + "url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1", + "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1", "shasum": "" }, "require": { @@ -3651,7 +3596,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.7" + "source": "https://github.com/symfony/process/tree/v5.4.19" }, "funding": [ { @@ -3667,20 +3612,20 @@ "type": "tidelift" } ], - "time": "2022-03-18T16:18:52+00:00" + "time": "2023-01-01T08:32:19+00:00" }, { "name": "symfony/routing", - "version": "v5.4.3", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "44b29c7a94e867ccde1da604792f11a469958981" + "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/44b29c7a94e867ccde1da604792f11a469958981", - "reference": "44b29c7a94e867ccde1da604792f11a469958981", + "url": "https://api.github.com/repos/symfony/routing/zipball/df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", + "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", "shasum": "" }, "require": { @@ -3695,7 +3640,7 @@ "symfony/yaml": "<4.4" }, "require-dev": { - "doctrine/annotations": "^1.12", + "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", "symfony/config": "^5.3|^6.0", "symfony/dependency-injection": "^4.4|^5.0|^6.0", @@ -3741,7 +3686,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.3" + "source": "https://github.com/symfony/routing/tree/v5.4.19" }, "funding": [ { @@ -3757,20 +3702,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2023-01-01T08:32:19+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", "shasum": "" }, "require": { @@ -3824,7 +3769,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" }, "funding": [ { @@ -3840,24 +3785,24 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2022-05-30T19:17:29+00:00" }, { "name": "symfony/string", - "version": "v6.0.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", + "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3869,6 +3814,7 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -3909,7 +3855,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.3" + "source": "https://github.com/symfony/string/tree/v6.2.5" }, "funding": [ { @@ -3925,24 +3871,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/translation", - "version": "v6.0.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1" + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1", - "reference": "b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1", + "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.3|^3.0" }, @@ -3958,6 +3904,7 @@ "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^4.13", "psr/log": "^1|^2|^3", "symfony/config": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", @@ -3967,10 +3914,12 @@ "symfony/http-kernel": "^5.4|^6.0", "symfony/intl": "^5.4|^6.0", "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", "symfony/service-contracts": "^1.1.2|^2|^3", "symfony/yaml": "^5.4|^6.0" }, "suggest": { + "nikic/php-parser": "To use PhpAstExtractor", "psr/log-implementation": "To use logging capability in translator", "symfony/config": "", "symfony/yaml": "" @@ -4004,7 +3953,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.0.7" + "source": "https://github.com/symfony/translation/tree/v6.2.5" }, "funding": [ { @@ -4020,24 +3969,24 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:18:25+00:00" + "time": "2023-01-05T07:00:27+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.0.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9" + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", - "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7", + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "suggest": { "symfony/translation-implementation": "" @@ -4045,7 +3994,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -4055,7 +4004,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4082,7 +4034,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.0.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.2.0" }, "funding": [ { @@ -4098,20 +4050,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.6", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0" + "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/294e9da6e2e0dd404e983daa5aa74253d92c05d0", - "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", + "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", "shasum": "" }, "require": { @@ -4171,7 +4123,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.6" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.19" }, "funding": [ { @@ -4187,20 +4139,20 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:42:23+00:00" + "time": "2023-01-16T10:52:33+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.4", + "version": "2.2.6", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c" + "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/da444caae6aca7a19c0c140f68c6182e337d5b1c", - "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", "shasum": "" }, "require": { @@ -4238,9 +4190,9 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.4" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" }, - "time": "2021-12-08T09:12:39+00:00" + "time": "2023-01-03T09:29:04+00:00" }, { "name": "tmilos/lexer", @@ -4437,16 +4389,16 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.4.1", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", - "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", "shasum": "" }, "require": { @@ -4461,15 +4413,19 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" }, "suggest": { "ext-filter": "Required to use the boolean validator." }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { - "dev-master": "5.4-dev" + "dev-master": "5.5-dev" } }, "autoload": { @@ -4501,7 +4457,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" }, "funding": [ { @@ -4513,7 +4469,7 @@ "type": "tidelift" } ], - "time": "2021-12-12T23:22:04+00:00" + "time": "2022-10-16T01:01:54+00:00" }, { "name": "voku/portable-ascii", @@ -4591,21 +4547,21 @@ }, { "name": "webmozart/assert", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { "phpstan/phpstan": "<0.12.20", @@ -4643,38 +4599,38 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2021-03-09T10:59:23+00:00" + "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -4701,7 +4657,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -4717,24 +4673,24 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "fakerphp/faker", - "version": "v1.19.0", + "version": "v1.21.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75" + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/d7f08a622b3346766325488aa32ddc93ccdecc75", - "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", + "php": "^7.4 || ^8.0", "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" }, @@ -4745,7 +4701,8 @@ "bamarni/composer-bin-plugin": "^1.4.1", "doctrine/persistence": "^1.3 || ^2.0", "ext-intl": "*", - "symfony/phpunit-bridge": "^4.4 || ^5.2" + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" }, "suggest": { "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", @@ -4757,7 +4714,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "v1.19-dev" + "dev-main": "v1.21-dev" } }, "autoload": { @@ -4782,22 +4739,22 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.19.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" }, - "time": "2022-02-02T17:38:57+00:00" + "time": "2022-12-13T13:54:32+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.2.1", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" + "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", - "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { @@ -4811,17 +4768,21 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.1", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.8 || ^9.3.10" + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -4883,7 +4844,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.2.1" + "source": "https://github.com/guzzle/psr7/tree/2.4.3" }, "funding": [ { @@ -4899,7 +4860,7 @@ "type": "tidelift" } ], - "time": "2022-03-20T21:55:58+00:00" + "time": "2022-10-26T14:07:24+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -4954,20 +4915,20 @@ }, { "name": "laravel/legacy-factories", - "version": "v1.3.0", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/laravel/legacy-factories.git", - "reference": "5edc7e7eb76e7b4b29221f32139bcbf806c8870f" + "reference": "cc6720da81094c82ea9f4737d615dd3d71f7f43d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/5edc7e7eb76e7b4b29221f32139bcbf806c8870f", - "reference": "5edc7e7eb76e7b4b29221f32139bcbf806c8870f", + "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/cc6720da81094c82ea9f4737d615dd3d71f7f43d", + "reference": "cc6720da81094c82ea9f4737d615dd3d71f7f43d", "shasum": "" }, "require": { - "illuminate/macroable": "^8.0|^9.0", + "illuminate/macroable": "^8.0|^9.0|^10.0", "php": "^7.3|^8.0", "symfony/finder": "^3.4|^4.0|^5.0|^6.0" }, @@ -5006,20 +4967,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-01-13T08:45:08+00:00" + "time": "2023-01-09T14:49:00+00:00" }, { "name": "mockery/mockery", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac" + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", "shasum": "" }, "require": { @@ -5076,9 +5037,9 @@ ], "support": { "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.5.0" + "source": "https://github.com/mockery/mockery/tree/1.5.1" }, - "time": "2022-01-20T13:18:17+00:00" + "time": "2022-09-07T15:32:08+00:00" }, { "name": "myclabs/deep-copy", @@ -5141,16 +5102,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.13.2", + "version": "v4.15.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { @@ -5191,28 +5152,28 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" }, - "time": "2021-11-30T19:35:32+00:00" + "time": "2023-01-16T22:05:37+00:00" }, { "name": "orchestra/testbench", - "version": "v6.24.1", + "version": "v6.25.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench.git", - "reference": "7b6a225851f6c148a80e241af5cbd833c83e572c" + "reference": "0516123d26d64117bc04f7e9cb982eae2624e750" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench/zipball/7b6a225851f6c148a80e241af5cbd833c83e572c", - "reference": "7b6a225851f6c148a80e241af5cbd833c83e572c", + "url": "https://api.github.com/repos/orchestral/testbench/zipball/0516123d26d64117bc04f7e9cb982eae2624e750", + "reference": "0516123d26d64117bc04f7e9cb982eae2624e750", "shasum": "" }, "require": { "laravel/framework": "^8.75", "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^6.28.1", + "orchestra/testbench-core": "^6.29.1", "php": "^7.3 || ^8.0", "phpunit/phpunit": "^8.5.21 || ^9.5.10", "spatie/laravel-ray": "^1.26.2" @@ -5246,7 +5207,7 @@ ], "support": { "issues": "https://github.com/orchestral/testbench/issues", - "source": "https://github.com/orchestral/testbench/tree/v6.24.1" + "source": "https://github.com/orchestral/testbench/tree/v6.25.1" }, "funding": [ { @@ -5258,20 +5219,20 @@ "type": "liberapay" } ], - "time": "2022-02-08T12:57:17+00:00" + "time": "2022-10-11T14:01:10+00:00" }, { "name": "orchestra/testbench-core", - "version": "v6.28.1", + "version": "v6.29.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "e66074e825e21b40b3433703dc3f76f2bfebebe0" + "reference": "29a7586915885f89b8d2203efe20f76afe9cf956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/e66074e825e21b40b3433703dc3f76f2bfebebe0", - "reference": "e66074e825e21b40b3433703dc3f76f2bfebebe0", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/29a7586915885f89b8d2203efe20f76afe9cf956", + "reference": "29a7586915885f89b8d2203efe20f76afe9cf956", "shasum": "" }, "require": { @@ -5285,7 +5246,7 @@ "laravel/laravel": "8.x-dev", "mockery/mockery": "^1.4.4", "orchestra/canvas": "^6.1", - "phpunit/phpunit": "^8.5.21 || ^9.5.10 || ^10.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10", "spatie/laravel-ray": "^1.7.1", "symfony/process": "^5.0" }, @@ -5348,7 +5309,7 @@ "type": "liberapay" } ], - "time": "2022-02-08T12:50:35+00:00" + "time": "2022-10-11T12:12:52+00:00" }, { "name": "phar-io/manifest", @@ -5461,252 +5422,25 @@ }, "time": "2022-02-21T01:04:05+00:00" }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" - }, - "time": "2021-12-08T12:19:24+00:00" - }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954", + "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.15", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -5755,7 +5489,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25" }, "funding": [ { @@ -5763,7 +5497,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2023-02-25T05:32:00+00:00" }, { "name": "phpunit/php-file-iterator", @@ -6008,20 +5742,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.20", + "version": "9.6.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d", + "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -6032,7 +5766,6 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", @@ -6040,20 +5773,16 @@ "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, "suggest": { "ext-soap": "*", "ext-xdebug": "*" @@ -6064,7 +5793,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -6095,7 +5824,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4" }, "funding": [ { @@ -6105,9 +5834,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-04-01T12:37:26+00:00" + "time": "2023-02-27T13:06:37+00:00" }, { "name": "pimple/pimple", @@ -6483,16 +6216,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -6545,7 +6278,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -6553,7 +6286,7 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -6680,16 +6413,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -6731,7 +6464,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -6739,20 +6472,20 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { @@ -6808,7 +6541,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { @@ -6816,7 +6549,7 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", @@ -7053,16 +6786,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -7101,10 +6834,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -7112,7 +6845,7 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -7171,16 +6904,16 @@ }, { "name": "sebastian/type", - "version": "3.0.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -7192,7 +6925,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -7215,7 +6948,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -7223,7 +6956,7 @@ "type": "github" } ], - "time": "2022-03-15T09:54:48+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -7280,16 +7013,16 @@ }, { "name": "spatie/backtrace", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "4ee7d41aa5268107906ea8a4d9ceccde136dbd5b" + "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/4ee7d41aa5268107906ea8a4d9ceccde136dbd5b", - "reference": "4ee7d41aa5268107906ea8a4d9ceccde136dbd5b", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", + "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", "shasum": "" }, "require": { @@ -7325,8 +7058,7 @@ "spatie" ], "support": { - "issues": "https://github.com/spatie/backtrace/issues", - "source": "https://github.com/spatie/backtrace/tree/1.2.1" + "source": "https://github.com/spatie/backtrace/tree/1.2.2" }, "funding": [ { @@ -7338,29 +7070,29 @@ "type": "other" } ], - "time": "2021-11-09T10:57:15+00:00" + "time": "2023-02-21T08:29:12+00:00" }, { "name": "spatie/laravel-ray", - "version": "1.29.5", + "version": "1.32.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ray.git", - "reference": "7bb6f1f749e235cea5a963b4c12986baefafd624" + "reference": "0c28a8274ec261a2857b4318b6f934af98024395" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/7bb6f1f749e235cea5a963b4c12986baefafd624", - "reference": "7bb6f1f749e235cea5a963b4c12986baefafd624", + "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/0c28a8274ec261a2857b4318b6f934af98024395", + "reference": "0c28a8274ec261a2857b4318b6f934af98024395", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/contracts": "^7.20|^8.19|^9.0", - "illuminate/database": "^7.20|^8.19|^9.0", - "illuminate/queue": "^7.20|^8.19|^9.0", - "illuminate/support": "^7.20|^8.19|^9.0", - "php": "^7.3|^8.0", + "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0", + "illuminate/database": "^7.20|^8.19|^9.0|^10.0", + "illuminate/queue": "^7.20|^8.19|^9.0|^10.0", + "illuminate/support": "^7.20|^8.19|^9.0|^10.0", + "php": "^7.4|^8.0", "spatie/backtrace": "^1.0", "spatie/ray": "^1.33", "symfony/stopwatch": "4.2|^5.1|^6.0", @@ -7368,11 +7100,12 @@ }, "require-dev": { "guzzlehttp/guzzle": "^7.3", - "laravel/framework": "^7.20|^8.19|^9.0", - "orchestra/testbench-core": "^5.0|^6.0|^7.0", + "laravel/framework": "^7.20|^8.19|^9.0|^10.0", + "orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0", + "pestphp/pest": "^1.22", "phpstan/phpstan": "^0.12.93", "phpunit/phpunit": "^9.3", - "spatie/phpunit-snapshot-assertions": "^4.2" + "spatie/pest-plugin-snapshots": "^1.1" }, "type": "library", "extra": { @@ -7410,7 +7143,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-ray/issues", - "source": "https://github.com/spatie/laravel-ray/tree/1.29.5" + "source": "https://github.com/spatie/laravel-ray/tree/1.32.2" }, "funding": [ { @@ -7422,7 +7155,7 @@ "type": "other" } ], - "time": "2022-04-05T18:07:59+00:00" + "time": "2023-02-06T09:46:50+00:00" }, { "name": "spatie/macroable", @@ -7476,16 +7209,16 @@ }, { "name": "spatie/ray", - "version": "1.34.2", + "version": "1.36.2", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "ef5836b44f7bc4a0162f1a590cbaf33e8ab655dd" + "reference": "71dfde21900447ab37698fc07ff28b7f1e1822b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/ef5836b44f7bc4a0162f1a590cbaf33e8ab655dd", - "reference": "ef5836b44f7bc4a0162f1a590cbaf33e8ab655dd", + "url": "https://api.github.com/repos/spatie/ray/zipball/71dfde21900447ab37698fc07ff28b7f1e1822b8", + "reference": "71dfde21900447ab37698fc07ff28b7f1e1822b8", "shasum": "" }, "require": { @@ -7500,7 +7233,8 @@ }, "require-dev": { "illuminate/support": "6.x|^8.18|^9.0", - "nesbot/carbon": "^2.43", + "nesbot/carbon": "^2.63", + "pestphp/pest": "^1.22", "phpstan/phpstan": "^0.12.92", "phpunit/phpunit": "^9.5", "spatie/phpunit-snapshot-assertions": "^4.2", @@ -7535,7 +7269,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.34.2" + "source": "https://github.com/spatie/ray/tree/1.36.2" }, "funding": [ { @@ -7547,24 +7281,24 @@ "type": "other" } ], - "time": "2022-04-08T13:41:27+00:00" + "time": "2023-02-10T09:24:13+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.0.5", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "f2c1780607ec6502f2121d9729fd8150a655d337" + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f2c1780607ec6502f2121d9729fd8150a655d337", - "reference": "f2c1780607ec6502f2121d9729fd8150a655d337", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6", + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/service-contracts": "^1|^2|^3" }, "type": "library", @@ -7593,7 +7327,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.0.5" + "source": "https://github.com/symfony/stopwatch/tree/v6.2.5" }, "funding": [ { @@ -7609,20 +7343,20 @@ "type": "tidelift" } ], - "time": "2022-02-21T17:15:17+00:00" + "time": "2023-01-01T08:36:55+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.3", + "version": "v5.4.19", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e80f87d2c9495966768310fc531b487ce64237a2" + "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2", - "reference": "e80f87d2c9495966768310fc531b487ce64237a2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/71c05db20cb9b54d381a28255f17580e2b7e36a5", + "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5", "shasum": "" }, "require": { @@ -7668,7 +7402,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.3" + "source": "https://github.com/symfony/yaml/tree/v5.4.19" }, "funding": [ { @@ -7684,7 +7418,7 @@ "type": "tidelift" } ], - "time": "2022-01-26T16:32:32+00:00" + "time": "2023-01-10T18:51:14+00:00" }, { "name": "theseer/tokenizer", @@ -7738,28 +7472,30 @@ }, { "name": "zbateson/mail-mime-parser", - "version": "2.2.1", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/zbateson/mail-mime-parser.git", - "reference": "24955de7ec352b3258c1d4551efd21202cb8710c" + "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/24955de7ec352b3258c1d4551efd21202cb8710c", - "reference": "24955de7ec352b3258c1d4551efd21202cb8710c", + "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/20b3e48eb799537683780bc8782fbbe9bc25934a", + "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a", "shasum": "" }, "require": { "guzzlehttp/psr7": "^1.7.0|^2.0", - "php": ">=5.4", + "php": ">=7.1", "pimple/pimple": "^3.0", "zbateson/mb-wrapper": "^1.0.1", "zbateson/stream-decorators": "^1.0.6" }, "require-dev": { + "friendsofphp/php-cs-fixer": "*", "mikey179/vfsstream": "^1.6.0", - "sanmai/phpunit-legacy-adapter": "^6.3 || ^8.2" + "phpstan/phpstan": "*", + "phpunit/phpunit": "<10" }, "suggest": { "ext-iconv": "For best support/performance", @@ -7807,29 +7543,31 @@ "type": "github" } ], - "time": "2022-02-22T21:35:59+00:00" + "time": "2023-02-14T22:58:03+00:00" }, { "name": "zbateson/mb-wrapper", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/zbateson/mb-wrapper.git", - "reference": "bfd45fb3e2a8cf4c496b2c3ebd63b5f815721498" + "reference": "faf35dddfacfc5d4d5f9210143eafd7a7fe74334" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/bfd45fb3e2a8cf4c496b2c3ebd63b5f815721498", - "reference": "bfd45fb3e2a8cf4c496b2c3ebd63b5f815721498", + "url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/faf35dddfacfc5d4d5f9210143eafd7a7fe74334", + "reference": "faf35dddfacfc5d4d5f9210143eafd7a7fe74334", "shasum": "" }, "require": { - "php": ">=5.4", + "php": ">=7.1", "symfony/polyfill-iconv": "^1.9", "symfony/polyfill-mbstring": "^1.9" }, "require-dev": { - "sanmai/phpunit-legacy-adapter": "^6.3 || ^8" + "friendsofphp/php-cs-fixer": "*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "<=9.0" }, "suggest": { "ext-iconv": "For best support/performance", @@ -7866,7 +7604,7 @@ ], "support": { "issues": "https://github.com/zbateson/mb-wrapper/issues", - "source": "https://github.com/zbateson/mb-wrapper/tree/1.1.1" + "source": "https://github.com/zbateson/mb-wrapper/tree/1.2.0" }, "funding": [ { @@ -7874,29 +7612,31 @@ "type": "github" } ], - "time": "2021-11-22T21:59:45+00:00" + "time": "2023-01-11T23:05:44+00:00" }, { "name": "zbateson/stream-decorators", - "version": "1.0.6", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/zbateson/stream-decorators.git", - "reference": "3403c4323bd1cd15fe54348b031b26b064c706af" + "reference": "7466ff45d249c86b96267a83cdae68365ae1787e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/3403c4323bd1cd15fe54348b031b26b064c706af", - "reference": "3403c4323bd1cd15fe54348b031b26b064c706af", + "url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/7466ff45d249c86b96267a83cdae68365ae1787e", + "reference": "7466ff45d249c86b96267a83cdae68365ae1787e", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.7.0|^2.0", - "php": ">=5.4", + "guzzlehttp/psr7": "^1.9 | ^2.0", + "php": ">=7.1", "zbateson/mb-wrapper": "^1.0.0" }, "require-dev": { - "sanmai/phpunit-legacy-adapter": "^6.3 || ^8" + "friendsofphp/php-cs-fixer": "*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "<=9.0" }, "type": "library", "autoload": { @@ -7927,7 +7667,7 @@ ], "support": { "issues": "https://github.com/zbateson/stream-decorators/issues", - "source": "https://github.com/zbateson/stream-decorators/tree/1.0.6" + "source": "https://github.com/zbateson/stream-decorators/tree/1.1.0" }, "funding": [ { @@ -7935,7 +7675,7 @@ "type": "github" } ], - "time": "2021-07-08T19:01:59+00:00" + "time": "2023-01-11T23:22:44+00:00" } ], "aliases": [], From 00f8da48264bb7c791ffd2bd77c9b70f93d1365a Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Tue, 14 Mar 2023 20:03:10 +0100 Subject: [PATCH 05/58] Add support for Laravel 10 (#39) --- .github/workflows/ci.yml | 16 ++++++++++++---- composer.json | 12 ++++++------ phpunit.xml | 10 +++++----- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c18565..b47842e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,19 +8,27 @@ jobs: continue-on-error: true strategy: matrix: - php: [7.3, 7.4, 8.0, 8.1] - laravel: [6.*, 7.*, 8.*, 9.*] + php: [7.3, 7.4, 8.0, 8.1, 8.2] + laravel: [6.*, 7.*, 8.*, 9.*, 10.*] exclude: - laravel: 6.* php: 8.1 + - laravel: 6.* + php: 8.2 - laravel: 7.* php: 8.1 - - laravel: 8.* - php: 7.2 + - laravel: 7.* + php: 8.2 - laravel: 9.* php: 7.3 - laravel: 9.* php: 7.4 + - laravel: 10.* + php: 7.3 + - laravel: 10.* + php: 7.4 + - laravel: 10.* + php: 8.0 name: PHP ${{ matrix.php }} on Laravel ${{ matrix.laravel }} diff --git a/composer.json b/composer.json index 949934a8..f97cb517 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^7.0|^8.0", - "illuminate/database": "^6.0|^7.0|^8.0|^9.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0", - "illuminate/console": "^6.0|^7.0|^8.0|^9.0", + "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, @@ -29,10 +29,10 @@ } }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", "laravel/legacy-factories": "*" }, - + "extra": { "laravel": { "providers": [ @@ -43,5 +43,5 @@ } } } - + } diff --git a/phpunit.xml b/phpunit.xml index 888a80d5..4888111e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,13 @@ - From fdcb139a7a4e2f224ba1af7e68d0e9658cb9de5a Mon Sep 17 00:00:00 2001 From: Brady Wetherington Date: Wed, 26 Apr 2023 00:39:58 -0700 Subject: [PATCH 06/58] Changes the ID attribute to always be a string, as per spec. (#42) --- src/SCIMConfig.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index d7e70f5b..acf6d4b5 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -55,7 +55,11 @@ public function getUserConfig() // Map a SCIM attribute to an attribute of the object. 'mapping' => [ - 'id' => AttributeMapping::eloquent("id")->disableWrite(), + 'id' => (new AttributeMapping())->setRead( + function (&$object) { + return (string)$object->id; + } + )->disableWrite(), 'externalId' => null, From 351937587b0120e0083b38a98d81f050bd18aa67 Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Mon, 24 Jul 2023 20:45:13 +0200 Subject: [PATCH 07/58] Add support for model factories (#49) --- src/Http/Controllers/ResourceController.php | 7 +------ src/ResourceType.php | 9 +++++++++ src/SCIMConfig.php | 4 ++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index fce42203..25158c49 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -108,12 +108,7 @@ public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint throw (new SCIMException('This is not allowed'))->setCode(403); } - $class = $resourceType->getClass(); - - /** - * @var Model -*/ - $resourceObject = new $class(); + $resourceObject = $resourceType->getFactory()(); $allAttributeConfigs = []; diff --git a/src/ResourceType.php b/src/ResourceType.php index 159d6116..ba2bb27d 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -47,6 +47,15 @@ public function getClass() return $this->configuration['class']; } + public function getFactory() + { + return $this->configuration['factory'] ?? function () { + $class = $this->getClass(); + + return new $class(); + }; + } + public function getQuery() { return Arr::get($this->configuration, 'query') ?? $this->getClass()::query(); diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index acf6d4b5..a32071c4 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -5,6 +5,7 @@ use ArieTimmerman\Laravel\SCIMServer\SCIM\Schema; use ArieTimmerman\Laravel\SCIMServer\Helper; use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; +use Illuminate\Support\Facades\Auth; class SCIMConfig { @@ -27,6 +28,9 @@ public function getUserConfig() // Set to 'null' to make use of $class::query() 'query' => null, + + // Set to 'null' to make use new $class() + 'factory' => null, 'validations' => [ From df437e39a9009ee0e636d7356cb14de3a7f65826 Mon Sep 17 00:00:00 2001 From: atymic Date: Sun, 1 Oct 2023 06:13:20 +1100 Subject: [PATCH 08/58] fix: set default schema on sub node in AttributeMapping (#50) --- src/Attribute/AttributeMapping.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Attribute/AttributeMapping.php b/src/Attribute/AttributeMapping.php index 4037402e..6980562d 100644 --- a/src/Attribute/AttributeMapping.php +++ b/src/Attribute/AttributeMapping.php @@ -539,6 +539,10 @@ public function getNode($attributePath) foreach ($elements as $element) { try { $node = $node->getSubNode($element, $schema); + + if ($node instanceof AttributeMapping && $this->getDefaultSchema()) { + $node->setDefaultSchema($this->getDefaultSchema()); + } } catch (\Exception $e) { throw $e; } From ff01a451c0bb648d2b048f16149e04bcf0027f34 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Tue, 3 Oct 2023 23:22:19 +0200 Subject: [PATCH 09/58] introduced test for custom schema (#52) --- src/Attribute/Collection.php | 3 - src/Http/Controllers/ResourceController.php | 2 +- tests/BaseTest.php | 46 +++++++++++ tests/BasicTest.php | 73 +++++------------- tests/CustomSchemaTest.php | 85 +++++++++++++++++++++ 5 files changed, 151 insertions(+), 58 deletions(-) create mode 100644 tests/BaseTest.php create mode 100644 tests/CustomSchemaTest.php diff --git a/src/Attribute/Collection.php b/src/Attribute/Collection.php index bb2cf57c..b4d57b0e 100644 --- a/src/Attribute/Collection.php +++ b/src/Attribute/Collection.php @@ -25,9 +25,6 @@ public function add($value, &$object) } } else { foreach ($value as $key => $v) { - // var_dump($value); - // echo $this->getFullKey() . " - " . $key . "\n"; - if ($this->getSubNode($key) != null) { $this->getSubNode($key)->add($v, $object); } else { diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 25158c49..6b24c2c2 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -60,7 +60,7 @@ protected static function validateScim(ResourceType $resourceType, $flattened, ? foreach ($validations as $key => $value) { $simpleValidations[ preg_replace('/([^*])\.([^*])/', '${1}___${2}', $key) - ] = !is_string($value) ? $value : ($resourceObject != null ? preg_replace('/,\[OBJECT_ID\]/', ','.$resourceObject->id, $value) : str_replace(',[OBJECT_ID]', '', $value)); + ] = !is_string($value) ? $value : ($resourceObject != null ? preg_replace('/,\[OBJECT_ID\]/', ',' . $resourceObject->id, $value) : str_replace(',[OBJECT_ID]', '', $value)); } $validator = Validator::make($objectPreparedForValidation, $simpleValidations); diff --git a/tests/BaseTest.php b/tests/BaseTest.php new file mode 100644 index 00000000..8b48196a --- /dev/null +++ b/tests/BaseTest.php @@ -0,0 +1,46 @@ +loadLaravelMigrations('testbench'); + + $this->withFactories(realpath(dirname(__DIR__) . '/database/factories')); + + \ArieTimmerman\Laravel\SCIMServer\RouteProvider::routes(); + + factory(\ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class, 100)->create(); + } + + protected function getEnvironmentSetUp($app) + { + $app ['config']->set('app.url', 'http://localhost'); + $app ['config']->set('app.debug', true); + + $app->register(ServiceProvider::class); + + // Setup default database to use sqlite :memory: + $app['config']->set('scimserver.Users.class', \ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class); + $app['config']->set('auth.providers.users.model', \ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class); + $app['config']->set('database.default', 'testbench'); + $app['config']->set('database.connections.testbench', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } + +} \ No newline at end of file diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 28c50719..486c6d10 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -2,73 +2,38 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; + use ArieTimmerman\Laravel\SCIMServer\ServiceProvider; -use Orchestra\Testbench\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase; -class BasicTest extends TestCase +class BasicTest extends BaseTest { - protected $baseUrl = 'http://localhost'; - - use RefreshDatabase; - - protected function setUp(): void - { - parent::setUp(); - - $this->loadLaravelMigrations('testbench'); - - $this->withFactories(realpath(dirname(__DIR__).'/database/factories')); - - \ArieTimmerman\Laravel\SCIMServer\RouteProvider::routes(); - - factory(\ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class, 100)->create(); - } - - protected function getEnvironmentSetUp($app) - { - $app ['config']->set('app.url', 'http://localhost'); - $app ['config']->set('app.debug', true); - - $app->register(ServiceProvider::class); - - // Setup default database to use sqlite :memory: - $app['config']->set('scimserver.Users.class', \ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class); - $app['config']->set('auth.providers.users.model', \ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class); - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => '', - ]); - } - public function testGet() { $response = $this->get('/scim/v2/Users'); - + $response->assertStatus(200); } public function testPut() { $response = $this->put('/scim/v2/Users/1', [ - "id"=> 1, + "id" => 1, "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:User", ], "urn:ietf:params:scim:schemas:core:2.0:User" => [ - "userName"=> "Dr. John Doe", - "emails"=> [ + "userName" => "Dr. John Doe", + "emails" => [ [ - "value"=> "johndoe@bailey.org", - "type"=> "other", - "primary"=> true + "value" => "johndoe@bailey.org", + "type" => "other", + "primary" => true ] ] ] ]); - + $response->assertStatus(200); $json = $response->json(); @@ -97,7 +62,7 @@ public function testPatch() ] ]] ]); - + $response->assertStatus(200); $json = $response->json(); @@ -115,23 +80,23 @@ public function testDelete() public function testPost() { $response = $this->post('/scim/v2/Users', [ - "id"=> 1, + "id" => 1, "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:User", ], "urn:ietf:params:scim:schemas:core:2.0:User" => [ - "userName"=> "Dr. Marie Jo", - "password"=>"Password123", - "emails"=> [ + "userName" => "Dr. Marie Jo", + "password" => "Password123", + "emails" => [ [ - "value"=> "mariejo@example.com", - "type"=> "primary", - "primary"=> true + "value" => "mariejo@example.com", + "type" => "primary", + "primary" => true ] ] ] ]); - + $this->assertEquals( 201, $response->baseResponse->getStatusCode(), diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php new file mode 100644 index 00000000..60f25ec3 --- /dev/null +++ b/tests/CustomSchemaTest.php @@ -0,0 +1,85 @@ + AttributeMapping::eloquent("employeeNumber") + ]; + + return $config; + } +} + +class CustomSchemaTest extends BaseTest +{ + + + protected function setUp(): void + { + parent::setUp(); + + Schema::table('users', function (Blueprint $table) { + $table->string('employeeNumber')->nullable(); + }); + } + + protected function getEnvironmentSetUp($app) + { + parent::getEnvironmentSetUp($app); + + $app->singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', CustomSCIMConfig::class); + } + + public function testPost() + { + $response = $this->post('/scim/v2/Users', [ + "id" => 1, + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:User", + 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' + ], + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" => [ + 'employeeNumber' => '123' + ], + "urn:ietf:params:scim:schemas:core:2.0:User" => [ + "userName" => "Dr. Marie Jo", + "password" => "Password123", + 'employeeNumber' => '123', + "emails" => [ + [ + "value" => "mariejo@example.com", + "type" => "primary", + "primary" => true + ] + ] + ] + ]); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertEquals('mariejo@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); + $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + $this->assertEquals('123', $json['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['employeeNumber']); + } +} From 590e3a8325820b918366d71b64ee573d9b7af81f Mon Sep 17 00:00:00 2001 From: tovijaeschke <96891811+tovijaeschke@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:01:45 +1030 Subject: [PATCH 10/58] Fix Azure AD SCIM emails/phoneNumbers replace operation with setReplace (#53) https://github.com/arietimmerman/laravel-scim-server/issues/34 --- src/Attribute/Collection.php | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Attribute/Collection.php b/src/Attribute/Collection.php index b4d57b0e..aad85be6 100644 --- a/src/Attribute/Collection.php +++ b/src/Attribute/Collection.php @@ -81,8 +81,11 @@ public function getSubNode($key, $schema = null) if (!empty($this->collection) && is_array($this->collection[0]) && array_key_exists($key, $this->collection[0])) { $parent = $this; - return (new CollectionValue())->setEloquentAttributes($this->collection[0][$key]->getEloquentAttributes())->setKey($key)->setParent($this)->setAdd( - function ($value, &$object) use ($key, $parent) { + return (new CollectionValue()) + ->setEloquentAttributes($this->collection[0][$key]->getEloquentAttributes()) + ->setKey($key) + ->setParent($this) + ->setAdd(function ($value, &$object) use ($key, $parent) { $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); $result = []; @@ -90,9 +93,17 @@ function ($value, &$object) use ($key, $parent) { foreach ($collection as $o) { $o[$key]->add($value, $object); } - } - )->setRead( - function (&$object) use ($key, $parent) { + }) + ->setReplace(function ($value, &$object) use ($key, $parent) { + $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); + + $result = []; + + foreach ($collection as $o) { + $o[$key]->add($value, $object); + } + }) + ->setRead(function (&$object) use ($key, $parent) { $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); $result = []; @@ -102,8 +113,8 @@ function (&$object) use ($key, $parent) { } return $result; - } - )->setSchema($schema); + }) + ->setSchema($schema); } } From 65b46a525e09f4fd5ad111cfed6977d76fadd51b Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Sat, 4 Nov 2023 15:54:43 +0100 Subject: [PATCH 11/58] updated docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44744248..0bf3ea84 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ And optionally php artisan vendor:publish --tag=laravel-scim ~~~ -The module is used by [idaas.nl](https://www.idaas.nl/). +The module is used by [idaas.nl](https://www.idaas.nl/) and by [The SCIM Playground](https://scim.dev). # Routes From f9f48c8a195d0e3127fc791e0eb1736ad4dcf165 Mon Sep 17 00:00:00 2001 From: Nicolas Perraut Date: Fri, 10 Nov 2023 08:13:55 +0100 Subject: [PATCH 12/58] Allow extending RouteProvider to customize route prefix (#54) --- src/RouteProvider.php | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 3543e9b1..74f51101 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -17,10 +17,10 @@ public static function routes(array $options = []) { if (!isset($options['public_routes']) || $options['public_routes'] === true) { - self::publicRoutes($options); + static::publicRoutes($options); } - Route::prefix(self::$prefix)->group( + Route::prefix(static::$prefix)->group( function () use ($options) { Route::prefix('v2')->middleware( [ @@ -30,10 +30,10 @@ function () use ($options) { ] )->group( function () use ($options) { - self::allRoutes($options); + static::allRoutes($options); } ); - + Route::get('v1', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@wrongVersion'); Route::prefix('v1')->group( function () { @@ -46,40 +46,46 @@ function () { public static function meRoutes(array $options = []) { - Route::get('/scim/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@getMe')->name('scim.me.get'); - Route::put('/scim/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@replaceMe')->name('scim.me.put'); + Route::prefix(static::$prefix)->group(function () { + Route::get("/v2/Me", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@getMe')->name('scim.me.get'); + Route::put('/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@replaceMe')->name('scim.me.put'); + }); } public static function meRoutePost(array $options = []) { - Route::post('/scim/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@createMe')->name('scim.me.post'); + Route::prefix(static::$prefix)->group(function () { + Route::post('/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@createMe')->name('scim.me.post'); + }); } public static function publicRoutes(array $options = []) { - Route::get("/scim/v2/ServiceProviderConfig", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ServiceProviderController@index')->name('scim.serviceproviderconfig'); - - Route::get("/scim/v2/Schemas", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@index'); - Route::get("/scim/v2/Schemas/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@show')->name('scim.schemas'); - - Route::get("/scim/v2/ResourceTypes", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@index'); - Route::get("/scim/v2/ResourceTypes/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@show')->name('scim.resourcetype'); + Route::prefix(static::$prefix)->group(function () { + Route::get("/v2/ServiceProviderConfig", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ServiceProviderController@index')->name('scim.serviceproviderconfig'); + + Route::get("/v2/Schemas", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@index'); + Route::get("/v2/Schemas/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@show')->name('scim.schemas'); + + Route::get("/v2/ResourceTypes", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@index'); + Route::get("/v2/ResourceTypes/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@show')->name('scim.resourcetype'); + }); } private static function allRoutes(array $options = []) { Route::post('.search', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@notImplemented'); - + // TODO: Use the attributes parameters ?attributes=userName, excludedAttributes=asdg,asdg (respect "returned" settings "always") Route::get('/{resourceType}/{resourceObject}', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@show')->name('scim.resource'); Route::get("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@index')->name('scim.resources'); - + Route::post("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@create'); - + Route::put("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@replace'); Route::patch("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@update'); Route::delete("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@delete'); - + Route::fallback('\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@notImplemented'); } } From 397ee690f52908a3307d34e27608ef56b6b10c7a Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Sat, 18 Nov 2023 10:52:15 +0100 Subject: [PATCH 13/58] Support for Bulk Operations (#55) --- src/Http/Controllers/BulkController.php | 114 ++++++++++++++++++ .../Controllers/ServiceProviderController.php | 4 +- src/RouteProvider.php | 2 + 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/Http/Controllers/BulkController.php diff --git a/src/Http/Controllers/BulkController.php b/src/Http/Controllers/BulkController.php new file mode 100644 index 00000000..e7ecedea --- /dev/null +++ b/src/Http/Controllers/BulkController.php @@ -0,0 +1,114 @@ +getContent(), '8bit'); + + if($contentSize > static::MAX_PAYLOAD_SIZE){ + throw (new SCIMException('Payload too large!'))->setCode(413)->setScimType('tooLarge'); + } + + $validator = Validator::make($request->input(), [ + 'schemas' => 'required|array', + 'schemas.*' => 'required|string|in:urn:ietf:params:scim:api:messages:2.0:BulkRequest', + // TODO: implement failOnErrors + 'failOnErrors' => 'nullable|int', + 'Operations' => 'required|array', + 'Operations.*.method' => 'required|string|in:POST,PUT,PATCH,DELETE', + 'Operations.*.path' => 'required|string|in:/Users,/Groups', + 'Operations.*.bulkId' => 'nullable|string', + 'Operations.*.data' => 'nullable|array', + ]); + + if ($validator->fails()) { + $e = $validator->errors(); + + throw (new SCIMException('Invalid data!'))->setCode(400)->setScimType('invalidSyntax')->setErrors($e); + } + + $operations = $request->input('Operations'); + + if(count($operations) > static::MAX_OPERATIONS){ + throw (new SCIMException('Too many operations!'))->setCode(413)->setScimType('tooLarge'); + } + + $bulkIdMapping = []; + $responses = []; + + // Remove everything till the last occurence of Bulk, e.g. /scim/v2/Bulk should become /scim/v2/ + $prefix = substr($request->path(), 0, strrpos($request->path(), '/Bulk')); + + foreach ($operations as $operation) { + + $method = $operation['method']; + $bulkId = $operation['bulkId'] ?? null; + + // Call internal Laravel route based on method, path and data + $encoded = json_encode($operation['data'] ?? []); + $encoded = str_replace(array_keys($bulkIdMapping), array_values($bulkIdMapping), $encoded); + + $request = Request::create( + $prefix . $operation['path'], + $operation['method'], + server: [ + 'HTTP_Authorization' => $request->header('Authorization'), + 'CONTENT_TYPE' => 'application/scim+json', + ], + content: $encoded + ); + + // run request and get response + /** @var \Illuminate\Http\Response */ + $response = app()->handle($request); + // Get the JSON content of the response + $jsonContent = $response->getContent(); + // Decode the JSON content + $responseData = json_decode($jsonContent, false); + + // Store the id attribute + $id = $responseData?->id ?? null; + + // Store the id attribute in the bulkIdMapping array + if ($bulkId !== null && $id !== null) { + $bulkIdMapping['bulkId:' . $bulkId] = $id; + } + + $responses[] = array_filter([ + "location" => $responseData?->meta?->location ?? null, + "method" => $method, + "bulkId" => $bulkId, + "version" => $responseData?->meta?->version ?? null, + "status" => $response->getStatusCode(), + "response" => $response->getStatusCode() >= 400 ? $responseData : null, + ]); + } + + // Return a response indicating the successful processing of the SCIM BULK request + return response()->json( + [ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:BulkResponse'], + 'Operations' => + $responses])->setStatusCode(200) + ->withHeaders(['Content-Type' => 'application/scim+json']); + } +} diff --git a/src/Http/Controllers/ServiceProviderController.php b/src/Http/Controllers/ServiceProviderController.php index eb2cea0b..d97d8b3c 100644 --- a/src/Http/Controllers/ServiceProviderController.php +++ b/src/Http/Controllers/ServiceProviderController.php @@ -14,7 +14,9 @@ public function index() "supported" => true, ], "bulk" => [ - "supported" => false, + "supported" => true, + "maxPayloadSize" => BulkController::MAX_PAYLOAD_SIZE, + "maxOperations" => BulkController::MAX_OPERATIONS ], "filter" => [ "supported" => true, diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 74f51101..82bb15eb 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -76,6 +76,8 @@ private static function allRoutes(array $options = []) { Route::post('.search', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@notImplemented'); + Route::post("/Bulk", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\BulkController@processBulkRequest'); + // TODO: Use the attributes parameters ?attributes=userName, excludedAttributes=asdg,asdg (respect "returned" settings "always") Route::get('/{resourceType}/{resourceObject}', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@show')->name('scim.resource'); Route::get("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@index')->name('scim.resources'); From 6ee3e16bfd501ad81ed96db9e1cb03034c023c8a Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Sat, 18 Nov 2023 11:12:32 +0100 Subject: [PATCH 14/58] fixes bulkid support (#56) --- src/Http/Controllers/BulkController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Http/Controllers/BulkController.php b/src/Http/Controllers/BulkController.php index e7ecedea..1febf40a 100644 --- a/src/Http/Controllers/BulkController.php +++ b/src/Http/Controllers/BulkController.php @@ -35,7 +35,6 @@ public function processBulkRequest(Request $request) 'failOnErrors' => 'nullable|int', 'Operations' => 'required|array', 'Operations.*.method' => 'required|string|in:POST,PUT,PATCH,DELETE', - 'Operations.*.path' => 'required|string|in:/Users,/Groups', 'Operations.*.bulkId' => 'nullable|string', 'Operations.*.data' => 'nullable|array', ]); @@ -66,9 +65,15 @@ public function processBulkRequest(Request $request) // Call internal Laravel route based on method, path and data $encoded = json_encode($operation['data'] ?? []); $encoded = str_replace(array_keys($bulkIdMapping), array_values($bulkIdMapping), $encoded); + $path = str_replace(array_keys($bulkIdMapping), array_values($bulkIdMapping), $operation['path']); + + // ensure $path starts with /Users or /Groups + if (!preg_match('/^\/(Users|Groups)/', $path)) { + throw (new SCIMException('Invalid path!'))->setCode(400)->setScimType('invalidPath'); + } $request = Request::create( - $prefix . $operation['path'], + $prefix . $path, $operation['method'], server: [ 'HTTP_Authorization' => $request->header('Authorization'), From cc954cb79316736d9edc3f224f952aedd708a66f Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 20 Dec 2023 23:54:48 +0100 Subject: [PATCH 15/58] Enriched events (#58) --- src/Events/AbstractEvent.php | 23 +++++++++++++++++++++ src/Events/Create.php | 19 ----------------- src/Events/Delete.php | 15 -------------- src/Events/EventInterface.php | 3 ++- src/Events/Get.php | 15 -------------- src/Events/Patch.php | 17 +-------------- src/Events/Replace.php | 17 +-------------- src/Http/Controllers/ResourceController.php | 10 ++++----- src/ResourceType.php | 1 - 9 files changed, 32 insertions(+), 88 deletions(-) diff --git a/src/Events/AbstractEvent.php b/src/Events/AbstractEvent.php index 472065da..9766da3a 100644 --- a/src/Events/AbstractEvent.php +++ b/src/Events/AbstractEvent.php @@ -2,11 +2,14 @@ namespace ArieTimmerman\Laravel\SCIMServer\Events; +use ArieTimmerman\Laravel\SCIMServer\ResourceType; use Illuminate\Queue\SerializesModels; use Illuminate\Database\Eloquent\Model; abstract class AbstractEvent implements EventInterface { + use SerializesModels; + public $model; /** @@ -14,6 +17,12 @@ abstract class AbstractEvent implements EventInterface */ public $me; + public $resourceType; + + public $oldObjectArray; + + public $input; + public function getModel() { return $this->model; @@ -23,4 +32,18 @@ public function isMe() { return $this->me; } + + public function getResourceType(): ResourceType + { + return $this->resourceType; + } + + public function __construct(Model $model, ResourceType $resourceType, bool $me = null, $input, $odlObjectArray = []) + { + $this->model = $model; + $this->resourceType = $resourceType; + $this->me = $me; + $this->input = $input; + $this->oldObjectArray = $odlObjectArray; + } } diff --git a/src/Events/Create.php b/src/Events/Create.php index bca78137..9747031c 100644 --- a/src/Events/Create.php +++ b/src/Events/Create.php @@ -2,25 +2,6 @@ namespace ArieTimmerman\Laravel\SCIMServer\Events; -use Illuminate\Queue\SerializesModels; -use Illuminate\Database\Eloquent\Model; - class Create extends AbstractEvent { - use SerializesModels; - - public $model; - public $me; - - /** - * Create a new event instance. - * - * @param \App\Order $order - * @return void - */ - public function __construct(Model $model, bool $me = null) - { - $this->model = $model; - $this->me = $me; - } } diff --git a/src/Events/Delete.php b/src/Events/Delete.php index 0af8caa8..4b905f2f 100644 --- a/src/Events/Delete.php +++ b/src/Events/Delete.php @@ -7,19 +7,4 @@ class Delete extends AbstractEvent { - use SerializesModels; - - public $model; - - /** - * Create a new event instance. - * - * @param \App\Order $order - * @return void - */ - public function __construct(Model $model, bool $me = null) - { - $this->model = $model; - $this->me = $me; - } } diff --git a/src/Events/EventInterface.php b/src/Events/EventInterface.php index 23bc9dde..d3eed147 100644 --- a/src/Events/EventInterface.php +++ b/src/Events/EventInterface.php @@ -2,9 +2,10 @@ namespace ArieTimmerman\Laravel\SCIMServer\Events; +use ArieTimmerman\Laravel\SCIMServer\ResourceType; use Illuminate\Database\Eloquent\Model; interface EventInterface { - public function __construct(Model $model, bool $me = null); + public function __construct(Model $model, ResourceType $resourceType, bool $me = null, $input, $odlObjectArray = []); } diff --git a/src/Events/Get.php b/src/Events/Get.php index bda525c4..80a3c986 100644 --- a/src/Events/Get.php +++ b/src/Events/Get.php @@ -7,19 +7,4 @@ class Get extends AbstractEvent { - use SerializesModels; - - public $model; - - /** - * Create a new event instance. - * - * @param \App\Order $order - * @return void - */ - public function __construct(Model $model, bool $me = null) - { - $this->model = $model; - $this->me = $me; - } } diff --git a/src/Events/Patch.php b/src/Events/Patch.php index 58a72c51..36463d00 100644 --- a/src/Events/Patch.php +++ b/src/Events/Patch.php @@ -2,25 +2,10 @@ namespace ArieTimmerman\Laravel\SCIMServer\Events; +use ArieTimmerman\Laravel\SCIMServer\ResourceType; use Illuminate\Queue\SerializesModels; use Illuminate\Database\Eloquent\Model; class Patch extends AbstractEvent { - use SerializesModels; - - public $odlObjectArray; - - /** - * Create a new event instance. - * - * @param \App\Order $order - * @return void - */ - public function __construct(Model $model, bool $me = null, $odlObjectArray = []) - { - $this->model = $model; - $this->me = $me; - $this->odlObjectArray = $odlObjectArray; - } } diff --git a/src/Events/Replace.php b/src/Events/Replace.php index 5e4451af..1f83a21a 100644 --- a/src/Events/Replace.php +++ b/src/Events/Replace.php @@ -2,25 +2,10 @@ namespace ArieTimmerman\Laravel\SCIMServer\Events; +use ArieTimmerman\Laravel\SCIMServer\ResourceType; use Illuminate\Queue\SerializesModels; use Illuminate\Database\Eloquent\Model; class Replace extends AbstractEvent { - use SerializesModels; - - public $odlObjectArray; - - /** - * Create a new event instance. - * - * @param \App\Order $order - * @return void - */ - public function __construct(Model $model, bool $me = null, $odlObjectArray = []) - { - $this->model = $model; - $this->me = $me; - $this->odlObjectArray = $odlObjectArray; - } } diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 6b24c2c2..6376f11a 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -141,7 +141,7 @@ public function createObject(Request $request, PolicyDecisionPoint $pdp, Resourc $resourceObject = self::createFromSCIM($resourceType, $input, $pdp, $request, false, $isMe); - event(new Create($resourceObject, $isMe)); + event(new Create($resourceObject, $resourceType, $isMe, $request->input())); return $resourceObject; } @@ -163,7 +163,7 @@ public function create(Request $request, PolicyDecisionPoint $pdp, ResourceType public function show(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType, Model $resourceObject) { - event(new Get($resourceObject)); + event(new Get($resourceObject, $resourceType, null, $request->input())); return Helper::objectToSCIMResponse($resourceObject, $resourceType); } @@ -172,7 +172,7 @@ public function delete(Request $request, PolicyDecisionPoint $pdp, ResourceType { $resourceObject->delete(); - event(new Delete($resourceObject)); + event(new Delete($resourceObject, $resourceType, null, $request->input())); return response(null, 204); } @@ -232,7 +232,7 @@ public function replace(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceObject->save(); - event(new Replace($resourceObject, $isMe, $originalRaw)); + event(new Replace($resourceObject, $resourceType, $isMe, $request->input(), $originalRaw)); return Helper::objectToSCIMResponse($resourceObject, $resourceType); } @@ -319,7 +319,7 @@ public function update(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceObject->save(); - event(new Patch($resourceObject, $isMe, $oldObject)); + event(new Patch($resourceObject, $resourceType, $isMe, $request->input(), $oldObject)); return Helper::objectToSCIMResponse($resourceObject, $resourceType); } diff --git a/src/ResourceType.php b/src/ResourceType.php index ba2bb27d..3b662a79 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -23,7 +23,6 @@ public function getConfiguration() public function getMapping() { - $mapping = $this->configuration['mapping']; if (!isset($mapping['schema'])) { From b3d7feb6e4696b30a9642666bf446c1b139786ba Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Fri, 22 Mar 2024 14:01:41 +0100 Subject: [PATCH 16/58] Fix compatibility with PhpUnit 10 and newer (#64) --- tests/BasicTest.php | 6 +----- tests/CustomSchemaTest.php | 4 +--- tests/{BaseTest.php => TestCase.php} | 4 ++-- 3 files changed, 4 insertions(+), 10 deletions(-) rename tests/{BaseTest.php => TestCase.php} (93%) diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 486c6d10..212d811d 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -2,11 +2,7 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; - -use ArieTimmerman\Laravel\SCIMServer\ServiceProvider; -use Illuminate\Foundation\Testing\RefreshDatabase; - -class BasicTest extends BaseTest +class BasicTest extends TestCase { public function testGet() { diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php index 60f25ec3..b1e25f3b 100644 --- a/tests/CustomSchemaTest.php +++ b/tests/CustomSchemaTest.php @@ -24,10 +24,8 @@ public function getUserConfig() } } -class CustomSchemaTest extends BaseTest +class CustomSchemaTest extends TestCase { - - protected function setUp(): void { parent::setUp(); diff --git a/tests/BaseTest.php b/tests/TestCase.php similarity index 93% rename from tests/BaseTest.php rename to tests/TestCase.php index 8b48196a..9636015f 100644 --- a/tests/BaseTest.php +++ b/tests/TestCase.php @@ -3,10 +3,10 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; use ArieTimmerman\Laravel\SCIMServer\ServiceProvider; -use Orchestra\Testbench\TestCase; +use Orchestra\Testbench\TestCase as BaseTestCase; use Illuminate\Foundation\Testing\RefreshDatabase; -abstract class BaseTest extends TestCase +abstract class TestCase extends BaseTestCase { use RefreshDatabase; From 3bf03eb7625ca01785005d9ca4483e476a0830fa Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Fri, 22 Mar 2024 15:18:24 +0100 Subject: [PATCH 17/58] Add support for Laravel 11 (#62) --- .github/workflows/ci.yml | 10 +++++++++- composer.json | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b47842e6..dd7ff25b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: php: [7.3, 7.4, 8.0, 8.1, 8.2] - laravel: [6.*, 7.*, 8.*, 9.*, 10.*] + laravel: [6.*, 7.*, 8.*, 9.*, 10.*, 11.*] exclude: - laravel: 6.* php: 8.1 @@ -29,6 +29,14 @@ jobs: php: 7.4 - laravel: 10.* php: 8.0 + - laravel: 11.* + php: 7.3 + - laravel: 11.* + php: 7.4 + - laravel: 11.* + php: 8.0 + - laravel: 11.* + php: 8.1 name: PHP ${{ matrix.php }} on Laravel ${{ matrix.laravel }} diff --git a/composer.json b/composer.json index f97cb517..d8e5f958 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^7.0|^8.0", - "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, @@ -29,7 +29,7 @@ } }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0", "laravel/legacy-factories": "*" }, From 403b1b2c8e576a94d9e2bb6391bcc5d39c7e96f5 Mon Sep 17 00:00:00 2001 From: juanf Date: Thu, 6 Jun 2024 02:46:11 -0300 Subject: [PATCH 18/58] Fixes endpoint /scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group (#69) Co-authored-by: Juan Ferrari --- src/Http/Controllers/SchemaController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Controllers/SchemaController.php b/src/Http/Controllers/SchemaController.php index 5fd151b3..b70aee6f 100644 --- a/src/Http/Controllers/SchemaController.php +++ b/src/Http/Controllers/SchemaController.php @@ -22,7 +22,7 @@ public function getSchemas() $schemas = []; foreach ($config as $key => $value) { - if ($key != 'Users' && $key != 'Group') { + if ($key != 'Users' && $key != 'Groups') { continue; } From 6a2b207c8325ad39cd75d9ffe5992a8c27f64307 Mon Sep 17 00:00:00 2001 From: juanf Date: Thu, 6 Jun 2024 11:09:37 -0300 Subject: [PATCH 19/58] =?UTF-8?q?Fix=20exception=20not=20accepting=20code?= =?UTF-8?q?=20on=20the=20constructor.=20Fix=20409=20error=20to=E2=80=A6=20?= =?UTF-8?q?(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix exception not accepting code on the constructor. Fix 409 error to match spec https://datatracker.ietf.org/doc/html/rfc7644#section-3.3 --------- Co-authored-by: Juan Ferrari --- src/Exceptions/SCIMException.php | 5 +++-- src/SCIM/Error.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Exceptions/SCIMException.php b/src/Exceptions/SCIMException.php index 6e788a7b..ef3f60b2 100644 --- a/src/Exceptions/SCIMException.php +++ b/src/Exceptions/SCIMException.php @@ -12,9 +12,10 @@ class SCIMException extends Exception protected $errors = []; - public function __construct($message) + public function __construct($message, $code = 404) { - parent::__construct($message); + parent::__construct($message, $code); + $this->setCode($code); } public function setScimType($scimType) : SCIMException diff --git a/src/SCIM/Error.php b/src/SCIM/Error.php index ee4c7811..4c6edad3 100644 --- a/src/SCIM/Error.php +++ b/src/SCIM/Error.php @@ -19,7 +19,7 @@ public function toJson($options = 0) "schemas" => ['urn:ietf:params:scim:api:messages:2.0:Error'], "detail" => $this->detail, "status" => $this->status, - "scimType" => ($this->status == 400 ? $this->scimType : null), + "scimType" => ($this->status == 400 ? $this->scimType : ($this->status == 409 ? 'uniqueness' : null)), // not defined in SCIM 2.0 'errors' => $this->errors From f72963dc46f867cf147c93320d5baf50d63bdd96 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 26 Jun 2024 16:37:40 +0200 Subject: [PATCH 20/58] Simplified validation (#71) * dropped support for laravel 6 --- .github/workflows/ci.yml | 6 +-- composer.json | 6 +-- composer.lock | 2 +- src/Http/Controllers/ResourceController.php | 57 ++------------------- src/SCIMConfig.php | 14 ++--- tests/CustomSchemaTest.php | 2 +- 6 files changed, 18 insertions(+), 69 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd7ff25b..78dfbfbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,12 +9,8 @@ jobs: strategy: matrix: php: [7.3, 7.4, 8.0, 8.1, 8.2] - laravel: [6.*, 7.*, 8.*, 9.*, 10.*, 11.*] + laravel: [7.*, 8.*, 9.*, 10.*, 11.*] exclude: - - laravel: 6.* - php: 8.1 - - laravel: 6.* - php: 8.2 - laravel: 7.* php: 8.1 - laravel: 7.* diff --git a/composer.json b/composer.json index d8e5f958..a19e745e 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^7.0|^8.0", - "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/database": "^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^7.0|^8.0|^9.0|^10.0|^11.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, diff --git a/composer.lock b/composer.lock index 581c14e6..ccd197c2 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": "1f53fbc4eae4d64b08077692ce5a2aa4", + "content-hash": "08f1170fee904936c4acd85558b56b82", "packages": [ { "name": "brick/math", diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 6376f11a..2a9ef71f 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -27,72 +27,25 @@ protected static function isAllowed(PolicyDecisionPoint $pdp, Request $request, return $pdp->isAllowed($request, $operation, $attributes, $resourceType, $resourceObject, $isMe); } - protected static function replaceKeys(array $input) - { - $return = array(); - foreach ($input as $key => $value) { - if (strpos($key, '_') > 0) { - $key = str_replace('___', '.', $key); - } - - if (is_array($value)) { - $value = self::replaceKeys($value); - } - - $return[$key] = $value; - } - return $return; - } - protected static function validateScim(ResourceType $resourceType, $flattened, ?Model $resourceObject) { - $objectPreparedForValidation = []; $validations = $resourceType->getValidations(); - $simpleValidations = []; - - /** - * Dots have a different meaning in SCIM and in Laravel's validation logic - */ - foreach ($flattened as $key => $value) { - $objectPreparedForValidation[preg_replace('/([^*])\.([^*])/', '${1}___${2}', $key)] = $value; - } foreach ($validations as $key => $value) { - $simpleValidations[ - preg_replace('/([^*])\.([^*])/', '${1}___${2}', $key) - ] = !is_string($value) ? $value : ($resourceObject != null ? preg_replace('/,\[OBJECT_ID\]/', ',' . $resourceObject->id, $value) : str_replace(',[OBJECT_ID]', '', $value)); + if (is_string($value)) { + $validations[$key] = $resourceObject ? preg_replace('/,\[OBJECT_ID\]/', ',' . $resourceObject->id, $value) : str_replace(',[OBJECT_ID]', '', $value); + } } - $validator = Validator::make($objectPreparedForValidation, $simpleValidations); + $validator = Validator::make($flattened, $validations); if ($validator->fails()) { $e = $validator->errors(); - $e = self::replaceKeys($e->toArray()); throw (new SCIMException('Invalid data!'))->setCode(400)->setScimType('invalidSyntax')->setErrors($e); } - $validTemp = $validator->validate(); - $valid = []; - - $keys = collect($simpleValidations)->keys()->map( - function ($rule) { - return explode('.', $rule)[0]; - } - )->unique()->toArray(); - - foreach ($keys as $key) { - if (array_key_exists($key, $validTemp)) { - $valid[$key] = $validTemp[$key]; - } - } - - $flattened = []; - foreach ($valid as $key => $value) { - $flattened[str_replace(['___'], ['.'], $key)] = $value; - } - - return $flattened; + return $validator->validate(); } public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint $pdp = null, Request $request = null, $allowAlways = false, $isMe = false) diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index a32071c4..c5e3e995 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -34,13 +34,13 @@ public function getUserConfig() 'validations' => [ - 'urn:ietf:params:scim:schemas:core:2.0:User:userName' => 'required', - 'urn:ietf:params:scim:schemas:core:2.0:User:password' => 'nullable', - 'urn:ietf:params:scim:schemas:core:2.0:User:active' => 'boolean', - 'urn:ietf:params:scim:schemas:core:2.0:User:emails' => 'required|array', - 'urn:ietf:params:scim:schemas:core:2.0:User:emails.*.value' => 'required|email', - 'urn:ietf:params:scim:schemas:core:2.0:User:roles' => 'nullable|array', - 'urn:ietf:params:scim:schemas:core:2.0:User:roles.*.value' => 'required', + 'urn:ietf:params:scim:schemas:core:2\.0:User:userName' => 'required', + 'urn:ietf:params:scim:schemas:core:2\.0:User:password' => 'nullable', + 'urn:ietf:params:scim:schemas:core:2\.0:User:active' => 'boolean', + 'urn:ietf:params:scim:schemas:core:2\.0:User:emails' => 'required|array', + 'urn:ietf:params:scim:schemas:core:2\.0:User:emails.*.value' => 'required|email', + 'urn:ietf:params:scim:schemas:core:2\.0:User:roles' => 'nullable|array', + 'urn:ietf:params:scim:schemas:core:2\.0:User:roles.*.value' => 'required', ], diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php index b1e25f3b..8cd66d00 100644 --- a/tests/CustomSchemaTest.php +++ b/tests/CustomSchemaTest.php @@ -14,7 +14,7 @@ public function getUserConfig() $config = parent::getUserConfig(); $config['schemas'][] = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'; - $config['validations']['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber'] = 'nullable'; + $config['validations']['urn:ietf:params:scim:schemas:extension:enterprise:2\.0:User:employeeNumber'] = 'nullable'; $config['mapping']['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'] = [ 'employeeNumber' => AttributeMapping::eloquent("employeeNumber") From 38d4eec9fc416eceebbf2eb7c9efbe8269b4fe12 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Fri, 5 Jul 2024 19:45:17 +0200 Subject: [PATCH 21/58] Improved Schema setup and handling (#72) --- .github/workflows/ci.yml | 2 +- composer.json | 2 +- composer.lock | 4172 +++++++++++------ database/factories/GroupFactory.php | 11 + phpunit.xml | 2 - src/Attribute/AbstractComplex.php | 71 + src/Attribute/Attribute.php | 322 ++ src/Attribute/AttributeMapping.php | 639 --- src/Attribute/AttributeValue.php | 13 + src/Attribute/Collection.php | 244 +- src/Attribute/CollectionValue.php | 7 - src/Attribute/Complex.php | 225 + src/Attribute/Constant.php | 45 + src/Attribute/Eloquent.php | 125 + src/Attribute/EloquentAttributeMapping.php | 11 - src/Attribute/Meta.php | 34 + src/Attribute/MutableCollection.php | 76 + src/Attribute/Schema.php | 29 + src/Helper.php | 176 +- src/Http/Controllers/ResourceController.php | 143 +- .../Controllers/ResourceTypesController.php | 5 +- src/Http/Controllers/SchemaController.php | 33 +- src/Parser/AttributePath.php | 21 + src/Parser/Filter.php | 15 + src/Parser/Parser.php | 34 + src/Parser/Path.php | 98 + src/Parser/ValuePath.php | 45 + src/ResourceType.php | 41 +- src/RouteProvider.php | 3 - src/SCIM/ListResponse.php | 8 +- src/SCIMConfig.php | 314 +- src/ServiceProvider.php | 1 - tests/BasicTest.php | 204 +- tests/CustomSchemaTest.php | 6 +- tests/GroupsTest.php | 116 + tests/Model/Group.php | 14 + tests/Model/User.php | 4 + tests/ResourceTypesTest.php | 44 + tests/SchemaTest.php | 45 + tests/ServiceProviderConfigTest.php | 21 + tests/TestCase.php | 35 +- 41 files changed, 4633 insertions(+), 2823 deletions(-) create mode 100644 database/factories/GroupFactory.php create mode 100644 src/Attribute/AbstractComplex.php create mode 100644 src/Attribute/Attribute.php delete mode 100644 src/Attribute/AttributeMapping.php create mode 100644 src/Attribute/AttributeValue.php delete mode 100644 src/Attribute/CollectionValue.php create mode 100644 src/Attribute/Complex.php create mode 100644 src/Attribute/Constant.php create mode 100644 src/Attribute/Eloquent.php delete mode 100644 src/Attribute/EloquentAttributeMapping.php create mode 100644 src/Attribute/Meta.php create mode 100644 src/Attribute/MutableCollection.php create mode 100644 src/Attribute/Schema.php create mode 100644 src/Parser/AttributePath.php create mode 100644 src/Parser/Filter.php create mode 100644 src/Parser/Parser.php create mode 100644 src/Parser/Path.php create mode 100644 src/Parser/ValuePath.php create mode 100644 tests/GroupsTest.php create mode 100644 tests/Model/Group.php create mode 100644 tests/ResourceTypesTest.php create mode 100644 tests/SchemaTest.php create mode 100644 tests/ServiceProviderConfigTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78dfbfbb..2405a7e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: continue-on-error: true strategy: matrix: - php: [7.3, 7.4, 8.0, 8.1, 8.2] + php: [8.0, 8.1, 8.2] laravel: [7.*, 8.*, 9.*, 10.*, 11.*] exclude: - laravel: 7.* diff --git a/composer.json b/composer.json index a19e745e..6f387e0e 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "php": "^7.0|^8.0", + "php": "^8.0", "illuminate/database": "^7.0|^8.0|^9.0|^10.0|^11.0", "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0", "illuminate/console": "^7.0|^8.0|^9.0|^10.0|^11.0", diff --git a/composer.lock b/composer.lock index ccd197c2..6380c693 100644 --- a/composer.lock +++ b/composer.lock @@ -4,30 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "08f1170fee904936c4acd85558b56b82", + "content-hash": "35d7d3286fcc782631ce7311b28f4707", "packages": [ { "name": "brick/math", - "version": "0.10.2", + "version": "0.12.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f" + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f", - "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.0", - "vimeo/psalm": "4.25.0" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", "autoload": { @@ -47,12 +46,17 @@ "arithmetic", "bigdecimal", "bignum", + "bignumber", "brick", - "math" + "decimal", + "integer", + "math", + "mathematics", + "rational" ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.10.2" + "source": "https://github.com/brick/math/tree/0.12.1" }, "funding": [ { @@ -60,7 +64,76 @@ "type": "github" } ], - "time": "2022-08-10T22:54:19+00:00" + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-12-11T17:09:12+00:00" }, { "name": "dflydev/dot-access-data", @@ -139,28 +212,28 @@ }, { "name": "doctrine/inflector", - "version": "2.0.6", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^10", + "doctrine/coding-standard": "^11.0", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.3", "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25" + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", "autoload": { @@ -210,7 +283,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.6" + "source": "https://github.com/doctrine/inflector/tree/2.0.10" }, "funding": [ { @@ -226,35 +299,36 @@ "type": "tidelift" } ], - "time": "2022-10-20T09:10:12+00:00" + "time": "2024-02-18T20:23:39+00:00" }, { "name": "doctrine/lexer", - "version": "1.2.3", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -286,7 +360,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -302,20 +376,20 @@ "type": "tidelift" } ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -355,7 +429,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -363,31 +437,30 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "egulias/email-validator", - "version": "2.1.25", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4" + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { - "doctrine/lexer": "^1.0.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.10" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "dominicsayers/isemail": "^3.0.7", - "phpunit/phpunit": "^4.8.36|^7.5.15", - "satooshi/php-coveralls": "^1.0.1" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -395,7 +468,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -423,7 +496,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { @@ -431,28 +504,99 @@ "type": "github" } ], - "time": "2020-12-29T14:50:06+00:00" + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.1", + "version": "v1.1.2", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.1" + "phpoption/phpoption": "^1.9.2" }, "require-dev": { - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "autoload": { @@ -481,7 +625,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" }, "funding": [ { @@ -493,60 +637,160 @@ "type": "tidelift" } ], - "time": "2023-02-25T20:23:15+00:00" + "time": "2023-11-12T22:16:48+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2023-12-03T19:50:20+00:00" }, { "name": "laravel/framework", - "version": "v8.83.27", + "version": "v10.48.15", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49" + "reference": "1723e038c9ec99b434e5bed9c27f044bd50a4ffb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49", - "reference": "e1afe088b4ca613fb96dc57e6d8dbcb8cc2c6b49", + "url": "https://api.github.com/repos/laravel/framework/zipball/1723e038c9ec99b434e5bed9c27f044bd50a4ffb", + "reference": "1723e038c9ec99b434e5bed9c27f044bd50a4ffb", "shasum": "" }, "require": { - "doctrine/inflector": "^1.4|^2.0", - "dragonmantank/cron-expression": "^3.0.2", - "egulias/email-validator": "^2.1.10", - "ext-json": "*", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", "ext-mbstring": "*", "ext-openssl": "*", - "laravel/serializable-closure": "^1.0", - "league/commonmark": "^1.3|^2.0.2", - "league/flysystem": "^1.1", - "monolog/monolog": "^2.0", - "nesbot/carbon": "^2.53.1", - "opis/closure": "^3.6", - "php": "^7.3|^8.0", - "psr/container": "^1.0", - "psr/log": "^1.0|^2.0", - "psr/simple-cache": "^1.0", - "ramsey/uuid": "^4.2.2", - "swiftmailer/swiftmailer": "^6.3", - "symfony/console": "^5.4", - "symfony/error-handler": "^5.4", - "symfony/finder": "^5.4", - "symfony/http-foundation": "^5.4", - "symfony/http-kernel": "^5.4", - "symfony/mime": "^5.4", - "symfony/process": "^5.4", - "symfony/routing": "^5.4", - "symfony/var-dumper": "^5.4", - "tijsverkoyen/css-to-inline-styles": "^2.2.2", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^1.6.1" + "voku/portable-ascii": "^2.0" }, "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", + "phpunit/phpunit": ">=11.0.0", "tightenco/collect": "<5.5.33" }, "provide": { - "psr/container-implementation": "1.0", - "psr/simple-cache-implementation": "1.0" + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" }, "replace": { "illuminate/auth": "self.version", @@ -554,6 +798,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", "illuminate/container": "self.version", @@ -571,6 +816,7 @@ "illuminate/notifications": "self.version", "illuminate/pagination": "self.version", "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", "illuminate/routing": "self.version", @@ -582,59 +828,76 @@ "illuminate/view": "self.version" }, "require-dev": { - "aws/aws-sdk-php": "^3.198.1", - "doctrine/dbal": "^2.13.3|^3.1.4", - "filp/whoops": "^2.14.3", - "guzzlehttp/guzzle": "^6.5.5|^7.0.1", - "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^6.27", + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.23.4", "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.5.19|^9.5.8", - "predis/predis": "^1.1.9", - "symfony/cache": "^5.4" + "phpstan/phpstan": "^1.4.7", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", - "ext-bcmath": "Required to use the multiple_of validation rule.", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", "ext-memcached": "Required to use the memcache cache driver.", - "ext-pcntl": "Required to use all features of the queue worker.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", - "mockery/mockery": "Required to use mocking (^1.4.4).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^8.5.19|^9.5.8).", - "predis/predis": "Required to use the predis connector (^1.1.9).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.4).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { "files": [ "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], @@ -642,7 +905,8 @@ "Illuminate\\": "src/Illuminate/", "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", - "src/Illuminate/Collections/" + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" ] } }, @@ -666,20 +930,78 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-12-08T15:28:55+00:00" + "time": "2024-07-02T18:01:24+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.24", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "409b0b4305273472f3754826e68f4edbd0150149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/409b0b4305273472f3754826e68f4edbd0150149", + "reference": "409b0b4305273472f3754826e68f4edbd0150149", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.24" + }, + "time": "2024-06-17T13:58:22+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.0", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", "shasum": "" }, "require": { @@ -726,20 +1048,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-01-30T18:31:20+00:00" + "time": "2023-11-08T14:08:06+00:00" }, { "name": "league/commonmark", - "version": "2.3.9", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5" + "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c1e114f74e518daca2729ea8c4bf1167038fa4b5", - "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", "shasum": "" }, "require": { @@ -752,7 +1074,7 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.0", + "commonmark/cmark": "0.30.3", "commonmark/commonmark.js": "0.30.0", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", @@ -762,10 +1084,10 @@ "michelf/php-markdown": "^1.4 || ^2.0", "nyholm/psr7": "^1.5", "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.21", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "symfony/finder": "^5.3 | ^6.0 || ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -775,7 +1097,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" } }, "autoload": { @@ -832,7 +1154,7 @@ "type": "tidelift" } ], - "time": "2023-02-15T14:07:24+00:00" + "time": "2024-02-02T11:59:32+00:00" }, { "name": "league/config", @@ -918,54 +1240,55 @@ }, { "name": "league/flysystem", - "version": "1.1.10", + "version": "3.28.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" + "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c", + "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "league/mime-type-detection": "^1.3", - "php": "^7.2.5 || ^8.0" + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" }, "conflict": { - "league/flysystem-sftp": "<1.0.6" + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" }, "require-dev": { - "phpspec/prophecy": "^1.11.1", - "phpunit/phpunit": "^8.5.8" - }, - "suggest": { - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { - "League\\Flysystem\\": "src/" + "League\\Flysystem\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -975,68 +1298,53 @@ "authors": [ { "name": "Frank de Jonge", - "email": "info@frenky.net" + "email": "info@frankdejonge.nl" } ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "File storage abstraction for PHP", "keywords": [ - "Cloud Files", "WebDAV", - "abstraction", "aws", "cloud", - "copy.com", - "dropbox", - "file systems", + "file", "files", "filesystem", "filesystems", "ftp", - "rackspace", - "remote", "s3", "sftp", "storage" ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" + "source": "https://github.com/thephpleague/flysystem/tree/3.28.0" }, - "funding": [ - { - "url": "https://offset.earth/frankdejonge", - "type": "other" - } - ], - "time": "2022-10-04T09:16:37+00:00" + "time": "2024-05-22T10:09:12+00:00" }, { - "name": "league/mime-type-detection", - "version": "1.11.0", + "name": "league/flysystem-local", + "version": "3.28.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/13f22ea8be526ea58c2ddff9e158ef7c296e4f40", + "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" }, "type": "library", "autoload": { "psr-4": { - "League\\MimeTypeDetection\\": "src" + "League\\Flysystem\\Local\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1049,61 +1357,112 @@ "email": "info@frankdejonge.nl" } ], - "description": "Mime-type detection for Flysystem", + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], "support": { - "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.28.0" }, - "funding": [ - { - "url": "https://github.com/frankdejonge", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "type": "tidelift" - } + "time": "2024-05-06T20:05:52+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.15.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.15.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } ], - "time": "2022-04-17T13:12:02+00:00" + "time": "2024-01-28T23:22:08+00:00" }, { "name": "monolog/monolog", - "version": "2.9.1", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1" + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1", - "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", "shasum": "" }, "require": { - "php": ">=7.2", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^3.0", "doctrine/couchdb": "~1.0@dev", "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpspec/prophecy": "^1.15", - "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5.14", - "predis/predis": "^1.1 || ^2.0", - "rollbar/rollbar": "^1.3 || ^2 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.5.17", + "predis/predis": "^1.1 || ^2", "ruflin/elastica": "^7", - "swiftmailer/swiftmailer": "^5.3|^6.0", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -1126,7 +1485,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -1154,7 +1513,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.9.1" + "source": "https://github.com/Seldaek/monolog/tree/3.7.0" }, "funding": [ { @@ -1166,32 +1525,37 @@ "type": "tidelift" } ], - "time": "2023-02-06T13:44:46+00:00" + "time": "2024-06-28T09:40:51+00:00" }, { "name": "nesbot/carbon", - "version": "2.66.0", + "version": "2.72.5", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "496712849902241f04902033b0441b269effe001" + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", - "reference": "496712849902241f04902033b0441b269effe001", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed", + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed", "shasum": "" }, "require": { + "carbonphp/carbon-doctrine-types": "*", "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", "ondrejmirtes/better-reflection": "*", @@ -1208,8 +1572,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-3.x": "3.x-dev", - "dev-master": "2.x-dev" + "dev-master": "3.x-dev", + "dev-2.x": "2.x-dev" }, "laravel": { "providers": [ @@ -1268,35 +1632,35 @@ "type": "tidelift" } ], - "time": "2023-01-29T18:53:47+00:00" + "time": "2024-06-03T19:18:41+00:00" }, { "name": "nette/schema", - "version": "v1.2.3", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", "shasum": "" }, "require": { - "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "nette/utils": "^4.0", + "php": "8.1 - 8.3" }, "require-dev": { - "nette/tester": "^2.3 || ^2.4", + "nette/tester": "^2.4", "phpstan/phpstan-nette": "^1.0", - "tracy/tracy": "^2.7" + "tracy/tracy": "^2.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -1328,26 +1692,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "source": "https://github.com/nette/schema/tree/v1.3.0" }, - "time": "2022-10-13T01:24:26+00:00" + "time": "2023-12-11T11:54:22+00:00" }, { "name": "nette/utils", - "version": "v4.0.0", + "version": "v4.0.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", "shasum": "" }, "require": { - "php": ">=8.0 <8.3" + "php": ">=8.0 <8.4" }, "conflict": { "nette/finder": "<3", @@ -1355,7 +1719,7 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "^2.4", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", "tracy/tracy": "^2.9" }, @@ -1365,8 +1729,7 @@ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", "ext-json": "to use Nette\\Utils\\Json", "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" }, "type": "library", "extra": { @@ -1415,43 +1778,55 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.0" + "source": "https://github.com/nette/utils/tree/v4.0.4" }, - "time": "2023-02-02T10:41:53+00:00" + "time": "2024-01-17T16:50:36+00:00" }, { - "name": "opis/closure", - "version": "3.6.3", + "name": "nunomaduro/termwind", + "version": "v1.15.1", "source": { "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", - "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", "shasum": "" }, "require": { - "php": "^5.4 || ^7.0 || ^8.0" + "ext-mbstring": "*", + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" }, "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.6.x-dev" + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] } }, "autoload": { "files": [ - "functions.php" + "src/Functions.php" ], "psr-4": { - "Opis\\Closure\\": "src/" + "Termwind\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1460,42 +1835,51 @@ ], "authors": [ { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", + "description": "Its like Tailwind CSS, but for the console.", "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" + "cli", + "console", + "css", + "package", + "php", + "style" ], "support": { - "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.6.3" + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" }, - "time": "2022-01-27T09:35:39+00:00" + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2023-02-08T01:06:31+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.1", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", "shasum": "" }, "require": { @@ -1503,7 +1887,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "extra": { @@ -1545,7 +1929,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" }, "funding": [ { @@ -1557,26 +1941,79 @@ "type": "tidelift" } ], - "time": "2023-02-25T19:38:58+00:00" + "time": "2023-11-12T21:59:55+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1603,9 +2040,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -1659,16 +2096,16 @@ }, { "name": "psr/log", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", - "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { @@ -1677,7 +2114,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -1703,31 +2140,31 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/2.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-07-14T16:41:46+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1742,7 +2179,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -1754,9 +2191,9 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2017-10-23T01:57:42+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { "name": "ramsey/collection", @@ -1849,20 +2286,20 @@ }, { "name": "ramsey/uuid", - "version": "4.7.3", + "version": "4.7.6", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "433b2014e3979047db08a17a205f410ba3869cf2" + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2", - "reference": "433b2014e3979047db08a17a205f410ba3869cf2", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" @@ -1925,7 +2362,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.3" + "source": "https://github.com/ramsey/uuid/tree/4.7.6" }, "funding": [ { @@ -1937,132 +2374,51 @@ "type": "tidelift" } ], - "time": "2023-01-12T18:13:24+00:00" - }, - { - "name": "swiftmailer/swiftmailer", - "version": "v6.3.0", - "source": { - "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.0|^3.1", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.4" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.2-dev" - } - }, - "autoload": { - "files": [ - "lib/swift_required.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Corbyn" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" - ], - "support": { - "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", - "type": "tidelift" - } - ], - "abandoned": "symfony/mailer", - "time": "2021-10-18T15:26:12+00:00" + "time": "2024-04-27T21:32:50+00:00" }, { "name": "symfony/console", - "version": "v5.4.19", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", + "url": "https://api.github.com/repos/symfony/console/zipball/6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2091,12 +2447,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.19" + "source": "https://github.com/symfony/console/tree/v6.4.9" }, "funding": [ { @@ -2112,20 +2468,20 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-06-28T09:49:33+00:00" }, { "name": "symfony/css-selector", - "version": "v6.2.5", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1" + "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", - "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4b61b02fe15db48e3687ce1c45ea385d1780fe08", + "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08", "shasum": "" }, "require": { @@ -2161,7 +2517,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.2.5" + "source": "https://github.com/symfony/css-selector/tree/v6.4.8" }, "funding": [ { @@ -2177,20 +2533,20 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -2199,7 +2555,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2228,7 +2584,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -2244,31 +2600,35 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/error-handler", - "version": "v5.4.19", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822" + "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/438ef3e5e6481244785da3ce8cf8f4e74e7f2822", - "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c9b7cc075b3ab484239855622ca05cb0b99c13ec", + "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -2299,7 +2659,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/v5.4.19" + "source": "https://github.com/symfony/error-handler/tree/v6.4.9" }, "funding": [ { @@ -2315,28 +2675,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-06-21T16:04:15+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.2.5", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68" + "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", - "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8d7507f02b06e06815e56bb39aa0128e3806208b", + "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2|^3" + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4" + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", @@ -2344,17 +2705,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^5.4|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2382,7 +2739,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/v6.2.5" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.8" }, "funding": [ { @@ -2398,33 +2755,30 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.2.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "0782b0b52a737a05b4383d0df35a474303cabdae" + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", - "reference": "0782b0b52a737a05b4383d0df35a474303cabdae", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", "shasum": "" }, "require": { "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2461,7 +2815,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" }, "funding": [ { @@ -2477,26 +2831,27 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/finder", - "version": "v5.4.19", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f" + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6071aebf810ad13fe8200c224f36103abb37cf1f", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f", + "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -2524,7 +2879,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.19" + "source": "https://github.com/symfony/finder/tree/v6.4.8" }, "funding": [ { @@ -2540,39 +2895,40 @@ "type": "tidelift" } ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.20", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a" + "reference": "27de8cc95e11db7a50b027e71caaab9024545947" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0435363362a47c14e9cf50663cb8ffbf491875a", - "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/27de8cc95e11db7a50b027e71caaab9024545947", + "reference": "27de8cc95e11db7a50b027e71caaab9024545947", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-php83": "^1.27" }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "conflict": { + "symfony/cache": "<6.3" }, - "suggest": { - "symfony/mime": "To use the file extension guesser" + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2600,7 +2956,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.20" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.8" }, "funding": [ { @@ -2616,76 +2972,78 @@ "type": "tidelift" } ], - "time": "2023-01-29T11:11:52+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.20", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e" + "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e", - "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", + "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^5.0|^6.0", - "symfony/http-foundation": "^5.3.7|^6.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.0", - "symfony/config": "<5.0", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<5.3", - "symfony/doctrine-bridge": "<5.0", - "symfony/form": "<5.0", - "symfony/http-client": "<5.0", - "symfony/mailer": "<5.0", - "symfony/messenger": "<5.0", - "symfony/translation": "<5.0", - "symfony/twig-bridge": "<5.0", - "symfony/validator": "<5.0", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, "type": "library", "autoload": { "psr-4": { @@ -2712,7 +3070,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/v5.4.20" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.9" }, "funding": [ { @@ -2728,48 +3086,48 @@ "type": "tidelift" } ], - "time": "2023-02-01T08:18:48+00:00" + "time": "2024-06-28T11:48:06+00:00" }, { - "name": "symfony/mime", - "version": "v5.4.19", + "name": "symfony/mailer", + "version": "v6.4.9", "source": { "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb" + "url": "https://github.com/symfony/mailer.git", + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/a858429a9c704edc53fe057228cf9ca282ba48eb", - "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16" + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<4.4", - "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6" + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1|^4", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.1|^6.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Mime\\": "" + "Symfony\\Component\\Mailer\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2789,14 +3147,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Allows manipulating MIME messages", + "description": "Helps sending emails", "homepage": "https://symfony.com", - "keywords": [ - "mime", - "mime-type" - ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.19" + "source": "https://github.com/symfony/mailer/tree/v6.4.9" }, "funding": [ { @@ -2812,48 +3166,53 @@ "type": "tidelift" } ], - "time": "2023-01-09T05:43:46+00:00" + "time": "2024-06-28T07:59:05+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "name": "symfony/mime", + "version": "v6.4.9", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "url": "https://github.com/symfony/mime.git", + "reference": "7d048964877324debdcb4e0549becfa064a20d43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/mime/zipball/7d048964877324debdcb4e0549becfa064a20d43", + "reference": "7d048964877324debdcb4e0549becfa064a20d43", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" }, - "provide": { - "ext-ctype": "*" + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, - "suggest": { - "ext-ctype": "For best performance" + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2861,24 +3220,22 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Allows manipulating MIME messages", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "mime", + "mime-type" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/mime/tree/v6.4.9" }, "funding": [ { @@ -2894,36 +3251,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-06-28T09:49:33+00:00" }, { - "name": "symfony/polyfill-iconv", - "version": "v1.27.0", + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "927013f3aac555983a5059aada98e1907d842695" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/927013f3aac555983a5059aada98e1907d842695", - "reference": "927013f3aac555983a5059aada98e1907d842695", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { "php": ">=7.1" }, "provide": { - "ext-iconv": "*" + "ext-ctype": "*" }, "suggest": { - "ext-iconv": "For best performance" + "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2934,7 +3288,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" + "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2943,25 +3297,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Iconv extension", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "iconv", + "ctype", "polyfill", - "portable", - "shim" + "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -2977,20 +3330,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { @@ -3001,9 +3354,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3042,7 +3392,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -3058,20 +3408,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", "shasum": "" }, "require": { @@ -3084,9 +3434,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3129,7 +3476,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" }, "funding": [ { @@ -3145,20 +3492,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { @@ -3169,9 +3516,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3213,7 +3557,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -3229,20 +3573,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -3256,9 +3600,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3296,7 +3637,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -3312,20 +3653,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "10112722600777e02d2745716b70c5db4ca70442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", + "reference": "10112722600777e02d2745716b70c5db4ca70442", "shasum": "" }, "require": { @@ -3333,9 +3674,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3372,7 +3710,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" }, "funding": [ { @@ -3388,20 +3726,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.27.0", + "name": "symfony/polyfill-php80", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { @@ -3409,9 +3747,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3422,7 +3757,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "classmap": [ "Resources/stubs" @@ -3433,6 +3768,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3442,7 +3781,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -3451,7 +3790,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" }, "funding": [ { @@ -3467,20 +3806,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "name": "symfony/polyfill-php83", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", "shasum": "" }, "require": { @@ -3488,9 +3827,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3501,7 +3837,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -3512,10 +3848,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3525,7 +3857,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -3534,7 +3866,86 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:35:24+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9", + "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0" }, "funding": [ { @@ -3550,25 +3961,24 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/process", - "version": "v5.4.19", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1" + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1", + "url": "https://api.github.com/repos/symfony/process/zipball/8d92dd79149f29e89ee0f480254db595f6a6a2c5", + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -3596,7 +4006,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.19" + "source": "https://github.com/symfony/process/tree/v6.4.8" }, "funding": [ { @@ -3612,47 +4022,40 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/routing", - "version": "v5.4.19", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5" + "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", - "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", + "url": "https://api.github.com/repos/symfony/routing/zipball/8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3686,7 +4089,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.19" + "source": "https://github.com/symfony/routing/tree/v6.4.8" }, "funding": [ { @@ -3702,37 +4105,34 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.2", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3742,7 +4142,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3769,7 +4172,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -3785,20 +4188,20 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", - "version": "v6.2.5", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" + "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", - "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "url": "https://api.github.com/repos/symfony/string/zipball/76792dbd99690a5ebef8050d9206c60c59e681d7", + "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7", "shasum": "" }, "require": { @@ -3809,14 +4212,14 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3855,7 +4258,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.5" + "source": "https://github.com/symfony/string/tree/v6.4.9" }, "funding": [ { @@ -3871,32 +4274,35 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2024-06-28T09:25:38+00:00" }, { "name": "symfony/translation", - "version": "v6.2.5", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c" + "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c", - "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "url": "https://api.github.com/repos/symfony/translation/zipball/a002933b13989fc4bd0b58e04bf7eec5210e438a", + "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.3|^3.0" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { "symfony/config": "<5.4", "symfony/console": "<5.4", "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", "symfony/twig-bundle": "<5.4", "symfony/yaml": "<5.4" }, @@ -3904,25 +4310,19 @@ "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "nikic/php-parser": "^4.13", + "nikic/php-parser": "^4.18|^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "nikic/php-parser": "To use PhpAstExtractor", - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3953,7 +4353,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.2.5" + "source": "https://github.com/symfony/translation/tree/v6.4.8" }, "funding": [ { @@ -3969,32 +4369,29 @@ "type": "tidelift" } ], - "time": "2023-01-05T07:00:27+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.2.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "68cce71402305a015f8c1589bfada1280dc64fe7" + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7", - "reference": "68cce71402305a015f8c1589bfada1280dc64fe7", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", "shasum": "" }, "require": { "php": ">=8.1" }, - "suggest": { - "symfony/translation-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4034,7 +4431,81 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/uid", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/35904eca37a84bb764c560cbfcac9f0ac2bcdbdf", + "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.8" }, "funding": [ { @@ -4050,43 +4521,39 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.19", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b" + "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", - "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c31566e4ca944271cc8d8ac6887cbf31b8c6a172", + "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" + "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, "bin": [ "Resources/bin/var-dump-server" ], @@ -4123,7 +4590,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.19" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.9" }, "funding": [ { @@ -4139,27 +4606,27 @@ "type": "tidelift" } ], - "time": "2023-01-16T10:52:33+00:00" + "time": "2024-06-27T13:23:14+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.6", + "version": "v2.2.7", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" @@ -4190,9 +4657,9 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" }, - "time": "2023-01-03T09:29:04+00:00" + "time": "2023-12-08T13:03:43+00:00" }, { "name": "tmilos/lexer", @@ -4389,31 +4856,31 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.0.2", - "php": "^7.1.3 || ^8.0", - "phpoption/phpoption": "^1.8", - "symfony/polyfill-ctype": "^1.23", - "symfony/polyfill-mbstring": "^1.23.1", - "symfony/polyfill-php80": "^1.23.1" + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "suggest": { "ext-filter": "Required to use the boolean validator." @@ -4425,7 +4892,7 @@ "forward-command": true }, "branch-alias": { - "dev-master": "5.5-dev" + "dev-master": "5.6-dev" } }, "autoload": { @@ -4457,7 +4924,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" }, "funding": [ { @@ -4469,20 +4936,20 @@ "type": "tidelift" } ], - "time": "2022-10-16T01:01:54+00:00" + "time": "2023-11-12T22:43:29+00:00" }, { "name": "voku/portable-ascii", - "version": "1.6.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a" + "reference": "b56450eed252f6801410d810c8e1727224ae0743" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", "shasum": "" }, "require": { @@ -4519,7 +4986,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/1.6.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" }, "funding": [ { @@ -4543,7 +5010,7 @@ "type": "tidelift" } ], - "time": "2022-01-24T18:55:24+00:00" + "time": "2022-03-08T17:03:00+00:00" }, { "name": "webmozart/assert", @@ -4606,36 +5073,35 @@ ], "packages-dev": [ { - "name": "doctrine/instantiator", - "version": "2.0.0", + "name": "composer/semver", + "version": "3.4.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4644,49 +5110,61 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "constructor", - "instantiate" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://packagist.com", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" + "url": "https://github.com/composer", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { "name": "fakerphp/faker", - "version": "v1.21.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", "shasum": "" }, "require": { @@ -4712,11 +5190,6 @@ "ext-mbstring": "Required for multibyte Unicode string functionality." }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "v1.21-dev" - } - }, "autoload": { "psr-4": { "Faker\\": "src/Faker/" @@ -4739,28 +5212,99 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + }, + "time": "2024-01-02T13:46:09+00:00" + }, + { + "name": "filp/whoops", + "version": "2.15.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.15.4" }, - "time": "2022-12-13T13:54:32+00:00" + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2023-11-03T12:00:00+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.3", + "version": "2.6.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "67c26b443f348a51926030c83481b85718457d3d" + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", - "reference": "67c26b443f348a51926030c83481b85718457d3d", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0" }, "provide": { @@ -4768,9 +5312,9 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -4780,9 +5324,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -4844,7 +5385,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.3" + "source": "https://github.com/guzzle/psr7/tree/2.6.2" }, "funding": [ { @@ -4860,7 +5401,7 @@ "type": "tidelift" } ], - "time": "2022-10-26T14:07:24+00:00" + "time": "2023-12-03T20:05:35+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -4915,22 +5456,22 @@ }, { "name": "laravel/legacy-factories", - "version": "v1.3.2", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/laravel/legacy-factories.git", - "reference": "cc6720da81094c82ea9f4737d615dd3d71f7f43d" + "reference": "6cb79f668fc36b8b396ada1da3ba45867889c30f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/cc6720da81094c82ea9f4737d615dd3d71f7f43d", - "reference": "cc6720da81094c82ea9f4737d615dd3d71f7f43d", + "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/6cb79f668fc36b8b396ada1da3ba45867889c30f", + "reference": "6cb79f668fc36b8b396ada1da3ba45867889c30f", "shasum": "" }, "require": { - "illuminate/macroable": "^8.0|^9.0|^10.0", + "illuminate/macroable": "^8.0|^9.0|^10.0|^11.0", "php": "^7.3|^8.0", - "symfony/finder": "^3.4|^4.0|^5.0|^6.0" + "symfony/finder": "^3.4|^4.0|^5.0|^6.0|^7.0" }, "type": "library", "extra": { @@ -4967,42 +5508,108 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-01-09T14:49:00+00:00" + "time": "2024-01-15T13:55:14+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.9.0" + }, + "time": "2024-01-04T16:10:04+00:00" }, { "name": "mockery/mockery", - "version": "1.5.1", + "version": "1.6.12", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", - "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.3 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, "autoload": { - "psr-0": { - "Mockery": "library/" + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" } }, "notification-url": "https://packagist.org/downloads/", @@ -5013,12 +5620,20 @@ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -5036,23 +5651,26 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.5.1" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2022-09-07T15:32:08+00:00" + "time": "2024-05-16T03:13:13+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -5060,11 +5678,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -5090,7 +5709,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -5098,29 +5717,31 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -5128,7 +5749,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -5152,38 +5773,281 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { - "name": "orchestra/testbench", - "version": "v6.25.1", + "name": "nunomaduro/collision", + "version": "v7.10.0", "source": { "type": "git", - "url": "https://github.com/orchestral/testbench.git", - "reference": "0516123d26d64117bc04f7e9cb982eae2624e750" + "url": "https://github.com/nunomaduro/collision.git", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench/zipball/0516123d26d64117bc04f7e9cb982eae2624e750", - "reference": "0516123d26d64117bc04f7e9cb982eae2624e750", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", "shasum": "" }, "require": { - "laravel/framework": "^8.75", - "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^6.29.1", - "php": "^7.3 || ^8.0", - "phpunit/phpunit": "^8.5.21 || ^9.5.10", - "spatie/laravel-ray": "^1.26.2" + "filp/whoops": "^2.15.3", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.3.4" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", + "sebastian/environment": "^6.0.1", + "spatie/laravel-ignition": "^2.3.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-10-11T15:45:01+00:00" + }, + { + "name": "orchestra/canvas", + "version": "v8.11.9", + "source": { + "type": "git", + "url": "https://github.com/orchestral/canvas.git", + "reference": "9bed1ce6084af2ce166e9ea1cb160ff22dc94a6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/canvas/zipball/9bed1ce6084af2ce166e9ea1cb160ff22dc94a6d", + "reference": "9bed1ce6084af2ce166e9ea1cb160ff22dc94a6d", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "composer/semver": "^3.0", + "illuminate/console": "^10.48.4", + "illuminate/database": "^10.48.4", + "illuminate/filesystem": "^10.48.4", + "illuminate/support": "^10.48.4", + "orchestra/canvas-core": "^8.10.2", + "orchestra/testbench-core": "^8.19", + "php": "^8.1", + "symfony/polyfill-php83": "^1.28", + "symfony/yaml": "^6.2" + }, + "require-dev": { + "laravel/framework": "^10.48.4", + "laravel/pint": "^1.6", + "mockery/mockery": "^1.5.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^10.5", + "spatie/laravel-ray": "^1.33" + }, + "bin": [ + "canvas" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.0-dev" + }, + "laravel": { + "providers": [ + "Orchestra\\Canvas\\LaravelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Orchestra\\Canvas\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mior Muhammad Zaki", + "email": "crynobone@gmail.com" + } + ], + "description": "Code Generators for Laravel Applications and Packages", + "support": { + "issues": "https://github.com/orchestral/canvas/issues", + "source": "https://github.com/orchestral/canvas/tree/v8.11.9" + }, + "time": "2024-06-18T08:26:09+00:00" + }, + { + "name": "orchestra/canvas-core", + "version": "v8.10.2", + "source": { + "type": "git", + "url": "https://github.com/orchestral/canvas-core.git", + "reference": "3af8fb6b1ebd85903ba5d0e6df1c81aedacfedfc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/canvas-core/zipball/3af8fb6b1ebd85903ba5d0e6df1c81aedacfedfc", + "reference": "3af8fb6b1ebd85903ba5d0e6df1c81aedacfedfc", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "composer/semver": "^3.0", + "illuminate/console": "^10.38.1", + "illuminate/filesystem": "^10.38.1", + "php": "^8.1", + "symfony/polyfill-php83": "^1.28" + }, + "conflict": { + "orchestra/canvas": "<8.11.0", + "orchestra/testbench-core": "<8.2.0" + }, + "require-dev": { + "laravel/framework": "^10.38.1", + "laravel/pint": "^1.6", + "mockery/mockery": "^1.5.1", + "orchestra/testbench-core": "^8.19", + "phpstan/phpstan": "^1.10.6", + "phpunit/phpunit": "^10.1", + "symfony/yaml": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "9.0-dev" + }, + "laravel": { + "providers": [ + "Orchestra\\Canvas\\Core\\LaravelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Orchestra\\Canvas\\Core\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mior Muhammad Zaki", + "email": "crynobone@gmail.com" } + ], + "description": "Code Generators Builder for Laravel Applications and Packages", + "support": { + "issues": "https://github.com/orchestral/canvas/issues", + "source": "https://github.com/orchestral/canvas-core/tree/v8.10.2" + }, + "time": "2023-12-28T01:27:59+00:00" + }, + { + "name": "orchestra/testbench", + "version": "v8.23.2", + "source": { + "type": "git", + "url": "https://github.com/orchestral/testbench.git", + "reference": "c9f89b66aaa245a2e36f046aa431587ba46a3f2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/testbench/zipball/c9f89b66aaa245a2e36f046aa431587ba46a3f2e", + "reference": "c9f89b66aaa245a2e36f046aa431587ba46a3f2e", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "fakerphp/faker": "^1.21", + "laravel/framework": "^10.48.10", + "mockery/mockery": "^1.5.1", + "orchestra/testbench-core": "^8.24.3", + "orchestra/workbench": "^1.4.1 || ^8.5", + "php": "^8.1", + "phpunit/phpunit": "^9.6 || ^10.1", + "symfony/process": "^6.2", + "symfony/yaml": "^6.2", + "vlucas/phpdotenv": "^5.4.1" }, + "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -5200,75 +6064,77 @@ "keywords": [ "BDD", "TDD", + "dev", "laravel", - "orchestra-platform", - "orchestral", + "laravel-packages", "testing" ], "support": { "issues": "https://github.com/orchestral/testbench/issues", - "source": "https://github.com/orchestral/testbench/tree/v6.25.1" + "source": "https://github.com/orchestral/testbench/tree/v8.23.2" }, - "funding": [ - { - "url": "https://paypal.me/crynobone", - "type": "custom" - }, - { - "url": "https://liberapay.com/crynobone", - "type": "liberapay" - } - ], - "time": "2022-10-11T14:01:10+00:00" + "time": "2024-06-04T12:24:55+00:00" }, { "name": "orchestra/testbench-core", - "version": "v6.29.1", + "version": "v8.24.4", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "29a7586915885f89b8d2203efe20f76afe9cf956" + "reference": "3acbb96840e2bd4acbe670ee95e9bd55f517964c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/29a7586915885f89b8d2203efe20f76afe9cf956", - "reference": "29a7586915885f89b8d2203efe20f76afe9cf956", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/3acbb96840e2bd4acbe670ee95e9bd55f517964c", + "reference": "3acbb96840e2bd4acbe670ee95e9bd55f517964c", "shasum": "" }, "require": { - "fakerphp/faker": "^1.9.1", - "php": "^7.3 || ^8.0", - "symfony/yaml": "^5.0", - "vlucas/phpdotenv": "^5.1" + "composer-runtime-api": "^2.2", + "php": "^8.1", + "symfony/polyfill-php83": "^1.28" + }, + "conflict": { + "brianium/paratest": "<6.4.0 || >=7.0.0 <7.1.4 || >=8.0.0", + "laravel/framework": "<10.48.2 || >=11.0.0", + "nunomaduro/collision": "<6.4.0 || >=7.0.0 <7.4.0 || >=8.0.0", + "orchestra/testbench-dusk": "<8.21.0 || >=9.0.0", + "orchestra/workbench": "<1.0.0", + "phpunit/phpunit": "<9.6.0 || >=10.6.0" }, "require-dev": { - "laravel/framework": "^8.75", - "laravel/laravel": "8.x-dev", - "mockery/mockery": "^1.4.4", - "orchestra/canvas": "^6.1", - "phpunit/phpunit": "^8.5.21 || ^9.5.10", - "spatie/laravel-ray": "^1.7.1", - "symfony/process": "^5.0" + "fakerphp/faker": "^1.21", + "laravel/framework": "^10.48.2", + "laravel/pint": "^1.6", + "mockery/mockery": "^1.5.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^10.1", + "spatie/laravel-ray": "^1.32.4", + "symfony/process": "^6.2", + "symfony/yaml": "^6.2", + "vlucas/phpdotenv": "^5.4.1" }, "suggest": { - "laravel/framework": "Required for testing (^8.75).", - "mockery/mockery": "Allow using Mockery for testing (^1.4.4).", - "orchestra/testbench-browser-kit": "Allow using legacy Laravel BrowserKit for testing (^6.0).", - "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^6.0).", - "phpunit/phpunit": "Allow using PHPUnit for testing (^8.5.21|^9.5.10|^10.0)." + "brianium/paratest": "Allow using parallel testing (^6.4 || ^7.1.4).", + "ext-pcntl": "Required to use all features of the console signal trapping.", + "fakerphp/faker": "Allow using Faker for testing (^1.21).", + "laravel/framework": "Required for testing (^10.48.2).", + "mockery/mockery": "Allow using Mockery for testing (^1.5.1).", + "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^6.4 || ^7.4).", + "orchestra/testbench-browser-kit": "Allow using legacy Laravel BrowserKit for testing (^8.0).", + "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^8.0).", + "phpunit/phpunit": "Allow using PHPUnit for testing (^9.6 || ^10.1).", + "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^6.2).", + "symfony/yaml": "Required for Testbench CLI (^6.2).", + "vlucas/phpdotenv": "Required for Testbench CLI (^5.4.1)." }, "bin": [ "testbench" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.0-dev" - } - }, "autoload": { "files": [ - "src/helpers.php" + "src/functions.php" ], "psr-4": { "Orchestra\\Testbench\\": "src/" @@ -5290,43 +6156,105 @@ "keywords": [ "BDD", "TDD", + "dev", "laravel", - "orchestra-platform", - "orchestral", + "laravel-packages", "testing" ], "support": { "issues": "https://github.com/orchestral/testbench/issues", "source": "https://github.com/orchestral/testbench-core" }, - "funding": [ - { - "url": "https://paypal.me/crynobone", - "type": "custom" - }, + "time": "2024-06-26T12:14:16+00:00" + }, + { + "name": "orchestra/workbench", + "version": "v8.5.0", + "source": { + "type": "git", + "url": "https://github.com/orchestral/workbench.git", + "reference": "dce002c20de63b6bde74e0cae2ca558d031a8a17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/workbench/zipball/dce002c20de63b6bde74e0cae2ca558d031a8a17", + "reference": "dce002c20de63b6bde74e0cae2ca558d031a8a17", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "fakerphp/faker": "^1.21", + "laravel/framework": "^10.38.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/collision": "^6.4 || ^7.10", + "orchestra/canvas": "^8.11.4", + "orchestra/testbench-core": "^8.24", + "php": "^8.1", + "spatie/laravel-ray": "^1.32.4", + "symfony/polyfill-php83": "^1.28", + "symfony/yaml": "^6.2" + }, + "require-dev": { + "laravel/pint": "^1.4", + "mockery/mockery": "^1.5.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^10.1", + "symfony/process": "^6.2" + }, + "suggest": { + "ext-pcntl": "Required to use all features of the console signal trapping." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Orchestra\\Workbench\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://liberapay.com/crynobone", - "type": "liberapay" + "name": "Mior Muhammad Zaki", + "email": "crynobone@gmail.com" } ], - "time": "2022-10-11T12:12:52+00:00" + "description": "Workbench Companion for Laravel Packages Development", + "keywords": [ + "dev", + "laravel", + "laravel-packages", + "testing" + ], + "support": { + "issues": "https://github.com/orchestral/workbench/issues", + "source": "https://github.com/orchestral/workbench/tree/v8.5.0" + }, + "time": "2024-05-20T23:51:13+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -5367,9 +6295,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -5422,46 +6356,232 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "php-di/invoker", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86", + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.4" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2023-09-08T09:24:21+00:00" + }, + { + "name": "php-di/php-di", + "version": "7.0.6", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "8097948a89f6ec782839b3e958432f427cac37fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/8097948a89f6ec782839b3e958432f427cac37fd", + "reference": "8097948a89f6ec782839b3e958432f427cac37fd", + "shasum": "" + }, + "require": { + "laravel/serializable-closure": "^1.0", + "php": ">=8.0", + "php-di/invoker": "^2.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/proxy-manager-lts": "^1", + "mnapoli/phpunit-easymock": "^1.3", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.6" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "DI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", + "keywords": [ + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" + ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/7.0.6" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "time": "2023-11-02T10:04:50+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.11.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6ac78f1165346c83b4a753f7e4186d969c6ad0ee", + "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-07-01T15:33:06+00:00" + }, { "name": "phpunit/php-code-coverage", - "version": "9.2.25", + "version": "10.1.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954" + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "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", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.1" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "10.1-dev" } }, "autoload": { @@ -5489,7 +6609,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" }, "funding": [ { @@ -5497,32 +6618,32 @@ "type": "github" } ], - "time": "2023-02-25T05:32:00+00:00" + "time": "2024-06-29T08:25:15+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5549,7 +6670,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -5557,28 +6679,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-pcntl": "*" @@ -5586,7 +6708,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5612,7 +6734,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, "funding": [ { @@ -5620,32 +6742,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -5671,7 +6793,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -5679,32 +6802,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5730,7 +6853,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -5738,24 +6861,23 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.4", + "version": "10.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d" + "reference": "831bf82312be6037e811833ddbea0b8de60ea314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/831bf82312be6037e811833ddbea0b8de60ea314", + "reference": "831bf82312be6037e811833ddbea0b8de60ea314", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -5765,27 +6887,26 @@ "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -5793,7 +6914,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -5824,7 +6945,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.25" }, "funding": [ { @@ -5840,38 +6962,35 @@ "type": "tidelift" } ], - "time": "2023-02-27T13:06:37+00:00" + "time": "2024-07-03T05:49:17+00:00" }, { - "name": "pimple/pimple", - "version": "v3.5.0", + "name": "psr/http-factory", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1 || ^2.0" - }, - "require-dev": { - "symfony/phpunit-bridge": "^5.4@dev" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "Pimple": "src/" + "psr-4": { + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5880,43 +6999,47 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Pimple, a simple Dependency Injection Container", - "homepage": "https://pimple.symfony.com", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "container", - "dependency injection" + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" ], "support": { - "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2021-10-28T11:13:42+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { - "name": "psr/http-factory", - "version": "1.0.1", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -5931,51 +7054,76 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "factory", "http", - "message", + "http-message", "psr", - "psr-17", "psr-7", "request", "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "psy/psysh", + "version": "v0.12.4", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/bobthecow/psysh.git", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", "shasum": "" }, "require": { - "php": ">=5.3.0" + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "0.12.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false } }, "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Psy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5984,24 +7132,24 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "REPL", + "console", + "interactive", + "shell" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2024-06-10T01:18:23+00:00" }, { "name": "ralouphie/getallheaders", @@ -6047,30 +7195,89 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "rector/rector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/2fa387553db22b6f9bcccf5ff16f2c2c18a52a65", + "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "phpstan/phpstan": "^1.11" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2024-07-01T14:24:45+00:00" + }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -6093,7 +7300,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, "funding": [ { @@ -6101,32 +7309,32 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -6149,7 +7357,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -6157,32 +7365,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -6204,7 +7412,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -6212,34 +7420,36 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6278,7 +7488,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -6286,33 +7497,33 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -6335,7 +7546,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -6343,33 +7555,33 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -6401,7 +7613,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, "funding": [ { @@ -6409,27 +7622,27 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" @@ -6437,7 +7650,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -6456,7 +7669,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -6464,7 +7677,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -6472,34 +7686,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "5.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -6541,7 +7755,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" }, "funding": [ { @@ -6549,38 +7764,35 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -6599,13 +7811,14 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" }, "funding": [ { @@ -6613,33 +7826,33 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -6662,7 +7875,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -6670,34 +7884,34 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6719,7 +7933,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -6727,32 +7941,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -6774,7 +7988,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -6782,32 +7996,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6837,62 +8051,7 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { @@ -6900,32 +8059,32 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -6948,7 +8107,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -6956,29 +8115,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -7001,7 +8160,7 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -7009,20 +8168,20 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "spatie/backtrace", - "version": "1.2.2", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1" + "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", - "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23", + "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23", "shasum": "" }, "require": { @@ -7030,7 +8189,9 @@ }, "require-dev": { "ext-json": "*", + "laravel/serializable-closure": "^1.3", "phpunit/phpunit": "^9.3", + "spatie/phpunit-snapshot-assertions": "^4.2", "symfony/var-dumper": "^5.1" }, "type": "library", @@ -7058,7 +8219,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.2.2" + "source": "https://github.com/spatie/backtrace/tree/1.6.1" }, "funding": [ { @@ -7070,47 +8231,49 @@ "type": "other" } ], - "time": "2023-02-21T08:29:12+00:00" + "time": "2024-04-24T13:22:11+00:00" }, { "name": "spatie/laravel-ray", - "version": "1.32.2", + "version": "1.37.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ray.git", - "reference": "0c28a8274ec261a2857b4318b6f934af98024395" + "reference": "f57b294a3815be37effa9d13f54f2fbe5a2fff37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/0c28a8274ec261a2857b4318b6f934af98024395", - "reference": "0c28a8274ec261a2857b4318b6f934af98024395", + "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/f57b294a3815be37effa9d13f54f2fbe5a2fff37", + "reference": "f57b294a3815be37effa9d13f54f2fbe5a2fff37", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0", - "illuminate/database": "^7.20|^8.19|^9.0|^10.0", - "illuminate/queue": "^7.20|^8.19|^9.0|^10.0", - "illuminate/support": "^7.20|^8.19|^9.0|^10.0", + "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0|^11.0", + "illuminate/database": "^7.20|^8.19|^9.0|^10.0|^11.0", + "illuminate/queue": "^7.20|^8.19|^9.0|^10.0|^11.0", + "illuminate/support": "^7.20|^8.19|^9.0|^10.0|^11.0", "php": "^7.4|^8.0", + "rector/rector": "^0.19.2|^1.0", "spatie/backtrace": "^1.0", - "spatie/ray": "^1.33", - "symfony/stopwatch": "4.2|^5.1|^6.0", - "zbateson/mail-mime-parser": "^1.3.1|^2.0" + "spatie/ray": "^1.41.1", + "symfony/stopwatch": "4.2|^5.1|^6.0|^7.0", + "zbateson/mail-mime-parser": "^1.3.1|^2.0|^3.0" }, "require-dev": { "guzzlehttp/guzzle": "^7.3", - "laravel/framework": "^7.20|^8.19|^9.0|^10.0", - "orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0", - "pestphp/pest": "^1.22", - "phpstan/phpstan": "^0.12.93", - "phpunit/phpunit": "^9.3", - "spatie/pest-plugin-snapshots": "^1.1" + "laravel/framework": "^7.20|^8.19|^9.0|^10.0|^11.0", + "orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0|^9.0", + "pestphp/pest": "^1.22|^2.0", + "phpstan/phpstan": "^1.10.57", + "phpunit/phpunit": "^9.3|^10.1", + "spatie/pest-plugin-snapshots": "^1.1|^2.0", + "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.29.x-dev" + "dev-main": "1.x-dev" }, "laravel": { "providers": [ @@ -7143,7 +8306,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-ray/issues", - "source": "https://github.com/spatie/laravel-ray/tree/1.32.2" + "source": "https://github.com/spatie/laravel-ray/tree/1.37.0" }, "funding": [ { @@ -7155,7 +8318,7 @@ "type": "other" } ], - "time": "2023-02-06T09:46:50+00:00" + "time": "2024-07-03T08:48:44+00:00" }, { "name": "spatie/macroable", @@ -7209,16 +8372,16 @@ }, { "name": "spatie/ray", - "version": "1.36.2", + "version": "1.41.2", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "71dfde21900447ab37698fc07ff28b7f1e1822b8" + "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/71dfde21900447ab37698fc07ff28b7f1e1822b8", - "reference": "71dfde21900447ab37698fc07ff28b7f1e1822b8", + "url": "https://api.github.com/repos/spatie/ray/zipball/c44f8cfbf82c69909b505de61d8d3f2d324e93fc", + "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc", "shasum": "" }, "require": { @@ -7228,19 +8391,28 @@ "ramsey/uuid": "^3.0|^4.1", "spatie/backtrace": "^1.1", "spatie/macroable": "^1.0|^2.0", - "symfony/stopwatch": "^4.0|^5.1|^6.0", - "symfony/var-dumper": "^4.2|^5.1|^6.0" + "symfony/stopwatch": "^4.0|^5.1|^6.0|^7.0", + "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3" }, "require-dev": { "illuminate/support": "6.x|^8.18|^9.0", "nesbot/carbon": "^2.63", "pestphp/pest": "^1.22", - "phpstan/phpstan": "^0.12.92", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5", + "rector/rector": "^0.19.2", "spatie/phpunit-snapshot-assertions": "^4.2", "spatie/test-time": "^1.2" }, + "bin": [ + "bin/remove-ray.sh" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, "autoload": { "files": [ "src/helpers.php" @@ -7269,7 +8441,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.36.2" + "source": "https://github.com/spatie/ray/tree/1.41.2" }, "funding": [ { @@ -7281,25 +8453,105 @@ "type": "other" } ], - "time": "2023-02-10T09:24:13+00:00" + "time": "2024-04-24T14:21:46+00:00" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "c027e6a3c6aee334663ec21f5852e89738abc805" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c027e6a3c6aee334663ec21f5852e89738abc805", + "reference": "c027e6a3c6aee334663ec21f5852e89738abc805", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.2.5", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6" + "reference": "63e069eb616049632cde9674c46957819454b8aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6", - "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/63e069eb616049632cde9674c46957819454b8aa", + "reference": "63e069eb616049632cde9674c46957819454b8aa", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/service-contracts": "^1|^2|^3" + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -7327,7 +8579,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.2.5" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.8" }, "funding": [ { @@ -7343,35 +8595,32 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:55+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.19", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5" + "reference": "52903de178d542850f6f341ba92995d3d63e60c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/71c05db20cb9b54d381a28255f17580e2b7e36a5", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5", + "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.3" + "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -7402,7 +8651,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.19" + "source": "https://github.com/symfony/yaml/tree/v6.4.8" }, "funding": [ { @@ -7418,20 +8667,20 @@ "type": "tidelift" } ], - "time": "2023-01-10T18:51:14+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -7460,7 +8709,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -7468,34 +8717,35 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "zbateson/mail-mime-parser", - "version": "2.4.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/zbateson/mail-mime-parser.git", - "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a" + "reference": "6ade63b0a43047935791d7977e22717a68cc388b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/20b3e48eb799537683780bc8782fbbe9bc25934a", - "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a", + "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/6ade63b0a43047935791d7977e22717a68cc388b", + "reference": "6ade63b0a43047935791d7977e22717a68cc388b", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.7.0|^2.0", - "php": ">=7.1", - "pimple/pimple": "^3.0", - "zbateson/mb-wrapper": "^1.0.1", - "zbateson/stream-decorators": "^1.0.6" + "guzzlehttp/psr7": "^2.5", + "php": ">=8.0", + "php-di/php-di": "^6.0|^7.0", + "psr/log": "^1|^2|^3", + "zbateson/mb-wrapper": "^2.0", + "zbateson/stream-decorators": "^2.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "mikey179/vfsstream": "^1.6.0", + "monolog/monolog": "^2|^3", "phpstan/phpstan": "*", - "phpunit/phpunit": "<10" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-iconv": "For best support/performance", @@ -7543,31 +8793,31 @@ "type": "github" } ], - "time": "2023-02-14T22:58:03+00:00" + "time": "2024-04-29T21:53:01+00:00" }, { "name": "zbateson/mb-wrapper", - "version": "1.2.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/zbateson/mb-wrapper.git", - "reference": "faf35dddfacfc5d4d5f9210143eafd7a7fe74334" + "reference": "9e4373a153585d12b6c621ac4a6bb143264d4619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/faf35dddfacfc5d4d5f9210143eafd7a7fe74334", - "reference": "faf35dddfacfc5d4d5f9210143eafd7a7fe74334", + "url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/9e4373a153585d12b6c621ac4a6bb143264d4619", + "reference": "9e4373a153585d12b6c621ac4a6bb143264d4619", "shasum": "" }, "require": { - "php": ">=7.1", + "php": ">=8.0", "symfony/polyfill-iconv": "^1.9", "symfony/polyfill-mbstring": "^1.9" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", "phpstan/phpstan": "*", - "phpunit/phpunit": "<=9.0" + "phpunit/phpunit": "<10.0" }, "suggest": { "ext-iconv": "For best support/performance", @@ -7604,7 +8854,7 @@ ], "support": { "issues": "https://github.com/zbateson/mb-wrapper/issues", - "source": "https://github.com/zbateson/mb-wrapper/tree/1.2.0" + "source": "https://github.com/zbateson/mb-wrapper/tree/2.0.0" }, "funding": [ { @@ -7612,31 +8862,31 @@ "type": "github" } ], - "time": "2023-01-11T23:05:44+00:00" + "time": "2024-03-20T01:38:07+00:00" }, { "name": "zbateson/stream-decorators", - "version": "1.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/zbateson/stream-decorators.git", - "reference": "7466ff45d249c86b96267a83cdae68365ae1787e" + "reference": "32a2a62fb0f26313395c996ebd658d33c3f9c4e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/7466ff45d249c86b96267a83cdae68365ae1787e", - "reference": "7466ff45d249c86b96267a83cdae68365ae1787e", + "url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/32a2a62fb0f26313395c996ebd658d33c3f9c4e5", + "reference": "32a2a62fb0f26313395c996ebd658d33c3f9c4e5", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.9 | ^2.0", - "php": ">=7.1", - "zbateson/mb-wrapper": "^1.0.0" + "guzzlehttp/psr7": "^2.5", + "php": ">=8.0", + "zbateson/mb-wrapper": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", "phpstan/phpstan": "*", - "phpunit/phpunit": "<=9.0" + "phpunit/phpunit": "^9.6|^10.0" }, "type": "library", "autoload": { @@ -7667,7 +8917,7 @@ ], "support": { "issues": "https://github.com/zbateson/stream-decorators/issues", - "source": "https://github.com/zbateson/stream-decorators/tree/1.1.0" + "source": "https://github.com/zbateson/stream-decorators/tree/2.1.1" }, "funding": [ { @@ -7675,7 +8925,7 @@ "type": "github" } ], - "time": "2023-01-11T23:22:44+00:00" + "time": "2024-04-29T21:42:39+00:00" } ], "aliases": [], @@ -7684,7 +8934,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.0|^8.0" + "php": "^8.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/database/factories/GroupFactory.php b/database/factories/GroupFactory.php new file mode 100644 index 00000000..f21c11f0 --- /dev/null +++ b/database/factories/GroupFactory.php @@ -0,0 +1,11 @@ +define(Group::class, function (Generator $faker) { + return [ + // 'username' => $faker->userName, + 'name' => $faker->company + ]; +}); diff --git a/phpunit.xml b/phpunit.xml index 4888111e..357d2102 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,12 +2,10 @@ diff --git a/src/Attribute/AbstractComplex.php b/src/Attribute/AbstractComplex.php new file mode 100644 index 00000000..9cf120fc --- /dev/null +++ b/src/Attribute/AbstractComplex.php @@ -0,0 +1,71 @@ +parent != null) { + return null; + } + + return collect($this->subAttributes)->first(fn ($element) => $element instanceof Schema); + } + + public function getSchemaNodes(){ + return collect($this->subAttributes)->filter(fn ($element) => $element instanceof Schema)->values()->toArray(); + } + + public function getValidations() + { + $result = [ + addcslashes($this->getFullKey(),'.') => $this->validations + ]; + + foreach ($this->subAttributes as $attribute) { + $result = array_merge($result, $attribute->getValidations()); + } + + $result = collect($result)->filter(fn ($v, $k) => !empty($v))->toArray(); + + return $result; + } + + public function withSubAttributes(...$subAttributes) + { + foreach ($subAttributes as $attribute) { + $attribute->setParent($this); + } + + $this->subAttributes = $subAttributes; + + return $this; + } + + public function getSubNode(string $key): ?Attribute + { + $result = collect($this->subAttributes)->first(fn ($element) => $element->name == $key); + + // if this is the root node, search for a subNode in one of the default schema nodes + if($result == null){ + $result = $this->getSchemaNode()?->getSubNode($key); + } + + return $result; + } + + public function generateSchema() + { + $base = parent::generateSchema(); + + $base['subAttributes'] = collect($this->subAttributes)->map(fn ($element) => $element->generateSchema())->toArray(); + + return $base; + } +} diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php new file mode 100644 index 00000000..58504d01 --- /dev/null +++ b/src/Attribute/Attribute.php @@ -0,0 +1,322 @@ +name = $name; + } + + public function getValidations() + { + $key = addcslashes($this->getFullKey(), '.'); + + if ($this->parent != null && $this->parent->getMultiValued()) { + $key = addcslashes($this->parent->getFullKey(), '.') . '.*.' . $this->name; + } + + return [ + $key => $this->validations + ]; + } + + /** + * Return SCIM schema for this attribute + */ + public function generateSchema(){ + return [ + 'name' => $this->name, + 'type' => $this->type, + 'mutability' => $this->mutability, + 'returned' => 'default', + 'uniqueness' => 'server', + 'required' => $this->isRequired(), + 'multiValued' => $this->getMultiValued(), + 'caseExact' => false + ]; + } + + public function setMultiValued($multiValued) + { + $this->multiValued = $multiValued; + + return $this; + } + + public function getMultiValued() + { + return $this->multiValued; + } + + public function setMutability($mutability) + { + $this->mutability = $mutability; + + return $this; + } + + public function ensure(...$validations) + { + $this->validations = $validations; + + return $this; + } + + public function isRequired() + { + return in_array('required', $this->validations); + } + + public function nullable() + { + return in_array('nullable', $this->validations); + } + + public function isReadSupported() + { + return $this->readEnabled; + } + + public function isWriteSupported() + { + return $this->writeEnabled; + } + + public function shouldReturn(&$object) + { + return true; + } + + public function setParent($parent) + { + $this->parent = $parent; + return $this; + } + + protected function isRequested($attributes){ + return empty($attributes) || in_array($this->name, $attributes) || in_array($this->getFullKey(), $attributes) || ($this->parent != null && $this->parent->isRequested($attributes)); + } + + public function read(&$object, array $attributes = []): ?AttributeValue + { + // check if name or getFullKey is in attributes + if (!$this->isRequested($attributes)) { + return null; + } + + return new AttributeValue($this->doRead($object, $attributes)); + } + + protected function doRead(&$object, $attributes = []) + { + throw new SCIMException(sprintf('Read is not implemented for "%s"', $this->getFullKey())); + } + + public function getFullKey() + { + if ($this->parent != null && $this->parent->name != null) { + $seperator = $this->parent instanceof AttributeSchema ? ':' : '.'; + return $this->parent->getFullKey() . $seperator . $this->name; + } else { + return $this->name; + } + } + + public function getDefaultSchema() + { + // TODO: why this? + return Schema::SCHEMA_USER; + } + + public function getSchema() + { + if ($this instanceof AttributeSchema) { + return $this->name; + } else { + return $this->parent?->getSchema(); + } + } + + public function getNode(?AttributePath $attributePath) + { + if (empty($attributePath)) { + return $this; + } + + //The first schema should be the default one + $schema = $attributePath->schema ?? $this->getDefaultSchema(); + + if (!empty($schema) && !empty($this->getSchema()) && $this->getSchema() != $schema) { + throw (new SCIMException(sprintf('Trying to get attribute for schema "%s". But schema is already "%s"', $attributePath->schema, $this->getSchema())))->setCode(500)->setScimType('noTarget'); + } + + $elements = []; + + // The attribute mapping MUST include the schema. Therefore, add the schema to the first element. + if (empty($attributePath->attributeNames) && !empty($schema)) { + $elements[] = $schema; + } elseif (empty($this->getSchema()) && !in_array($attributePath->attributeNames[0], Schema::ATTRIBUTES_CORE)) { + $elements[] = $schema ?? (is_array($this->getDefaultSchema()) ? $this->getDefaultSchema()[0] : $this->getDefaultSchema()); + } + + foreach ($attributePath->attributeNames as $a) { + $elements[] = $a; + } + + /** + * @var Attribute $node + */ + $node = $this; + + foreach ($elements as $element) { + try { + $node = $node->getSubNode($element, $schema); + + if ($node instanceof Attribute && $this->getDefaultSchema()) { + //$node->setDefaultSchema($this->getDefaultSchema()); + } + } catch (\Exception $e) { + throw $e; + } + } + + return $node; + } + + /** + * Used by scimFilterToLaravelQuery and getEloquentSortAttribute + * Example filter: + * - urn:ietf:params:scim:schemas:core:2.0:User:userName eq "bjensen" + * - userName co "jensen" + * example sort: + * urn:ietf:params:scim:schemas:core:2.0:User:userName + */ + public function getSubNodeWithPath($path) + { + if ($path == null) { + return $this; + } else { + $getAttributePath = function () { + return $this->attributePath; + }; + + $getValuePath = function () { + return $this->valuePath; + }; + + $getFilter = function () { + return $this->filter; + }; + + $first = @$getAttributePath->call((object)$getValuePath->call($path)); + $filter = @$getFilter->call((object)$getValuePath->call($path)); + $last = $getAttributePath->call((object)$path); + + return $this->getNode($first)->withFilter($filter)->getNode($last); + } + } + + public function setFilter($filter) + { + $this->filter = $filter; + + return $this; + } + + public function withFilter($filter) + { + $this->filter = $filter; + + return $this; + } + + public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) + { + throw new SCIMException(sprintf('Comparison is not implemented for "%s"', $this->getFullKey())); + } + + public function add($value, Model &$object) + { + new SCIMException(sprintf('Write is not implemented for "%s"', $this->getFullKey())); + } + + public function replace($value, Model &$object, Path $path = null) + { + throw new SCIMException(sprintf('Replace is not implemented for "%s"', $this->getFullKey())); + } + + public function patch($operation, $value, Model &$object, Path $path = null) + { + throw new SCIMException(sprintf('Patch is not implemented for "%s"', $this->getFullKey())); + } + + public function remove($value, Model &$object, string $path = null) + { + throw new SCIMException(sprintf('Remove is not implemented for "%s"', $this->getFullKey())); + } + + public function setSortAttribute($attribute) + { + $this->sortAttribute = $attribute; + + return $this; + } + + public function getSortAttribute() + { + if (!$this->readEnabled) { + throw new SCIMException(sprintf('Can\'t sort on unreadable attribute "%s"', $this->getFullKey())); + } + + return $this->sortAttribute; + } + + public function getSortAttributeByPath(Path $path) + { + throw new SCIMException(sprintf('Sort is not implemented for "%s"', $this->getFullKey())); + } + + public function isDirty() + { + return $this->dirty; + } +} diff --git a/src/Attribute/AttributeMapping.php b/src/Attribute/AttributeMapping.php deleted file mode 100644 index 6980562d..00000000 --- a/src/Attribute/AttributeMapping.php +++ /dev/null @@ -1,639 +0,0 @@ -disableWrite()->ignoreRead()->setParent($parent); - } - - public static function arrayOfObjects($mapping, $parent = null) : AttributeMapping - { - return (new Collection())->setStaticCollection($mapping)->setRead( - function (&$object) use ($mapping, $parent) { - $result = []; - - foreach ($mapping as $key => $o) { - $element = self::ensureAttributeMappingObject($o)->setParent($parent)->read($object); - - if ($element != null) { - $result[] = $element; - } - } - - return empty($result) ? null : $result; - } - ); - } - - public static function object($mapping, $parent = null) : AttributeMapping - { - return (new AttributeMapping())->setMappingAssocArray($mapping)->setRead( - function (&$object) use ($mapping, $parent) { - $result = []; - - foreach ($mapping as $key => $value) { - $result[$key] = self::ensureAttributeMappingObject($value)->setParent($parent)->read($object); - - if (empty($result[$key]) && !is_bool($result[$key])) { - unset($result[$key]); - } - } - - return empty($result) ? null : $result; - } - ); - } - - public static function constant($text, $parent = null) : AttributeMapping - { - return (new AttributeMapping())->disableWrite()->setParent($parent)->setRead( - function (&$object) use ($text) { - return $text; - } - ); - } - - public static function eloquent($eloquentAttribute, $parent = null) : AttributeMapping - { - return (new EloquentAttributeMapping())->setEloquentReadAttribute($eloquentAttribute)->setParent($parent)->setAdd( - function ($value, &$object) use ($eloquentAttribute) { - $object->{$eloquentAttribute} = $value; - } - )->setReplace( - function ($value, &$object) use ($eloquentAttribute) { - $object->{$eloquentAttribute} = $value; - } - )->setSortAttribute($eloquentAttribute)->setEloquentAttributes([$eloquentAttribute]); - } - - public static function eloquentCollection($eloquentAttribute, $parent = null) : AttributeMapping - { - return (new AttributeMapping())->setParent($parent)->setRead( - function (&$object) use ($eloquentAttribute) { - $result = $object->{$eloquentAttribute}; - - return self::eloquentAttributeToString($result); - } - )->setAdd( - function ($value, &$object) use ($eloquentAttribute) { - if (!is_array($value)) { - $value = [$value]; - } - - $object->{$eloquentAttribute}()->attach(collect($value)->pluck('value')); - } - )->setReplace( - function ($value, &$object) use ($eloquentAttribute) { - $object->{$eloquentAttribute}()->sync(collect($value)->pluck('value')); - } - )->setSortAttribute($eloquentAttribute)->setEloquentAttributes([$eloquentAttribute]); - } - - public function setMappingAssocArray($mapping) : AttributeMapping - { - $this->mappingAssocArray = $mapping; - - return $this; - } - - public function setSchema($schema) : AttributeMapping - { - $this->schema = $schema; - return $this; - } - - public function getSchema() - { - return $this->schema; - } - - public function setDefaultSchema($schema) : AttributeMapping - { - $this->defaultSchema = $schema; - - return $this; - } - - public function getDefaultSchema() - { - return $this->defaultSchema; - } - - public function setEloquentReadAttribute($attribute) - { - $this->eloquentReadAttribute = $attribute; - - return $this; - } - - public function setEloquentAttributes(array $attributes) - { - $this->eloquentAttributes = $attributes; - - return $this; - } - - public function getEloquentAttributes() - { - $result = $this->eloquentAttributes; - - if ($this->mappingAssocArray) { - foreach ($this->mappingAssocArray as $key => $value) { - foreach (self::ensureAttributeMappingObject($value)->setParent($this)->getEloquentAttributes() as $attribute) { - $result[] = $attribute; - } - } - } - - return $result; - } - - public function disableRead() - { - $parent = $this; - - $this->read = function (&$object) use ($parent) { - // throw new SCIMException('Read is not supported for ' . $parent->getFullKey()); - return null; //"disabled!!"; - }; - - $this->readEnabled = false; - - return $this; - } - - /** - * @return self - */ - public function ignoreRead() - { - $this->read = function (&$object) { - return null; - }; - - return $this; - } - - /** - * @return self - */ - public function ignoreWrite() - { - $ignore = function ($value, &$object) { - //ignore - }; - - $this->add = $ignore; - $this->replace = $ignore; - $this->remove = $ignore; - - return $this; - } - - public function disableWrite() - { - $disable = function ($value, &$object) { - throw (new SCIMException(sprintf('Write to "%s" is not supported', $this->getFullKey())))->setCode(500)->setScimType('mutability'); - }; - - $this->add = $disable; - $this->replace = $disable; - $this->remove = $disable; - - $this->writeEnabled = false; - - return $this; - } - - /** - * @return self - */ - public function setRead($read) : AttributeMapping - { - $this->read = $read; - - return $this; - } - - public function setAdd($write) - { - $this->add = $write; - - return $this; - } - - public function setRemove($write) - { - $this->remove = $write; - - return $this; - } - - public function setReturned($returned) - { - $this->returned = $returned; - - return $this; - } - - public function setReplace($replace) - { - $this->replace = $replace; - - return $this; - } - - public function setWriteAfter($writeAfter) - { - $this->writeAfter = $writeAfter; - - return $this; - } - - public function setParent($parent) - { - $this->parent = $parent; - return $this; - } - - public function setKey($key) - { - $this->key = $key; - return $this; - } - - public function getKey() - { - return $this->key; - } - - public function getFullKey() - { - $parent = $this->parent; - - $fullKey = []; - - while ($parent != null) { - array_unshift($fullKey, $parent->getKey()); - $parent = $parent->parent; - } - - $fullKey[] = $this->getKey(); - - //ugly hack - $fullKey = array_filter( - $fullKey, - function ($value) { - return !empty($value); - } - ); - - return Helper::getFlattenKey($fullKey, [$this->getSchema() ?? $this->getDefaultSchema()]); - } - - public function setFilter($filter) - { - $this->filter = $filter; - - return $this; - } - - public function readNotImplemented($object) - { - throw new SCIMException(sprintf('Read is not implemented for "%s"', $this->getFullKey())); - } - - public function writeNotImplemented($object) - { - throw new SCIMException(sprintf('Write is not implemented for "%s"', $this->getFullKey())); - } - - public function writeAfterIgnore($value, &$object) - { - } - - public function replaceNotImplemented($value, &$object) - { - throw new SCIMException(sprintf('Replace is not implemented for "%s"', $this->getFullKey())); - } - - public function defaultRemove($value, &$object) - { - } - - public function __construct() - { - } - - public function setSortAttribute($attribute) - { - $this->sortAttribute = $attribute; - - return $this; - } - - public function getSortAttribute() - { - if (!$this->readEnabled) { - throw new SCIMException(sprintf('Can\'t sort on unreadable attribute "%s"', $this->getFullKey())); - } - - return $this->sortAttribute; - } - - public function withFilter($filter) - { - return $this->setFilter($filter); - } - - public function add($value, &$object) - { - return $this->add ? ($this->add)($value, $object) : $this->writeNotImplemented($object); - } - - public function replace($value, &$object) - { - $current = $this->read($object); - - //TODO: Really implement replace ...??? - return $this->replace ? ($this->replace)($value, $object) : $this->replaceNotImplemented($value, $object); - } - - public function remove($value, &$object) - { - - //TODO: implement remove for multi valued attributes - return $this->remove ? ($this->remove)($value, $object) : $this->defaultRemove($value, $object); - } - - public function writeAfter($value, &$object) - { - return $this->writeAfter ? ($this->writeAfter)($value, $object) : $this->writeAfterIgnore($value, $object); - } - - public function read(&$object) - { - return $this->read ? ($this->read)($object) : $this->readNotImplemented($object); - } - - public static function eloquentAttributeToString($value) - { - if ($value instanceof \Carbon\Carbon) { - $value = $value->format('c'); - } - - return $value; - } - - public function isReadSupported() - { - return $this->readEnabled; - } - - public function isWriteSupported() - { - return $this->writeEnabled; - } - - public static function ensureAttributeMappingObject($attributeMapping, $parent = null) : AttributeMapping - { - $result = null; - - if ($attributeMapping == null) { - $result = self::noMapping($parent); - } elseif (is_array($attributeMapping) && !empty($attributeMapping) && isset($attributeMapping[0])) { - $result = self::arrayOfObjects($attributeMapping, $parent); - } elseif (is_array($attributeMapping)) { - $result = self::object($attributeMapping, $parent); - } elseif ($attributeMapping instanceof AttributeMapping) { - $result = $attributeMapping->setParent($parent); - } else { - throw (new SCIMException(sprintf('Found unknown attribute "%s" in "%s"', $attributeMapping, 'unknown'))) - ->setCode(500); - } - - return $result; - } - - /** - * Returns the AttributeMapping for a specific value. Uses for example for creating queries ... and sorting - * - * @param unknown $value - * @return \ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping - */ - public function getSubNode($key, $schema = null) - { - if ($this->getSubNode != null) { - return ($this->getSubNode)($key, $schema); - } - - if ($key == null) { - return $this; - } - - if ($this->mappingAssocArray != null && array_key_exists($key, $this->mappingAssocArray)) { - return self::ensureAttributeMappingObject($this->mappingAssocArray[$key])->setParent($this)->setKey($key)->setSchema($schema); - } else { - throw new SCIMException(sprintf('No mapping for "%s" in "%s"', $key, $this->getFullKey())); - } - } - - public function setGetSubNode($closure) - { - $this->getSubNode = $closure; - - return $this; - } - - public function setRelationship($relationship) - { - $this->relationship = $relationship; - - return $this; - } - - public function getNode($attributePath) - { - if (empty($attributePath)) { - return $this; - } - - //The first schema should be the default one - $schema = $attributePath->schema ?? $this->getDefaultSchema()[0]; - - if (!empty($schema) && !empty($this->getSchema()) && $this->getSchema() != $schema) { - throw (new SCIMException(sprintf('Trying to get attribute for schema "%s". But schema is already "%s"', $attributePath->schema, $this->getSchema())))->setCode(500)->setScimType('noTarget'); - } - - $elements = []; - - // The attribute mapping MUST include the schema. Therefore, add the schema to the first element. - if (empty($attributePath->attributeNames) && !empty($schema)) { - $elements[] = $schema; - } elseif (empty($this->getSchema()) && !in_array($attributePath->attributeNames[0], Schema::ATTRIBUTES_CORE)) { - $elements[] = $schema ?? (is_array($this->getDefaultSchema()) ? $this->getDefaultSchema()[0] : $this->getDefaultSchema()); - } - - foreach ($attributePath->attributeNames as $a) { - $elements[] = $a; - } - - /** - * @var AttributeMapping - */ - $node = $this; - - foreach ($elements as $element) { - try { - $node = $node->getSubNode($element, $schema); - - if ($node instanceof AttributeMapping && $this->getDefaultSchema()) { - $node->setDefaultSchema($this->getDefaultSchema()); - } - } catch (\Exception $e) { - throw $e; - } - } - - return $node; - } - - public function getSubNodeWithPath($path) - { - if ($path == null) { - return $this; - } else { - $getAttributePath = function () { - return $this->attributePath; - }; - - $getValuePath = function () { - return $this->valuePath; - }; - - $getFilter = function () { - return $this->filter; - }; - - $first = @$getAttributePath->call((object)$getValuePath->call($path)); - $filter = @$getFilter->call((object)$getValuePath->call($path)); - $last = $getAttributePath->call((object)$path); - - return $this->getNode($first)->withFilter($filter)->getNode($last); - } - } - - public function applyWhereConditionDirect($attribute, &$query, $operator, $value) - { - switch ($operator) { - case "eq": - $query->where($attribute, $value); - break; - case "ne": - $query->where($attribute, '<>', $value); - break; - case "co": - $query->where($attribute, 'like', '%' . addcslashes($value, '%_') . '%'); - break; - case "sw": - $query->where($attribute, 'like', addcslashes($value, '%_') . '%'); - break; - case "ew": - $query->where($attribute, 'like', '%' . addcslashes($value, '%_')); - break; - case "pr": - $query->whereNotNull($attribute); - break; - case "gt": - $query->where($attribute, '>', $value); - break; - case "ge": - $query->where($attribute, '>=', $value); - break; - case "lt": - $query->where($attribute, '<', $value); - break; - case "le": - $query->where($attribute, '<=', $value); - break; - default: - die("Not supported!!"); - break; - } - } - - public function applyWhereCondition(&$query, $operator, $value) - { - - //only filter on OWN eloquent attributes - if (empty($this->eloquentAttributes)) { - throw new SCIMException("Can't filter on . " . $this->getFullKey()); - } - - $attribute = $this->eloquentAttributes[0]; - - if ($this->relationship != null) { - $query->whereHas( - $this->relationship, - function ($query) use ($attribute, $operator, $value) { - $this->applyWhereConditionDirect($attribute, $query, $operator, $value); - } - )->get(); - } else { - $this->applyWhereConditionDirect($attribute, $query, $operator, $value); - } - } -} diff --git a/src/Attribute/AttributeValue.php b/src/Attribute/AttributeValue.php new file mode 100644 index 00000000..0c08fdad --- /dev/null +++ b/src/Attribute/AttributeValue.php @@ -0,0 +1,13 @@ +collection = $collection; + parent::__construct($name); - return $this; + $this->attribute = $attribute ?? $name; } - public function add($value, &$object) + public function read(&$object, array $attributes = []): ?AttributeValue { + if (!empty($attributes) && !in_array($this->name, $attributes) && !in_array($this->getFullKey(), $attributes)) { + return null; + } - //only for creation requests - if ($object->id == null) { - foreach ($value as $key => $v) { - $this->getSubNode($key)->add($v, $object); - } - } else { - foreach ($value as $key => $v) { - if ($this->getSubNode($key) != null) { - $this->getSubNode($key)->add($v, $object); - } else { - //TODO: log ignore - } - } + $result = $this->doRead($object, $attributes); - // throw (new SCIMException('Add is not implemented for updates of ' . $this->getFullKey()))->setCode(501); - } + return new AttributeValue($result); } - public function remove($value, &$object) + protected function doRead(&$object, $attributes = []) { - // throw (new SCIMException('Remove is not implemented for ' . $this->getFullKey()))->setCode(501); + $result = []; - foreach ($this->collection as $c) { - foreach ($c as $k => $v) { - $mapping = AttributeMapping::ensureAttributeMappingObject($v); + foreach ($object->{$this->attribute} as $o) { + $element = []; - if ($mapping->isWriteSupported()) { - $mapping->remove($value, $object); + foreach ($this->subAttributes as $attribute) { + if (($r = $attribute->read($o)) != null) { + $element[$attribute->name] = $r->value; } } - } - } - - public function replace($value, &$object) - { - $this->remove($value, $object); - - $this->add($value, $object); - - // var_dump(json_encode($object));exit; - // throw (new SCIMException('Replace is not implemented for ' . $this->getFullKey()))->setCode(501); - } - public function getEloquentAttributes() - { - $result = $this->eloquentAttributes; - - foreach ($this->collection as $value) { - $result = array_merge($result, AttributeMapping::ensureAttributeMappingObject($value)->getEloquentAttributes()); + $result[] = $element; } return $result; } - public function getSubNode($key, $schema = null) + public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) { - if ($key == null) { - return $this; + if ($path == null || empty($path->getAttributePathAttributes())) { + throw new SCIMException('No attribute path attributes found. Could not apply comparison in ' . $this->getFullKey()); } - if (!empty($this->collection) && is_array($this->collection[0]) && array_key_exists($key, $this->collection[0])) { - $parent = $this; - - return (new CollectionValue()) - ->setEloquentAttributes($this->collection[0][$key]->getEloquentAttributes()) - ->setKey($key) - ->setParent($this) - ->setAdd(function ($value, &$object) use ($key, $parent) { - $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); - - $result = []; - - foreach ($collection as $o) { - $o[$key]->add($value, $object); - } - }) - ->setReplace(function ($value, &$object) use ($key, $parent) { - $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); - - $result = []; - - foreach ($collection as $o) { - $o[$key]->add($value, $object); - } - }) - ->setRead(function (&$object) use ($key, $parent) { - $collection = Collection::filterCollection($parent->filter, collect($parent->collection), $object); + $attribute = $this->getSubNode($path->getAttributePathAttributes()[0]); - $result = []; - - foreach ($collection as $o) { - $result = AttributeMapping::ensureAttributeMappingObject($o); - } - - return $result; - }) - ->setSchema($schema); - } - } - - public static function filterCollection($scimFilter, $collection, $resourceObject) - { - if ($scimFilter == null) { - return $collection; - } - - $attribute = $scimFilter->attributePath->attributeNames[0]; - $operator = $scimFilter->operator; - $compareValue = $scimFilter->compareValue; - - $result = []; - - foreach ($collection->toArray() as $value) { - $result[] = AttributeMapping::ensureAttributeMappingObject($value)->read($resourceObject); - } - - $collectionOriginal = $collection; - - $collection = collect($result); - - switch ($operator) { - case "eq": - /** - * @var $collection Coll - */ - $result = $collection->where($attribute, '==', $compareValue); - break; - case "ne": - $result = $collection->where($attribute, '<>', $compareValue); - break; - case "co": - throw (new SCIMException(sprintf('"co" is not supported for attribute "%s"', $this->getFullKey())))->setCode(501); - break; - case "sw": - throw (new SCIMException(sprintf('"sw" is not supported for attribute "%s"', $this->getFullKey())))->setCode(501); - break; - case "ew": - throw (new SCIMException(sprintf('"ew" is not supported for attribute "%s"', $this->getFullKey())))->setCode(501); - break; - case "pr": - $result = $collection->where($attribute, '!=', null); - break; - case "gt": - $result = $collection->where($attribute, '>', $compareValue); - break; - case "ge": - $result = $collection->where($attribute, '>=', $compareValue); - break; - case "lt": - $result = $collection->where($attribute, '<', $compareValue); - break; - case "le": - $result = $collection->where($attribute, '<=', $compareValue); - break; - default: - die("Not supported!!"); - break; - - } - - foreach ($collectionOriginal->keys()->all() as $key) { - if (!in_array($key, (array)$result->keys()->all())) { - unset($collectionOriginal[$key]); - } - } - - return $collectionOriginal; - } - - /** - * Get an operator checker callback. - * - * @param string $key - * @param string $operator - * @param mixed $value - * @return \Closure - */ - protected function operatorForWhere($key, $operator, $value = null) - { - if (func_num_args() == 2) { - $value = $operator; - - $operator = '='; - } - - return function ($item) use ($key, $operator, $value) { - $retrieved = data_get($item, $key); - - $strings = array_filter( - [$retrieved, $value], - function ($value) { - return is_string($value) || (is_object($value) && method_exists($value, '__toString')); - } - ); - - if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { - return in_array($operator, ['!=', '<>', '!==']); - } - - switch ($operator) { - default: - case '=': - case '==': - return $retrieved == $value; - case '!=': - case '<>': - return $retrieved != $value; - case '<': - return $retrieved < $value; - case '>': - return $retrieved > $value; - case '<=': - return $retrieved <= $value; - case '>=': - return $retrieved >= $value; - case '===': - return $retrieved === $value; - case '!==': - return $retrieved !== $value; - } - }; + $query->whereHas( + $this->attribute, + fn (Builder $query) => $attribute->applyComparison($query, $path->shiftAttributePathAttributes(), $this->attribute) + )->get(); } - public function applyWhereCondition(&$query, $operator, $value) + public function replace($value, Model &$object, Path $path = null) { - throw (new SCIMException(sprintf('Filter is not supported for attribute "%s"', $this->getFullKey())))->setCode(501); + // ignore replace actions } } diff --git a/src/Attribute/CollectionValue.php b/src/Attribute/CollectionValue.php deleted file mode 100644 index 698e78e3..00000000 --- a/src/Attribute/CollectionValue.php +++ /dev/null @@ -1,7 +0,0 @@ -getSchemaNodes())->map(fn ($element) => $element->name)->values()->toArray(); + } + + + public function read(&$object, array $attributes = []): ?AttributeValue + { + if (!($this instanceof Schema) && $this->parent != null && !empty($attributes) && !in_array($this->name, $attributes) && !in_array($this->getFullKey(), $attributes)) { + return null; + } + + $result = $this->doRead($object, $attributes); + return !empty($result) ? new AttributeValue($result) : null; + } + + protected function doRead(&$object, $attributes = []) + { + $result = []; + foreach ($this->subAttributes as $attribute) { + if(($r = $attribute->read($object, $attributes)) != null){ + $result[$attribute->name] = $r->value; + } + } + return $result; + } + + public function patch($operation, $value, Model &$object, Path $path = null, $removeIfNotSet = false) + { + $this->dirty = true; + + if($this->mutability == 'readOnly'){ + // silently ignore + return; + } + + if ($path != null && $path->isNotEmpty()) { + $attributeNames = $path->getValuePathAttributes(); + + if (!empty($attributeNames)) { + // TODO: search for schema node + $attribute = $this->getSubNode($attributeNames[0]); + if ($attribute != null) { + $attribute->patch($operation, $value, $object, $path->shiftValuePathAttributes()); + } elseif ($this->parent == null) { + // pass the unchanged path object to the schema node + $this->getSchemaNode()->patch($operation, $value, $object, $path); + } else { + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + } + } elseif ($path->getValuePathFilter() != null) { + // apply filtering here, for each match, call replace with updated path + throw new \Exception('Filtering not implemented for this complex attribute'); + } elseif ($path->getAttributePath() != null) { + $attributeNames = $path?->getAttributePath()?->getAttributeNames() ?? []; + + if (!empty($attributeNames)) { + $attribute = $this->getSubNode($attributeNames[0]); + if ($attribute != null) { + $attribute->patch($operation, $value, $object, $path->shiftAttributePathAttributes()); + } elseif ($this->parent == null) { + // this is the root node, check within the first (the default) schema node + // pass the unchanged path object + $this->getSchemaNode()->patch($operation, $value, $object, $path); + } else { + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + } + } + } + } else { + // if there is no path, keys of value are attribute names + switch($operation) { + case 'replace': + $this->replace($value, $object, $path, false); + break; + case 'add': + $this->add($value, $object, $path); + break; + case 'remove': + $this->remove($value, $object, $path); + break; + default: + throw new SCIMException('Unknown operation: ' . $operation); + } + } + } + + /* + * @param $value + * @param Model $object + */ + public function replace($value, Model &$object, Path $path = null, $removeIfNotSet = false) + { + $match = false; + $this->dirty = true; + + if($this->mutability == 'readOnly'){ + // silently ignore + return; + } + + // if there is no path, keys of value are attribute names + foreach ($value as $key => $v) { + if (is_numeric($key)) { + throw new SCIMException('Invalid key: ' . $key . ' for complex object ' . $this->getFullKey()); + } + + $attribute = $this->getSubNode($key); + if ($attribute != null) { + $attribute->replace($v, $object, $path); + $match = true; + } + } + + // if this is the root, we may also check the schema nodes + if (!$match && $this->parent == null) { + foreach ($this->subAttributes as $attribute) { + if ($attribute instanceof Schema) { + $attribute->replace($value, $object, $path); + } + } + } + + if ($removeIfNotSet) { + foreach ($this->subAttributes as $attribute) { + if (!$attribute->isDirty()) { + $attribute->remove(null, $object); + } + } + } + } + + + public function remove($value, Model &$object, string $path = null) + { + if($this->mutability == 'readOnly'){ + // silently ignore + return; + } + // TODO: implement + } + + public function getSortAttributeByPath(Path $path) + { + if ($path->getValuePath() != null) { + throw new SCIMException('Incorrect sortBy parameter'); + } + + $attributeNames = $path->getAttributePathAttributes(); + + if (empty($attributeNames)) { + throw new SCIMException('Incorrect sortBy parameter. No attributes.'); + } + + $result = null; + + // TODO: search for schema node + $attribute = $this->getSubNode($attributeNames[0]); + if ($attribute != null) { + $result = $attribute->getSortAttributeByPath($path->shiftAttributePathAttributes()); + } elseif ($this->parent == null) { + // pass the unchanged path object to the schema node + $result = $this->getSchemaNode()->getSortAttributeByPath($path); + } else { + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + } + + return $result; + } + + public function applyComparison(Builder &$query, Path $path, $parentAttribute = null){ + + if ($path != null && $path->isNotEmpty()) { + $attributeNames = $path->getValuePathAttributes(); + + if (!empty($attributeNames)) { + // TODO: search for schema node + $attribute = $this->getSubNode($attributeNames[0]); + if ($attribute != null) { + // ($operation, $value, $object, $path->shiftValuePathAttributes()); + $attribute->applyComparison($query, $path->shiftValuePathAttributes()); + } elseif ($this->parent == null) { + // pass the unchanged path object to the schema node + $this->getSchemaNode()->applyComparison($query, $path); + } else { + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + } + } elseif ($path->getValuePathFilter() != null) { + // apply filtering here, for each match, call replace with updated path + throw new \Exception('Filtering not implemented for this complex attribute'); + } elseif ($path->getAttributePath() != null) { + $attributeNames = $path?->getAttributePath()?->getAttributeNames() ?? []; + + if (!empty($attributeNames)) { + $attribute = $this->getSubNode($attributeNames[0]); + if ($attribute != null) { + $attribute->applyComparison($query, $path->shiftAttributePathAttributes()); + } elseif ($this->parent == null) { + // this is the root node, check within the first (the default) schema node + // pass the unchanged path object + $this->getSchemaNode()->applyComparison($query, $path); + } else { + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + } + } + } + } + + } +} diff --git a/src/Attribute/Constant.php b/src/Attribute/Constant.php new file mode 100644 index 00000000..722d715a --- /dev/null +++ b/src/Attribute/Constant.php @@ -0,0 +1,45 @@ +value = $value; + } + + protected function doRead(&$object, $attributes = []) + { + return $this->value; + } + + public function add($value, &$object) + { + return $this->replace($value, $object); + } + + public function replace($value, &$object, $path = null) + { + $current = json_encode($this->read($object)?->value); + + if (json_encode($value) != $current) { + throw (new SCIMException(sprintf('Write to "%s" is not supported, tried to change "%s" to "%s"', $this->getFullKey(), $current, json_encode($value))))->setCode(500)->setScimType('mutability'); + } + + $this->dirty = true; + } + + public function patch($operation, $value, Model &$object, ?Path $path = null) + { + throw new SCIMException('Patch operation not supported for constant attributes'); + } +} diff --git a/src/Attribute/Eloquent.php b/src/Attribute/Eloquent.php new file mode 100644 index 00000000..c3d82127 --- /dev/null +++ b/src/Attribute/Eloquent.php @@ -0,0 +1,125 @@ +attribute = $attribute ?? $name; + $this->setSortAttribute($this->attribute); + } + + protected function doRead(&$object, $attributes = []) + { + $value = $object->{$this->attribute}; + + if ($value instanceof \Carbon\Carbon) { + $value = $value->format('c'); + } + return $value; + } + + public function add($value, Model &$object) + { + $object->{$this->attribute} = $value; + $this->dirty = true; + } + + public function replace($value, Model &$object, $path = null) + { + $object->{$this->attribute} = $value; + $this->dirty = true; + } + + public function patch($operation, $value, Model &$object, ?Path $path = null) + { + if ($path->isNotEmpty()) { + throw new SCIMException('path operation not support for eloquent type attributes'); + } + + if ($operation == 'replace' || $operation == 'add') { + $object->{$this->attribute} = $value; + $this->dirty = true; + } elseif ($operation == 'remove') { + $object->{$this->attribute} = null; + $this->dirty = true; + } + } + + public function getSortAttributeByPath(Path $path) + { + if ($path->getValuePath() != null) { + throw new SCIMException('Incorrect sortBy parameter'); + } + + return $this->attribute; + } + + public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) + { + $attribute = $this->attribute; + + // FIXME: ugly and perhaps incorrect. + if($parentAttribute != null){ + $attribute = $parentAttribute . '.' . $attribute; + } + + $operator = $path->node->operator; + $value = $path->node->compareValue; + + switch ($operator) { + case "eq": + $query->where($attribute, $value); + break; + case "ne": + $query->where($attribute, '<>', $value); + break; + case "co": + $query->where($attribute, 'like', '%' . addcslashes($value, '%_') . '%'); + break; + case "sw": + $query->where($attribute, 'like', addcslashes($value, '%_') . '%'); + break; + case "ew": + $query->where($attribute, 'like', '%' . addcslashes($value, '%_')); + break; + case "pr": + $query->whereNotNull($attribute); + break; + case "gt": + $query->where($attribute, '>', $value); + break; + case "ge": + $query->where($attribute, '>=', $value); + break; + case "lt": + $query->where($attribute, '<', $value); + break; + case "le": + $query->where($attribute, '<=', $value); + break; + default: + throw new SCIMException("Unknown operator " . $operator); + } + } + + + public function setRelationship($relationship) + { + $this->relationship = $relationship; + + return $this; + } +} diff --git a/src/Attribute/EloquentAttributeMapping.php b/src/Attribute/EloquentAttributeMapping.php deleted file mode 100644 index fc72f8d8..00000000 --- a/src/Attribute/EloquentAttributeMapping.php +++ /dev/null @@ -1,11 +0,0 @@ -read ? ($this->read)($object) : self::eloquentAttributeToString($object->{$this->eloquentReadAttribute}); - } -} diff --git a/src/Attribute/Meta.php b/src/Attribute/Meta.php new file mode 100644 index 00000000..9697c469 --- /dev/null +++ b/src/Attribute/Meta.php @@ -0,0 +1,34 @@ +setMutability('readOnly')->withSubAttributes( + new Eloquent('created', 'created_at'), + new Eloquent('lastModified', 'updated_at'), + (new class ('location', $resourceType) extends Eloquent { + + function __construct($name, public $resourceType) + { + parent::__construct($name); + } + + protected function doRead(&$object, $attributes = []) + { + return route( + 'scim.resource', + [ + 'resourceType' => $this->resourceType, + 'resourceObject' => $object->id ?? "not-saved" + ] + ); + } + }), + // get substring of $resourceType, everything till the last character + new Constant('resourceType', substr($resourceType, 0, -1)) + ); + } +} \ No newline at end of file diff --git a/src/Attribute/MutableCollection.php b/src/Attribute/MutableCollection.php new file mode 100644 index 00000000..8bb891d9 --- /dev/null +++ b/src/Attribute/MutableCollection.php @@ -0,0 +1,76 @@ +pluck('value')->all(); + + // Check if objects exist + $existingObjects = $object + ->{$this->attribute}() + ->getRelated() + ->findMany($values) + ->map(fn ($o) => $o->getKey()); + + if (($diff = collect($values)->diff($existingObjects))->count() > 0) { + throw new SCIMException( + sprintf('One or more %s are unknown: %s', $this->attribute, implode(',', $diff->all())), + 500 + ); + } + + $object->{$this->attribute}()->syncWithoutDetaching($existingObjects->all()); + + $object->load($this->attribute); + } + + public function remove($value, Model &$object, string $path = null) + { + $values = collect($value)->pluck('value')->all(); + + $object->{$this->attribute}()->detach($values); + + $object->load($this->attribute); + } + + public function replace($value, Model &$object, ?Path $path = null) + { + $values = collect($value)->pluck('value')->all(); + + // Check if objects exist + $existingObjects = $object + ->{$this->attribute}() + ->getRelated() + ::findMany($values) + ->map(fn ($o) => $o->getKey()); + + if (($diff = collect($values)->diff($existingObjects))->count() > 0) { + throw new SCIMException( + sprintf('One or more %s are unknown: %s', $this->attribute, implode(',', $diff->all())), + 500 + ); + } + + $object->{$this->attribute}()->sync($existingObjects->all()); + } + + public function patch($operation, $value, Model &$object, ?Path $path = null) + { + if ($operation == 'add') { + $this->add($value, $object); + } elseif ($operation == 'remove') { + $this->remove($value, $object); + } elseif ($operation == 'replace') { + $this->replace($value, $object); + } else { + throw new SCIMException('Operation not supported: ' . $operation); + } + } +} diff --git a/src/Attribute/Schema.php b/src/Attribute/Schema.php new file mode 100644 index 00000000..7e15d14d --- /dev/null +++ b/src/Attribute/Schema.php @@ -0,0 +1,29 @@ + [ + "urn:ietf:params:scim:schemas:core:2.0:Schema" + ], + "id" => $this->name, + "meta" => [ + "resourceType" => "Schema", + "created" => "2001-01-01T00:00:00+00:00", + "lastModified" => "2001-01-01T00:00:00+00:00", + "version" => 'W/"1"', + "location" => route('scim.schemas', ['id' => $this->name]) + ], + // name is substring after last occurence of : + "name" => substr($this->name, strrpos($this->name, ':') + 1), + "description" => $this->description, + "attributes" => collect($this->subAttributes)->map(fn ($element) => $element->generateSchema())->toArray() + ]; + } + +} diff --git a/src/Helper.php b/src/Helper.php index 1303ca92..0f63f1b2 100644 --- a/src/Helper.php +++ b/src/Helper.php @@ -2,20 +2,16 @@ namespace ArieTimmerman\Laravel\SCIMServer; -use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; use Illuminate\Contracts\Support\Arrayable; use Tmilos\ScimFilterParser\Ast\ComparisonExpression; use Tmilos\ScimFilterParser\Ast\Negation; use Tmilos\ScimFilterParser\Ast\Conjunction; use Tmilos\ScimFilterParser\Ast\Disjunction; -use Tmilos\ScimFilterParser\Parser; -use Tmilos\ScimFilterParser\Mode; -use Tmilos\ScimFilterParser\Ast\Path; -use Tmilos\ScimFilterParser\Ast\AttributePath; use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; +use ArieTimmerman\Laravel\SCIMServer\Parser\Path as ParserPath; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; -use Illuminate\Support\Facades\DB; use Tmilos\ScimFilterParser\Ast\Factor; use Tmilos\ScimFilterParser\Ast\ValuePath; @@ -30,7 +26,7 @@ public static function getAuthUserClass() * * @param unknown $object */ - public static function prepareReturn(Arrayable $object, ResourceType $resourceType = null) + public static function prepareReturn(Arrayable $object, ResourceType $resourceType = null, array $attributes = []) { $result = null; @@ -39,7 +35,7 @@ public static function prepareReturn(Arrayable $object, ResourceType $resourceTy $result = []; foreach ($object as $key => $value) { - $result[] = self::objectToSCIMArray($value, $resourceType); + $result[] = self::objectToSCIMArray($value, $resourceType, $attributes); } } } @@ -51,64 +47,22 @@ public static function prepareReturn(Arrayable $object, ResourceType $resourceTy return $result; } - // TODO: Auto map eloquent attributes with scim naming to the correct attributes - public static function objectToSCIMArray($object, ResourceType $resourceType = null) + public static function objectToSCIMArray($object, ResourceType $resourceType = null, array $attributes = []) { - $userArray = $object->toArray(); - - // If the getDates-method exists, ensure proper formatting of date attributes - if (method_exists($object, 'getDates')) { - $dateAttributes = $object->getDates(); - foreach ($dateAttributes as $dateAttribute) { - if (isset($userArray[$dateAttribute])) { - $userArray[$dateAttribute] = $object->getAttribute($dateAttribute)->format('c'); - } - } + if($resourceType == null){ + return $object; } - $result = []; - - if ($resourceType != null) { - $mapping = $resourceType->getMapping(); - - $uses = $mapping->getEloquentAttributes(); - - $result = $mapping->read($object); - - foreach ($uses as $key) { - unset($userArray[$key]); - } - - if (!empty($userArray) && (($resourceType->getConfiguration()['map_unmapped']) ?? false)) { - $namespace = $resourceType->getConfiguration()['unmapped_namespace'] ?? null; - - $parent = null; - - if ($namespace != null) { - if (!isset($result[$namespace])) { - $result[$namespace] = []; - } - $parent = &$result[$namespace]; - } else { - $parent = &$result; - } - - foreach ($userArray as $key => $value) { - $parent[$key] = AttributeMapping::eloquentAttributeToString($value); - } - } + $mapping = $resourceType->getMapping(); + $result = $mapping->read($object, $attributes)->value; - if (config('scim.omit_main_schema_in_return')) { - $defaultSchema = collect($mapping->getDefaultSchema())->first(); + if (config('scim.omit_main_schema_in_return')) { + $defaultSchema = collect($mapping->getDefaultSchema())->first(); - $main = $result[$defaultSchema]; - unset($result[$defaultSchema]); + $main = $result[$defaultSchema]; + unset($result[$defaultSchema]); - $result = array_merge($result, $main); - } - - } else { - $result = $userArray; + $result = array_merge($result, $main); } return $result; @@ -139,38 +93,25 @@ public static function objectToSCIMResponse(Model $object, ResourceType $resourc return response(self::objectToSCIMArray($object, $resourceType))->setEtag(self::getResourceObjectVersion($object)); } - public static function objectToSCIMCreateResponse(Model $object, ResourceType $resourceType = null) - { - return self::objectToSCIMResponse($object, $resourceType)->setStatusCode(201); - } - /** * See https://tools.ietf.org/html/rfc7644#section-3.4.2.2 * - * @param unknown $query - * @param unknown $node * @throws SCIMException */ - public static function scimFilterToLaravelQuery(ResourceType $resourceType, &$query, $node) + public static function scimFilterToLaravelQuery(ResourceType $resourceType, Builder &$query, ParserPath $path) { - - //var_dump($node);exit; - + $node = $path->node; if ($node instanceof Negation) { $filter = $node->getFilter(); throw (new SCIMException('Negation filters not supported'))->setCode(400)->setScimType('invalidFilter'); } elseif ($node instanceof ComparisonExpression) { - $operator = strtolower($node->operator); - - $attributeConfig = $resourceType->getMapping()->getSubNodeWithPath($node); - - $attributeConfig->applyWhereCondition($query, $operator, $node->compareValue); + $resourceType->getMapping()->applyComparison($query, $path); } elseif ($node instanceof Conjunction) { foreach ($node->getFactors() as $factor) { $query->where( function ($query) use ($factor, $resourceType) { - Helper::scimFilterToLaravelQuery($resourceType, $query, $factor); + Helper::scimFilterToLaravelQuery($resourceType, $query, new ParserPath($factor, $factor->dump())); } ); } @@ -178,90 +119,15 @@ function ($query) use ($factor, $resourceType) { foreach ($node->getTerms() as $term) { $query->orWhere( function ($query) use ($term, $resourceType) { - Helper::scimFilterToLaravelQuery($resourceType, $query, $term); + Helper::scimFilterToLaravelQuery($resourceType, $query, new ParserPath($term, $term->dump())); } ); } } elseif ($node instanceof ValuePath) { - // ->filer - $getAttributePath = function () { - return $this->attributePath; - }; - - $getFilter = function () { - return $this->filter; - }; - - $query->whereExists( - function ($query) { - $query->select(DB::raw(1)) - ->from('users AS users2') - ->whereRaw('users.id = users2.id'); - } - ); - - - //$node-> + throw new SCIMException('ValuePath not supported'); } elseif ($node instanceof Factor) { - var_dump($node); - die("Not ok hier!\n"); - } - } - - /** - * $scimAttribute could be - * - urn:ietf:params:scim:schemas:core:2.0:User.userName - * - userName - * - urn:ietf:params:scim:schemas:core:2.0:User.userName.name.formatted - * - urn:ietf:params:scim:schemas:core:2.0:User.emails.value - * - emails.value - * - emails.0.value - * - schemas.0 - * - * @param unknown $name - * @param unknown $scimAttribute - * @return AttributeMapping - */ - public static function getAttributeConfig(ResourceType $resourceType, $scimAttribute) - { - $parser = new Parser(Mode::PATH()); - - $scimAttribute = preg_replace('/\.[0-9]+$/', '', $scimAttribute); - $scimAttribute = preg_replace('/\.[0-9]+\./', '.', $scimAttribute); - - $path = $parser->parse($scimAttribute); - - //TODO: FIX this. If $scimAttribute is a schema-indication, it should be considered as a schema - if ($scimAttribute == 'urn:ietf:params:scim:schemas:core:2.0:User') { - $attributePath = new AttributePath(); - $attributePath->schema = 'urn:ietf:params:scim:schemas:core:2.0:User'; - - $path = Path::fromAttributePath($attributePath); + throw new SCIMException('Unknown filter not supported'); } - - return $resourceType->getMapping()->getSubNodeWithPath($path); - } - - public static function getAttributeConfigOrFail(ResourceType $resourceType, $scimAttribute) - { - $result = self::getAttributeConfig($resourceType, $scimAttribute); - - if ($result == null) { - throw (new SCIMException(sprintf('Unknown attribute "%s"', $scimAttribute)))->setCode(400); - } - - return $result; - } - - public static function getEloquentSortAttribute(ResourceType $resourceType, $scimAttribute) - { - $mapping = self::getAttributeConfig($resourceType, $scimAttribute); - - if ($mapping == null || $mapping->getSortAttribute() == null) { - throw (new SCIMException("Invalid sort property"))->setCode(400)->setScimType('invalidFilter'); - } - - return $mapping->getSortAttribute(); } public static function getFlattenKey($parts, $schemas) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 2a9ef71f..60aab806 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -6,8 +6,6 @@ use Illuminate\Http\Request; use ArieTimmerman\Laravel\SCIMServer\Helper; use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; -use Tmilos\ScimFilterParser\Parser; -use Tmilos\ScimFilterParser\Mode; use ArieTimmerman\Laravel\SCIMServer\ResourceType; use Illuminate\Database\Eloquent\Model; use ArieTimmerman\Laravel\SCIMServer\Events\Delete; @@ -15,9 +13,9 @@ use ArieTimmerman\Laravel\SCIMServer\Events\Create; use ArieTimmerman\Laravel\SCIMServer\Events\Replace; use ArieTimmerman\Laravel\SCIMServer\Events\Patch; -use ArieTimmerman\Laravel\SCIMServer\SCIM\Schema; +use ArieTimmerman\Laravel\SCIMServer\Parser\Parser as ParserParser; use ArieTimmerman\Laravel\SCIMServer\PolicyDecisionPoint; -use Illuminate\Database\QueryException; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Validator; class ResourceController extends Controller @@ -63,24 +61,14 @@ public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint $resourceObject = $resourceType->getFactory()(); - $allAttributeConfigs = []; + $resourceType->getMapping()->replace($input, $resourceObject); - foreach ($flattened as $scimAttribute => $value) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $scimAttribute); - $attributeConfig->add($value, $resourceObject); - $allAttributeConfigs[] = $attributeConfig; - } + //validate + $newObject = Helper::flatten(Helper::objectToSCIMArray($resourceObject, $resourceType), $resourceType->getSchema()); - try { - $resourceObject->save(); - } catch (QueryException $exception) { - throw $exception; - // throw new SCIMException('Could not save this'); - } + $flattened = self::validateScim($resourceType, $newObject, $resourceObject); - foreach ($allAttributeConfigs as &$attributeConfig) { - $attributeConfig->writeAfter($flattened[$attributeConfig->getFullKey()], $resourceObject); - } + $resourceObject->save(); return $resourceObject; } @@ -111,7 +99,7 @@ public function create(Request $request, PolicyDecisionPoint $pdp, ResourceType { $resourceObject = $this->createObject($request, $pdp, $resourceType, $isMe); - return Helper::objectToSCIMCreateResponse($resourceObject, $resourceType); + return Helper::objectToSCIMResponse($resourceObject, $resourceType)->setStatusCode(201); } public function show(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType, Model $resourceObject) @@ -133,56 +121,17 @@ public function delete(Request $request, PolicyDecisionPoint $pdp, ResourceType public function replace(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType, Model $resourceObject, $isMe = false) { $originalRaw = Helper::objectToSCIMArray($resourceObject, $resourceType); - $original = Helper::flatten($originalRaw, $resourceType->getSchema()); - - //TODO: get flattend from $resourceObject - $flattened = Helper::flatten($request->input(), $resourceType->getSchema()); - $flattened = $this->validateScim($resourceType, $flattened, $resourceObject); + + $resourceType->getMapping()->replace($request->input(), $resourceObject, null, true); - $updated = []; + $newObject = Helper::flatten(Helper::objectToSCIMArray($resourceObject, $resourceType), $resourceType->getSchema()); - foreach ($flattened as $key => $value) { - if (!isset($original[$key]) || json_encode($original[$key]) != json_encode($flattened[$key])) { - $updated[$key] = $flattened[$key]; - } - } + $flattened = $this->validateScim($resourceType, $newObject, $resourceObject); - if (!self::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PUT, $updated, $resourceType, null)) { + if (!self::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PATCH, $flattened, $resourceType, null)) { throw new SCIMException('This is not allowed'); } - //Keep an array of written values - $uses = []; - - //Write all values - foreach ($flattened as $scimAttribute => $value) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $scimAttribute); - - if ($attributeConfig->isWriteSupported()) { - $attributeConfig->replace($value, $resourceObject); - } - - $uses[] = $attributeConfig; - } - - //Find values that have not been written in order to empty these. - $allAttributeConfigs = $resourceType->getAllAttributeConfigs(); - - foreach ($uses as $use) { - foreach ($allAttributeConfigs as $key => $option) { - if ($use->getFullKey() == $option->getFullKey()) { - unset($allAttributeConfigs[$key]); - } - } - } - - foreach ($allAttributeConfigs as $attributeConfig) { - // Do not write write-only attribtues (such as passwords) - if ($attributeConfig->isReadSupported() && $attributeConfig->isWriteSupported()) { - // $attributeConfig->remove($resourceObject); - } - } - $resourceObject->save(); event(new Replace($resourceObject, $resourceType, $isMe, $request->input(), $originalRaw)); @@ -208,50 +157,19 @@ public function update(Request $request, PolicyDecisionPoint $pdp, ResourceType foreach ($input['Operations'] as $operation) { switch (strtolower($operation['op'])) { case "add": - if (isset($operation['path'])) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $operation['path']); - foreach ((array) $operation['value'] as $value) { - $attributeConfig->add($value, $resourceObject); - } - } else { - foreach ((array) $operation['value'] as $key => $value) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $key); - - foreach ((array) $value as $v) { - $attributeConfig->add($v, $resourceObject); - } - } - } - + $resourceType->getMapping()->patch('add', $operation['value'], $resourceObject, ParserParser::parse($operation['path'] ?? null)); break; case "remove": if (isset($operation['path'])) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $operation['path']); - $attributeConfig->remove($operation['value'] ?? null, $resourceObject); + $resourceType->getMapping()->patch('remove', $operation['value'], $resourceObject, ParserParser::parse($operation['path'] ?? null)); } else { throw new SCIMException('You MUST provide a "Path"'); } - - break; case "replace": - if (isset($operation['path'])) { - // Removed this check. A replace with a null/empty value should be valid. - // if(!isset($operation['value'])){ - // throw new SCIMException('Please provide a "value"',400); - // } - - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $operation['path']); - $attributeConfig->replace($operation['value'], $resourceObject); - } else { - foreach ((array) $operation['value'] as $key => $value) { - $attributeConfig = Helper::getAttributeConfigOrFail($resourceType, $key); - $attributeConfig->replace($value, $resourceObject); - } - } - + $resourceType->getMapping()->patch('replace', $operation['value'], $resourceObject, ParserParser::parse($operation['path'] ?? null)); break; default: @@ -261,7 +179,6 @@ public function update(Request $request, PolicyDecisionPoint $pdp, ResourceType $dirty = $resourceObject->getDirty(); - // TODO: prevent something from getten written before ... $newObject = Helper::flatten(Helper::objectToSCIMArray($resourceObject, $resourceType), $resourceType->getSchema()); $flattened = $this->validateScim($resourceType, $newObject, $resourceObject); @@ -302,18 +219,15 @@ public function index(Request $request, PolicyDecisionPoint $pdp, ResourceType $ $sortBy = null; if ($request->input('sortBy')) { - $sortBy = Helper::getEloquentSortAttribute($resourceType, $request->input('sortBy')); + $sortBy = $resourceType->getMapping()->getSortAttributeByPath(\ArieTimmerman\Laravel\SCIMServer\Parser\Parser::parse($request->input('sortBy'))); } $resourceObjectsBase = $query->when( $filter = $request->input('filter'), - function ($query) use ($filter, $resourceType) { - $parser = new Parser(Mode::FILTER()); - + function (Builder $query) use ($filter, $resourceType) { try { - $node = $parser->parse($filter); - Helper::scimFilterToLaravelQuery($resourceType, $query, $node); + Helper::scimFilterToLaravelQuery($resourceType, $query, ParserParser::parseFilter($filter)); } catch (\Tmilos\ScimFilterParser\Error\FilterException $e) { throw (new SCIMException($e->getMessage()))->setCode(400)->setScimType('invalidFilter'); } @@ -322,9 +236,13 @@ function ($query) use ($filter, $resourceType) { $totalResults = $resourceObjectsBase->count(); - $resourceObjects = $resourceObjectsBase->skip($startIndex - 1)->take($count); - - $resourceObjects = $resourceObjects->with($resourceType->getWithRelations()); + /** + * @var \Illuminate\Database\Query\Builder $resourceObjects + */ + $resourceObjects = $resourceObjectsBase + ->skip($startIndex - 1) + ->take($count) + ->with($resourceType->getWithRelations()); if ($sortBy != null) { $direction = $request->input('sortOrder') == 'descending' ? 'desc' : 'asc'; @@ -334,8 +252,15 @@ function ($query) use ($filter, $resourceType) { $resourceObjects = $resourceObjects->get(); + // TODO: splitting the attributes parameters by dot and comma is not correct, but works in most cases + $attributes = $request->input('attributes') ? preg_split('/[,.]/', $request->input('attributes')) : []; + + if(!empty($attributes)){ + $attributes[] = 'id'; + $attributes[] = 'meta'; + $attributes[] = 'schemas'; + } - $attributes = []; $excludedAttributes = []; return new ListResponse($resourceObjects, $startIndex, $totalResults, $attributes, $excludedAttributes, $resourceType); diff --git a/src/Http/Controllers/ResourceTypesController.php b/src/Http/Controllers/ResourceTypesController.php index 520fcfad..f3adfafe 100644 --- a/src/Http/Controllers/ResourceTypesController.php +++ b/src/Http/Controllers/ResourceTypesController.php @@ -4,7 +4,6 @@ use ArieTimmerman\Laravel\SCIMServer\SCIM\ListResponse; use ArieTimmerman\Laravel\SCIMServer\SCIM\ResourceType; -use ArieTimmerman\Laravel\SCIMServer\SCIM\Schema; use Illuminate\Http\Request; use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; @@ -21,7 +20,9 @@ public function __construct() $resourceTypes = []; foreach ($config as $key => $value) { - $resourceTypes[] = new ResourceType($value['singular'], $key, $key, $value['description'] ?? null, Schema::SCHEMA_USER, [ ]); + $schemas = $value['map']->getSchemas(); + + $resourceTypes[] = new ResourceType($value['singular'], $key, $key, $value['description'] ?? null, $schemas[0], array_slice($schemas, 1)); } $this->resourceTypes = collect($resourceTypes); diff --git a/src/Http/Controllers/SchemaController.php b/src/Http/Controllers/SchemaController.php index b70aee6f..72be376e 100644 --- a/src/Http/Controllers/SchemaController.php +++ b/src/Http/Controllers/SchemaController.php @@ -9,43 +9,29 @@ class SchemaController extends Controller { - private $schemas = null; - public function getSchemas() { - if ($this->schemas != null) { - return $this->schemas; - } - $config = resolve(SCIMConfig::class)->getConfig(); + $schemaNodes = []; $schemas = []; foreach ($config as $key => $value) { - if ($key != 'Users' && $key != 'Groups') { - continue; - } - - // TODO: FIX THIS. Schema is now an array but should be a string - $schema = (new SchemaBuilderV2())->get($value['schema'][0]); + $value['map']->generateSchema(); - if ($schema == null) { - throw new SCIMException("Schema not found"); - } - - $schema->getMeta()->setLocation(route('scim.schemas', ['id' => $schema->getId()])); - - $schemas[] = $schema->serializeObject(); + $schemaNodes = array_merge($schemaNodes, $value['map']->getSchemaNodes()); } - $this->schemas = collect($schemas); + foreach ($schemaNodes as $schemaNode) { + $schemas[] = $schemaNode->generateSchema(); + } - return $this->schemas; + return $schemas; } public function show($id) { - $result = $this->getSchemas()->first( + $result = collect($this->getSchemas())->first( function ($value, $key) use ($id) { return $value['id'] == $id; } @@ -60,6 +46,7 @@ function ($value, $key) use ($id) { public function index() { - return new ListResponse($this->getSchemas(), 1, $this->getSchemas()->count()); + $schemas = collect($this->getSchemas()); + return new ListResponse($schemas, 1, $schemas->count()); } } diff --git a/src/Parser/AttributePath.php b/src/Parser/AttributePath.php new file mode 100644 index 00000000..8326872d --- /dev/null +++ b/src/Parser/AttributePath.php @@ -0,0 +1,21 @@ +path = $path; + } + + public function getAttributeNames(){ + return $this->path->attributeNames; + } + + public function shiftAttributeName(){ + return array_shift($this->path->attributeNames); + } +} diff --git a/src/Parser/Filter.php b/src/Parser/Filter.php new file mode 100644 index 00000000..8fd1d61b --- /dev/null +++ b/src/Parser/Filter.php @@ -0,0 +1,15 @@ +filter = $filter; + } + +} diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php new file mode 100644 index 00000000..c8f4bab4 --- /dev/null +++ b/src/Parser/Parser.php @@ -0,0 +1,34 @@ +parse($input); + + return new Path( + $node, + $input + ); + } + + public static function parseFilter($input): Path { + if($input == null){ + return null; + } + + $node = (new ScimFilterParserParser(Mode::FILTER()))->parse($input); + + return new Path( + $node, + $input + ); + } +} diff --git a/src/Parser/Path.php b/src/Parser/Path.php new file mode 100644 index 00000000..18f47360 --- /dev/null +++ b/src/Parser/Path.php @@ -0,0 +1,98 @@ +node = $node; + $this->text = $text; + + // attribute path + $getAttributePath = function () { + return $this->attributePath; + }; + + $attributePath = $getAttributePath->call($this->node); + $this->attributePath = $attributePath != null ? new AttributePath($attributePath) : null; + + //value path + $getValuePath = function () { + return $this instanceof Factor ? null : $this->valuePath; + }; + + $valuePath = $getValuePath->call($this->node); + $this->valuePath = $valuePath != null ? new ValuePath($valuePath) : null; + + } + + public function getAttributePath(): ?AttributePath{ + return $this->attributePath; + } + + public function setAttributePath($attributePath){ + $this->attributePath = $attributePath; + } + + public function getValuePath(): ?ValuePath{ + return $this->valuePath; + } + + public function setValuePath($valuePath){ + $this->valuePath = $valuePath; + } + + public function getValuePathAttributes(): array{ + return $this->getValuePath()?->getAttributePath()?->getAttributeNames() ?? []; + } + + public function getAttributePathAttributes(): array{ + return $this->getAttributePath()?->getAttributeNames() ?? []; + } + + public function getValuePathFilter(){ + return $this->getValuePath()?->getFilter(); + } + + public function setValuePathFilter($filter){ + return $this->getValuePath()?->setFilter($filter); + } + + public function shiftValuePathAttributes(): Path { + $this->getValuePath()->getAttributePath()->shiftAttributeName(); + + if(empty($this->getValuePathAttributes())){ + $this->setValuePath(null); + } + + return $this; + } + + public function shiftAttributePathAttributes(): Path { + $this->getAttributePath()->shiftAttributeName(); + + if(empty($this->getAttributePathAttributes())){ + $this->setAttributePath(null); + } + + return $this; + } + + public function __toString() + { + return $this->text; + } + + public function isNotEmpty(){ + return + !empty($this->getAttributePathAttributes()) || + !empty($this->getAttributePathAttributes()) || + $this->getValuePathFilter() != null; + } +} \ No newline at end of file diff --git a/src/Parser/ValuePath.php b/src/Parser/ValuePath.php new file mode 100644 index 00000000..089722fb --- /dev/null +++ b/src/Parser/ValuePath.php @@ -0,0 +1,45 @@ +path = $path; + + $getAttributePath = function () { + return $this->attributePath; + }; + + $this->attributePath = new AttributePath($getAttributePath->call($this->path)); + + $getFilter = function () { + return $this->filter; + }; + + $this->filter = new Filter($getFilter->call($this->path)); + + } + + public function getAttributePath(): AttributePath{ + return $this->attributePath; + } + + public function setAttributePath(AttributePath $attributePath){ + $this->attributePath = $attributePath; + } + + public function getFilter(): Filter{ + return $this->filter; + } + + public function setFilter($filter){ + $this->filter = $filter; + } +} \ No newline at end of file diff --git a/src/ResourceType.php b/src/ResourceType.php index 3b662a79..40d71e3e 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -2,8 +2,9 @@ namespace ArieTimmerman\Laravel\SCIMServer; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Complex; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Arr; -use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; class ResourceType { @@ -21,14 +22,9 @@ public function getConfiguration() return $this->configuration; } - public function getMapping() + public function getMapping(): Complex { - $mapping = $this->configuration['mapping']; - - if (!isset($mapping['schema'])) { - $mapping['schemas'] = AttributeMapping::constant($this->configuration['schema'])->ignoreWrite(); - } - return AttributeMapping::object($mapping ?? [])->setDefaultSchema($this->configuration['schema']); + return $this->configuration['map']; } public function getName() @@ -38,7 +34,7 @@ public function getName() public function getSchema() { - return $this->configuration['schema']; + return $this->getMapping()->getSchemas(); } public function getClass() @@ -55,14 +51,14 @@ public function getFactory() }; } - public function getQuery() + public function getQuery(): Builder { return Arr::get($this->configuration, 'query') ?? $this->getClass()::query(); } public function getValidations() { - return $this->configuration['validations']; + return $this->getMapping()->getValidations(); } public function getWithRelations() @@ -74,27 +70,4 @@ public static function user() { return new ResourceType('Users', resolve(SCIMConfig::class)->getUserConfig()); } - - public function getAllAttributeConfigs($mapping = -1) - { - $result = []; - - if ($mapping == -1) { - $mapping = $this->getMapping(); - } - - foreach ($mapping as $key => $value) { - if ($value instanceof AttributeMapping && $value != null) { - $result[] = $value; - } elseif (is_array($value)) { - $extra = $this->getAllAttributeConfigs($value); - - if (!empty($extra)) { - $result = array_merge($result, $extra); - } - } - } - - return $result; - } } diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 82bb15eb..548159a5 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -1,9 +1,6 @@ resourceType = $resourceType; $this->resourceObjects = $resourceObjects; $this->startIndex = $startIndex; $this->totalResults = $totalResults; - $this->attribtues = $attributes; + $this->attributes = $attributes; $this->excludedAttributes = $excludedAttributes; } @@ -29,7 +29,7 @@ public function toJson($options = 0) { return json_encode($this->toSCIMArray(), $options); } - + public function toSCIMArray() { return [ @@ -39,7 +39,7 @@ public function toSCIMArray() "schemas" => [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], - 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType), + 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType, $this->attributes), ]; } } diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index c5e3e995..ee9d1460 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -4,181 +4,193 @@ use ArieTimmerman\Laravel\SCIMServer\SCIM\Schema; use ArieTimmerman\Laravel\SCIMServer\Helper; -use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; -use Illuminate\Support\Facades\Auth; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Attribute; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Collection; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Complex; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Constant; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Eloquent; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Meta; +use ArieTimmerman\Laravel\SCIMServer\Attribute\MutableCollection; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Schema as AttributeSchema; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\Group; +use Illuminate\Database\Eloquent\Model; + +function a($name = null): Attribute +{ + return new Attribute($name); +} + +function complex($name = null): Complex +{ + return new Complex($name); +} + +function eloquent($name, $attribute = null): Attribute +{ + return new Eloquent($name, $attribute); +} + class SCIMConfig { + + public function __construct() + { + } + public function getConfigForResource($name) { - if ($name == 'Users') { - return $this->getUserConfig(); - } else { - $result = $this->getConfig(); - return @$result[$name]; - } + $result = $this->getConfig(); + return @$result[$name]; } public function getUserConfig() { return [ - + // Set to 'null' to make use of auth.providers.users.model (App\User::class) 'class' => Helper::getAuthUserClass(), - - // Set to 'null' to make use of $class::query() - 'query' => null, - - // Set to 'null' to make use new $class() - 'factory' => null, - - 'validations' => [ - - 'urn:ietf:params:scim:schemas:core:2\.0:User:userName' => 'required', - 'urn:ietf:params:scim:schemas:core:2\.0:User:password' => 'nullable', - 'urn:ietf:params:scim:schemas:core:2\.0:User:active' => 'boolean', - 'urn:ietf:params:scim:schemas:core:2\.0:User:emails' => 'required|array', - 'urn:ietf:params:scim:schemas:core:2\.0:User:emails.*.value' => 'required|email', - 'urn:ietf:params:scim:schemas:core:2\.0:User:roles' => 'nullable|array', - 'urn:ietf:params:scim:schemas:core:2\.0:User:roles.*.value' => 'required', - - ], - 'singular' => 'User', - 'schema' => [ - Schema::SCHEMA_USER, - 'example:name:space' - ], - + //eager loading 'withRelations' => [], - 'map_unmapped' => true, - 'unmapped_namespace' => 'urn:ietf:params:scim:schemas:laravel:unmapped', 'description' => 'User Account', - - // Map a SCIM attribute to an attribute of the object. - 'mapping' => [ - - 'id' => (new AttributeMapping())->setRead( - function (&$object) { + + 'map' => complex()->withSubAttributes( + new class ('schemas', [ + "urn:ietf:params:scim:schemas:core:2.0:User", + ]) extends Constant { + public function replace($value, &$object, $path = null) + { + // do nothing + $this->dirty = true; + } + }, + (new class ('id', null) extends Constant { + protected function doRead(&$object, $attributes = []) + { return (string)$object->id; } - )->disableWrite(), - - 'externalId' => null, - - 'meta' => [ - 'created' => AttributeMapping::eloquent("created_at")->disableWrite(), - 'lastModified' => AttributeMapping::eloquent("updated_at")->disableWrite(), - - 'location' => (new AttributeMapping())->setRead( - function ($object) { - return route( - 'scim.resource', - [ - 'resourceType' => 'Users', - 'resourceObject' => $object->id - ] - ); + } + ), + new Meta('Users'), + (new AttributeSchema(Schema::SCHEMA_USER, true))->withSubAttributes( + eloquent('userName', 'name')->ensure('required'), + complex('name')->withSubAttributes(eloquent('formatted', 'name')), + eloquent('password')->ensure('nullable'), + (new class ('emails') extends Complex { + protected function doRead(&$object, $attributes = []) + { + return collect([$object->email])->map(function ($email) { + return [ + 'value' => $email, + 'type' => 'other', + 'primary' => true + ]; + })->toArray(); } - )->disableWrite(), - - 'resourceType' => AttributeMapping::constant("User") - ], - - 'example:name:space' => [ - 'cityPrefix' => AttributeMapping::eloquent('cityPrefix') - ], - - 'urn:ietf:params:scim:schemas:core:2.0:User' => [ - - 'userName' => AttributeMapping::eloquent("name"), - - 'name' => [ - 'formatted' => AttributeMapping::eloquent("name"), - 'familyName' => null, - 'givenName' => null, - 'middleName' => null, - 'honorificPrefix' => null, - 'honorificSuffix' => null - ], - - 'displayName' => null, - 'nickName' => null, - 'profileUrl' => null, - 'title' => null, - 'userType' => null, - 'preferredLanguage' => null, // Section 5.3.5 of [RFC7231] - 'locale' => null, // see RFC5646 - 'timezone' => null, // see RFC6557 - 'active' => null, - - 'password' => AttributeMapping::eloquent('password')->disableRead(), - - // Multi-Valued Attributes - 'emails' => [[ - "value" => AttributeMapping::eloquent("email"), - "display" => null, - "type" => AttributeMapping::constant("other")->ignoreWrite(), - "primary" => AttributeMapping::constant(true)->ignoreWrite() - ],[ - "value" => AttributeMapping::eloquent("email"), - "display" => null, - "type" => AttributeMapping::constant("work")->ignoreWrite(), - "primary" => AttributeMapping::constant(true)->ignoreWrite() - ]], - - 'phoneNumbers' => [[ - "value" => null, - "display" => null, - "type" => null, - "primary" => null - ]], - - 'ims' => [[ - "value" => null, - "display" => null, - "type" => null, - "primary" => null - ]], // Instant messaging addresses for the User - - 'photos' => [[ - "value" => null, - "display" => null, - "type" => null, - "primary" => null - ]], - - 'addresses' => [[ - 'formatted' => null, - 'streetAddress' => null, - 'locality' => null, - 'region' => null, - 'postalCode' => null, - 'country' => null - ]], - - 'groups' => [[ - 'value' => null, - '$ref' => null, - 'display' => null, - 'type' => null, - 'type' => null - ]], - - 'entitlements' => null, - 'roles' => null, - 'x509Certificates' => null - ], - - ] - ]; + public function add($value, Model &$object) + { + $object->email = $value[0]['value']; + } + public function replace($value, Model &$object, $path = null, $removeIfNotSet = false) + { + $object->email = $value[0]['value']; + } + })->withSubAttributes( + eloquent('value', 'email')->ensure('required', 'email'), + new Constant('type', 'other'), + new Constant('primary', true) + )->ensure('required', 'array') + ->setMultiValued(true), + (new Collection('groups'))->withSubAttributes( + eloquent('value', 'id'), + (new class ('$ref') extends Eloquent { + protected function doRead(&$object, $attributes = []) + { + return route( + 'scim.resource', + [ + 'resourceType' => 'Group', + 'resourceObject' => $object->id ?? "not-saved" + ] + ); + } + }), + eloquent('display', 'name') + ), + ), + (new AttributeSchema('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', true))->withSubAttributes( + eloquent('employeeNumber')->ensure('nullable') + ) + ), + ]; + } + + public function getGroupConfig() + { + return [ + + 'class' => Group::class, + 'singular' => 'Group', + + //eager loading + 'withRelations' => [], + 'description' => 'Group', + + 'map' => complex()->withSubAttributes( + new class ('schemas', [ + "urn:ietf:params:scim:schemas:core:2.0:Group", + ]) extends Constant { + public function replace($value, &$object, $path = null) + { + // do nothing + $this->dirty = true; + } + }, + (new class ('id', null) extends Constant { + protected function doRead(&$object, $attributes = []) + { + return (string)$object->id; + } + } + ), + new Meta('Groups'), + (new AttributeSchema(Schema::SCHEMA_GROUP, true))->withSubAttributes( + eloquent('name')->ensure('required', 'min:3', function ($attribute, $value, $fail) { + // check if group does not exist or if it exists, it is the same group + $group = Group::where('name', $value)->first(); + if ($group && (request()->route('resourceObject') == null || $group->id != request()->route('resourceObject')->id)) { + $fail('The name has already been taken.'); + } + }), + eloquent('displayName')->ensure('nullable'), + (new MutableCollection('members'))->withSubAttributes( + eloquent('value', 'id')->ensure('required'), + (new class ('$ref') extends Eloquent { + protected function doRead(&$object, $attributes = []) + { + return route( + 'scim.resource', + [ + 'resourceType' => 'Users', + 'resourceObject' => $object->id ?? "not-saved" + ] + ); + } + }), + eloquent('display', 'name') + )->ensure('nullable', 'array') + ) + ), + ]; } public function getConfig() { return [ - 'Users' => $this->getUserConfig() + 'Users' => $this->getUserConfig(), + 'Groups' => $this->getGroupConfig(), ]; } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 4b9348f6..657326b9 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -5,7 +5,6 @@ namespace ArieTimmerman\Laravel\SCIMServer; -use Illuminate\Support\Facades\Route; use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; class ServiceProvider extends \Illuminate\Support\ServiceProvider diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 212d811d..59c928ff 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -2,19 +2,157 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; +use ArieTimmerman\Laravel\SCIMServer\ResourceType; +use ArieTimmerman\Laravel\SCIMServer\SCIMConfig; +use Illuminate\Support\Arr; + class BasicTest extends TestCase { public function testGet() { $response = $this->get('/scim/v2/Users'); + $response->assertStatus(200); + $response->assertJsonCount(10, 'Resources'); + $response->assertJson([ + 'totalResults' => 100, + 'itemsPerPage' => 10, + 'startIndex' => 1 + ]); + + $response->assertJsonStructure([ + 'Resources' => [ + '*' => [ + 'id', + 'schemas', + 'meta', + 'urn:ietf:params:scim:schemas:core:2.0:User' => [ + 'userName', + 'name', + 'emails', + 'groups' => [ + '*' => [ + 'value', + ] + ] + ] + ] + ] + ]); + } + + public function testGetAttributes() + { + $response = $this->get('/scim/v2/Users?attributes=userName,name.formatted,groups'); + + $response->assertStatus(200); + $response->assertJsonCount(10, 'Resources'); + $response->assertJsonStructure([ + 'Resources' => [ + '*' => [ + 'urn:ietf:params:scim:schemas:core:2.0:User' => [ + 'userName', + ] + ] + ] + ]); + } + + public function testPagination() + { + $response = $this->get('/scim/v2/Users?startIndex=21&count=20'); + + $response->assertStatus(200); + $response->assertJsonCount(20, 'Resources'); + $response->assertJson([ + 'totalResults' => 100, + 'itemsPerPage' => 20, + 'startIndex' => 21 + ]); + } + + public function testSort() + { + $response = $this->get('/scim/v2/Users?sortBy=name.formatted'); + + $response->assertStatus(200); + + $formattedNames = collect($response->json('Resources') ?? []) + ->map(function ($resource) { + return $resource['urn:ietf:params:scim:schemas:core:2.0:User']['name']['formatted'] ?? null; + }) + ->filter() // Remove null values + ->values() // Re-index the array + ->toArray(); + + $this->assertEquals(Arr::sort($formattedNames), $formattedNames); + } + + public function testFilter() + { + // First get a username to search for + $response = $this->get('/scim/v2/Users?startIndex=30&count=1'); + $userName = $response->json('Resources')[0]['urn:ietf:params:scim:schemas:core:2.0:User']['userName']; + + // Now search for this username + $response = $this->get('/scim/v2/Users?filter=userName eq "'.$userName.'"'); + $response->assertStatus(200); + + $this->assertEquals(1, count($response->json('Resources'))); + } + + public function testFilterByGroup() + { + // Find a group + $response = $this->get('/scim/v2/Groups?startIndex=30&count=1'); + $groupValue = $response->json('Resources')[0]['id']; + + // Now search for this username + $response = $this->get('/scim/v2/Users?startIndex=30&count=1'); + $userValue = $response->json('Resources')[0]['id']; + + // SCIM Patch request + $response = $this->patch('/scim/v2/Groups/' . $groupValue, [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "add", + "path" => "members", + "value" => [ + [ + "value" => $userValue + ] + ] + ]] + ]); + $response->assertStatus(200); } + public function testGroupAssignment() + { + // First get a username to search for + $response = $this->get('/scim/v2/Users?startIndex=20&count=1'); + $groupValue = $response->json('Resources')[0]['urn:ietf:params:scim:schemas:core:2.0:User']['groups'][0]['value']; + + // find user id + + // (3) assign user to group via group endpoitn + + $this->assertTrue(count($response->json('Resources')) >= 1); + } + public function testPut() { $response = $this->put('/scim/v2/Users/1', [ - "id" => 1, + "id" => "1", + "meta" => [ + "resourceType" => "User", + "created" => "2010-01-23T04:56:22Z", + "lastModified" => "2011-05-13T04:42:34Z", + "version" => "W\/\"3694e05e9dff594\"" + ], "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:User", ], @@ -26,6 +164,11 @@ public function testPut() "type" => "other", "primary" => true ] + ], + "groups" => [ + [ + "value" => "1" + ] ] ] ]); @@ -47,14 +190,13 @@ public function testPatch() ], "Operations" => [[ "op" => "replace", + "path" => "emails", "value" => [ - "emails" => [ [ "value" => "something@example.com", "type" => "work", "primary" => true ] - ] ] ]] ]); @@ -67,6 +209,27 @@ public function testPatch() $this->assertEquals('something@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); } + public function testPatchUsername() + { + $response = $this->patch('/scim/v2/Users/4', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "add", + "path" => "userName", + "value" => "johndoe@example.com" + ]] + ]); + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertEquals('johndoe@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + } + public function testDelete() { $response = $this->delete('/scim/v2/Users/1'); @@ -76,7 +239,7 @@ public function testDelete() public function testPost() { $response = $this->post('/scim/v2/Users', [ - "id" => 1, + // "id" => 1, "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:User", ], @@ -105,4 +268,37 @@ public function testPost() $this->assertEquals('mariejo@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); } + + public function testPostTopLevel() + { + $response = $this->post('/scim/v2/Users', [ + // "id" => 1, + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:User", + ], + + "userName" => "Dr. Marie Jo", + "password" => "Password123", + "emails" => [ + [ + "value" => "mariejo@example.com", + "type" => "primary", + "primary" => true + ] + ] + + ]); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertEquals('mariejo@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); + $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + } } diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php index 8cd66d00..6b3f8852 100644 --- a/tests/CustomSchemaTest.php +++ b/tests/CustomSchemaTest.php @@ -2,7 +2,7 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; -use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping; +use ArieTimmerman\Laravel\SCIMServer\Attribute\Attribute; use ArieTimmerman\Laravel\SCIMServer\SCIMConfig; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -17,7 +17,7 @@ public function getUserConfig() $config['validations']['urn:ietf:params:scim:schemas:extension:enterprise:2\.0:User:employeeNumber'] = 'nullable'; $config['mapping']['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'] = [ - 'employeeNumber' => AttributeMapping::eloquent("employeeNumber") + 'employeeNumber' => new Attribute("employeeNumber") ]; return $config; @@ -45,7 +45,7 @@ protected function getEnvironmentSetUp($app) public function testPost() { $response = $this->post('/scim/v2/Users', [ - "id" => 1, + // "id" => 1, "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:User", 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' diff --git a/tests/GroupsTest.php b/tests/GroupsTest.php new file mode 100644 index 00000000..c85d1bf6 --- /dev/null +++ b/tests/GroupsTest.php @@ -0,0 +1,116 @@ +string('employeeNumber')->nullable(); + }); + } + + protected function getEnvironmentSetUp($app) + { + parent::getEnvironmentSetUp($app); + } + + public function testGet() + { + $response = $this->get('/scim/v2/Groups'); + $response->assertStatus(200); + + $response->assertJsonStructure([ + "schemas", + "totalResults", + "itemsPerPage", + "startIndex", + "Resources" => [ + '*' => [ + 'id', + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'members' => [ + '*' => [ + 'value', + 'display' + ] + ] + ] + ] + ] + ]); + } + + public function testCreate(){ + $response = $this->post('/scim/v2/Groups', [ + 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'name' => 'testgroup1', + 'displayName' => 'TestGroup' + ] + ]); + + $response->assertJsonStructure([ + 'id', + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'name', + 'displayName' + ] + ]); + + $this->assertNotNull(Group::find($response->json('id'))); + $this->assertNotNull(Group::where('name', 'testgroup1')->first()); + + } + + public function testBulk(){ + $response = $this->post('/scim/v2/Bulk', [ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:BulkRequest'], // Required + 'Operations' => [ + [ + 'method' => 'POST', + 'path' => '/Groups', + 'data' => [ + 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'name' => 'testgroup1', + 'displayName' => 'TestGroup' + ] + ] + ], + [ + 'method' => 'POST', + 'path' => '/Groups', + 'data' => [ + 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'name' => 'testgroup2', + 'displayName' => 'TestGroup2' + ] + ] + ] + ] + ]); + + $response->assertJsonStructure([ + 'schemas', + 'Operations' => [ + '*' => [ + 'method', + 'location', + 'status' + ] + ] + ]); + + // confirm testgroup1 exists + $this->assertNotNull(Group::where('name', 'testgroup2')->first()); + } +} diff --git a/tests/Model/Group.php b/tests/Model/Group.php new file mode 100644 index 00000000..58732f2e --- /dev/null +++ b/tests/Model/Group.php @@ -0,0 +1,14 @@ +belongsToMany(User::class); + } +} diff --git a/tests/Model/User.php b/tests/Model/User.php index 282811e8..8c6bfbba 100644 --- a/tests/Model/User.php +++ b/tests/Model/User.php @@ -4,4 +4,8 @@ class User extends \Illuminate\Foundation\Auth\User { + public function groups() + { + return $this->belongsToMany(Group::class); + } } diff --git a/tests/ResourceTypesTest.php b/tests/ResourceTypesTest.php new file mode 100644 index 00000000..f5ff9303 --- /dev/null +++ b/tests/ResourceTypesTest.php @@ -0,0 +1,44 @@ +singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', CustomSCIMConfigSchema::class); + } + + public function testGet() + { + $response = $this->get('/scim/v2/ResourceTypes'); + $response->assertStatus(200); + } + + public function testGetOne(){ + $response = $this->get('/scim/v2/ResourceTypes/User'); + $response->assertJson([ + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:ResourceType" + ], + "id" => "User", + "name" => "Users", + "endpoint" => "http://localhost/scim/v2/Users", + "description" => "User Account", + "schema" => "urn:ietf:params:scim:schemas:core:2.0:User", + "schemaExtensions" => [ + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" + ], + "meta" => [ + "location" => "http://localhost/scim/v2/ResourceTypes/User", + "resourceType" => "ResourceType" + ] + ]); + + $response->assertStatus(200); + } + +} diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php new file mode 100644 index 00000000..5fcc7449 --- /dev/null +++ b/tests/SchemaTest.php @@ -0,0 +1,45 @@ + new Attribute("employeeNumber") + ]; + + return $config; + } +} + + +class SchemaTest extends TestCase +{ + + protected function getEnvironmentSetUp($app) + { + parent::getEnvironmentSetUp($app); + + $app->singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', CustomSCIMConfigSchema::class); + } + + public function testGet() + { + $response = $this->get('/scim/v2/Schemas'); + $response->assertStatus(200); + } + + public function getSchemaGenerator(){ + // generateSchema + } +} diff --git a/tests/ServiceProviderConfigTest.php b/tests/ServiceProviderConfigTest.php new file mode 100644 index 00000000..0118fb6c --- /dev/null +++ b/tests/ServiceProviderConfigTest.php @@ -0,0 +1,21 @@ +singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', CustomSCIMConfigSchema::class); + } + + public function testGet() + { + $response = $this->get('/scim/v2/ServiceProviderConfig'); + $response->assertStatus(200); + } + +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 9636015f..1cd3b281 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,8 +3,12 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; use ArieTimmerman\Laravel\SCIMServer\ServiceProvider; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\Group; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\User; +use Illuminate\Database\Schema\Blueprint; use Orchestra\Testbench\TestCase as BaseTestCase; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Schema; abstract class TestCase extends BaseTestCase { @@ -18,11 +22,40 @@ protected function setUp(): void $this->loadLaravelMigrations('testbench'); + Schema::create('groups', function (Blueprint $table) { + $table->increments('id'); + // timestamp columns + $table->timestamps(); + $table->string('name')->nullable(); + $table->string('displayName')->nullable(); + }); + + Schema::create('group_user', function (Blueprint $table) { + $table->increments('id'); + + $table->uuid('group_id'); + $table->uuid('user_id'); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade'); + + $table->unique(['user_id', 'group_id']); + + $table->timestamps(); + }); + $this->withFactories(realpath(dirname(__DIR__) . '/database/factories')); \ArieTimmerman\Laravel\SCIMServer\RouteProvider::routes(); - factory(\ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class, 100)->create(); + $users = factory(User::class, 100)->create(); + $groups = factory(Group::class, 100)->create(); + + $users->each(function ($user) use ($groups) { + $user->groups()->attach( + $groups->random(rand(1, 3))->pluck('id')->toArray() + ); + }); } protected function getEnvironmentSetUp($app) From 18ba66bbd10c484a573190f752600793a2d23d4c Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 8 Jul 2024 21:30:30 +0200 Subject: [PATCH 22/58] etag by default (#73) --- src/Attribute/Meta.php | 21 ++++++++++++++- src/Helper.php | 6 ++--- src/ServiceProvider.php | 2 +- tests/HeaderTest.php | 58 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/HeaderTest.php diff --git a/src/Attribute/Meta.php b/src/Attribute/Meta.php index 9697c469..91939410 100644 --- a/src/Attribute/Meta.php +++ b/src/Attribute/Meta.php @@ -2,6 +2,9 @@ namespace ArieTimmerman\Laravel\SCIMServer\Attribute; +use ArieTimmerman\Laravel\SCIMServer\Helper; +use Illuminate\Database\Eloquent\Model; + class Meta extends Complex { public function __construct($resourceType){ @@ -28,7 +31,23 @@ protected function doRead(&$object, $attributes = []) } }), // get substring of $resourceType, everything till the last character - new Constant('resourceType', substr($resourceType, 0, -1)) + new Constant('resourceType', substr($resourceType, 0, -1)), + new class ('version', null) extends Constant { + protected function doRead(&$object, $attributes = []) + { + return Helper::getResourceObjectVersion($object); + } + } ); } + + public function remove($value, Model &$object, ?string $path = null) + { + // ignore + } + + public function add($value, Model &$object) + { + // ignore + } } \ No newline at end of file diff --git a/src/Helper.php b/src/Helper.php index 0f63f1b2..68df2f63 100644 --- a/src/Helper.php +++ b/src/Helper.php @@ -76,11 +76,11 @@ public static function getResourceObjectVersion($object) if (method_exists($object, "getSCIMVersion")) { $version = $object->getSCIMVersion(); } else { - $version = sha1($object->getKey() . $object->updated_at . $object->created_at); + $version = sprintf('W/"%s"', sha1($object->getKey() . $object->updated_at . $object->created_at)); } // Entity tags uniquely representing the requested resources. They are a string of ASCII characters placed between double quotes - return sprintf('W/"%s"', $version); + return $version; } /** @@ -90,7 +90,7 @@ public static function getResourceObjectVersion($object) */ public static function objectToSCIMResponse(Model $object, ResourceType $resourceType = null) { - return response(self::objectToSCIMArray($object, $resourceType))->setEtag(self::getResourceObjectVersion($object)); + return response(self::objectToSCIMArray($object, $resourceType))->header('ETag', self::getResourceObjectVersion($object)); } /** diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 657326b9..e2e493a0 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -56,7 +56,7 @@ function ($id, $route) { //if as version is '*' it is always ok if (!in_array($currentVersion, $versionsAllowed) && !in_array('*', $versionsAllowed)) { - throw (new SCIMException('Failed to update. Resource changed on the server.'))->setCode(412); + throw (new SCIMException('Failed to update. Resource changed on the server.'))->setCode(412); } } diff --git a/tests/HeaderTest.php b/tests/HeaderTest.php new file mode 100644 index 00000000..0765112c --- /dev/null +++ b/tests/HeaderTest.php @@ -0,0 +1,58 @@ +singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', CustomSCIMConfigSchema::class); + } + + public function testGet() + { + $response = $this->get('/scim/v2/Users/1'); + $response->assertStatus(200); + $response->assertHeader('ETag'); + $response->assertJsonStructure( + [ + 'meta' => [ + 'created', + 'lastModified', + 'location', + 'resourceType', + 'version' + ], + ] + ); + // get the value of version from the response content + $version = $response->baseResponse->original['meta']['version']; + $this->assertStringStartsWith('W/', $version); + $etag = $response->baseResponse->headers->get('ETag'); + $this->assertStringStartsWith('W/', $etag); + $this->assertEquals($etag, $version); + } + + + public function testPut(){ + $response = $this->put('/scim/v2/Users/1', [ + 'userName' => 'newUserName' + ], ['IF-MATCH' => 'W/"1"']); + $response->assertStatus(412); + + // first retrieve the version by sending a get request + $response = $this->get('/scim/v2/Users/1'); + $response->assertStatus(200); + $etag = $response->baseResponse->headers->get('ETag'); + + // now update it + $response = $this->put('/scim/v2/Users/1', [ + 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User'], + 'userName' => 'newUserName', + 'id' => '1', + ], ['If-Match' => $etag]); + $response->assertStatus(200); + } +} From 725b94d7d75c12ae17355390a1abfd05f32cb0fb Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 25 Jul 2024 07:23:53 +0200 Subject: [PATCH 23/58] Removed attribute name for groups (#77) --- database/factories/GroupFactory.php | 2 +- src/SCIMConfig.php | 3 +-- tests/GroupsTest.php | 8 ++------ tests/TestCase.php | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/database/factories/GroupFactory.php b/database/factories/GroupFactory.php index f21c11f0..295d49b8 100644 --- a/database/factories/GroupFactory.php +++ b/database/factories/GroupFactory.php @@ -6,6 +6,6 @@ $factory->define(Group::class, function (Generator $faker) { return [ // 'username' => $faker->userName, - 'name' => $faker->company + 'displayName' => $faker->company ]; }); diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index ee9d1460..cf146318 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -157,14 +157,13 @@ protected function doRead(&$object, $attributes = []) ), new Meta('Groups'), (new AttributeSchema(Schema::SCHEMA_GROUP, true))->withSubAttributes( - eloquent('name')->ensure('required', 'min:3', function ($attribute, $value, $fail) { + eloquent('displayName')->ensure('required', 'min:3', function ($attribute, $value, $fail) { // check if group does not exist or if it exists, it is the same group $group = Group::where('name', $value)->first(); if ($group && (request()->route('resourceObject') == null || $group->id != request()->route('resourceObject')->id)) { $fail('The name has already been taken.'); } }), - eloquent('displayName')->ensure('nullable'), (new MutableCollection('members'))->withSubAttributes( eloquent('value', 'id')->ensure('required'), (new class ('$ref') extends Eloquent { diff --git a/tests/GroupsTest.php b/tests/GroupsTest.php index c85d1bf6..48a3ae5b 100644 --- a/tests/GroupsTest.php +++ b/tests/GroupsTest.php @@ -52,7 +52,6 @@ public function testCreate(){ $response = $this->post('/scim/v2/Groups', [ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ - 'name' => 'testgroup1', 'displayName' => 'TestGroup' ] ]); @@ -60,13 +59,12 @@ public function testCreate(){ $response->assertJsonStructure([ 'id', 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ - 'name', 'displayName' ] ]); $this->assertNotNull(Group::find($response->json('id'))); - $this->assertNotNull(Group::where('name', 'testgroup1')->first()); + $this->assertNotNull(Group::where('displayName', 'TestGroup')->first()); } @@ -80,7 +78,6 @@ public function testBulk(){ 'data' => [ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ - 'name' => 'testgroup1', 'displayName' => 'TestGroup' ] ] @@ -91,7 +88,6 @@ public function testBulk(){ 'data' => [ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ - 'name' => 'testgroup2', 'displayName' => 'TestGroup2' ] ] @@ -111,6 +107,6 @@ public function testBulk(){ ]); // confirm testgroup1 exists - $this->assertNotNull(Group::where('name', 'testgroup2')->first()); + $this->assertNotNull(Group::where('displayName', 'TestGroup2')->first()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 1cd3b281..a78c101a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -26,7 +26,6 @@ protected function setUp(): void $table->increments('id'); // timestamp columns $table->timestamps(); - $table->string('name')->nullable(); $table->string('displayName')->nullable(); }); From 4f96ed55cfb1bac5c64f2553805db5af9337bc9c Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 25 Jul 2024 21:09:40 +0200 Subject: [PATCH 24/58] Support for cursor pagination (#78) As defined in https://datatracker.ietf.org/doc/draft-ietf-scim-cursor-pagination/ --- .github/workflows/ci.yml | 14 +- composer.json | 8 +- composer.lock | 250 +++++++++--------- database/factories/UserFactory.php | 2 +- src/Http/Controllers/ResourceController.php | 48 +++- .../Controllers/ServiceProviderController.php | 10 +- src/SCIM/ListResponse.php | 40 +-- tests/BasicTest.php | 34 +++ 8 files changed, 235 insertions(+), 171 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2405a7e5..9131ebaf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,20 +9,10 @@ jobs: strategy: matrix: php: [8.0, 8.1, 8.2] - laravel: [7.*, 8.*, 9.*, 10.*, 11.*] + laravel: [8.*, 9.*, 10.*, 11.*] exclude: - - laravel: 7.* - php: 8.1 - - laravel: 7.* + - laravel: 8.* php: 8.2 - - laravel: 9.* - php: 7.3 - - laravel: 9.* - php: 7.4 - - laravel: 10.* - php: 7.3 - - laravel: 10.* - php: 7.4 - laravel: 10.* php: 8.0 - laravel: 11.* diff --git a/composer.json b/composer.json index 6f387e0e..87e6a26b 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^8.0", - "illuminate/database": "^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/console": "^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/database": "^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^8.0|^9.0|^10.0|^11.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, @@ -29,7 +29,7 @@ } }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", "laravel/legacy-factories": "*" }, diff --git a/composer.lock b/composer.lock index 6380c693..82675ac0 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": "35d7d3286fcc782631ce7311b28f4707", + "content-hash": "d828374ef6a0ef831c83368a046325f2", "packages": [ { "name": "brick/math", @@ -137,16 +137,16 @@ }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -206,9 +206,9 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "doctrine/inflector", @@ -579,24 +579,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.2", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2" + "phpoption/phpoption": "^1.9.3" }, "require-dev": { - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "autoload": { @@ -625,7 +625,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" }, "funding": [ { @@ -637,7 +637,7 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:16:48+00:00" + "time": "2024-07-20T21:45:45+00:00" }, { "name": "guzzlehttp/uri-template", @@ -727,16 +727,16 @@ }, { "name": "laravel/framework", - "version": "v10.48.15", + "version": "v10.48.17", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "1723e038c9ec99b434e5bed9c27f044bd50a4ffb" + "reference": "60f3c8f667b24a09e0392e26b1f40fb9067cdc3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/1723e038c9ec99b434e5bed9c27f044bd50a4ffb", - "reference": "1723e038c9ec99b434e5bed9c27f044bd50a4ffb", + "url": "https://api.github.com/repos/laravel/framework/zipball/60f3c8f667b24a09e0392e26b1f40fb9067cdc3c", + "reference": "60f3c8f667b24a09e0392e26b1f40fb9067cdc3c", "shasum": "" }, "require": { @@ -930,7 +930,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-07-02T18:01:24+00:00" + "time": "2024-07-23T16:06:06+00:00" }, { "name": "laravel/prompts", @@ -1052,16 +1052,16 @@ }, { "name": "league/commonmark", - "version": "2.4.2", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c", + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c", "shasum": "" }, "require": { @@ -1074,8 +1074,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.3", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.0", + "commonmark/commonmark.js": "0.31.0", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -1097,7 +1097,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.6-dev" } }, "autoload": { @@ -1154,7 +1154,7 @@ "type": "tidelift" } ], - "time": "2024-02-02T11:59:32+00:00" + "time": "2024-07-24T12:52:09+00:00" }, { "name": "league/config", @@ -1870,16 +1870,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", "shasum": "" }, "require": { @@ -1887,13 +1887,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -1929,7 +1929,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" }, "funding": [ { @@ -1941,7 +1941,7 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2024-07-20T21:41:07+00:00" }, { "name": "psr/clock", @@ -4856,23 +4856,23 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.2", + "graham-campbell/result-type": "^1.1.3", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2", + "phpoption/phpoption": "^1.9.3", "symfony/polyfill-ctype": "^1.24", "symfony/polyfill-mbstring": "^1.24", "symfony/polyfill-php80": "^1.24" @@ -4889,7 +4889,7 @@ "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "5.6-dev" @@ -4924,7 +4924,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" }, "funding": [ { @@ -4936,7 +4936,7 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:43:29+00:00" + "time": "2024-07-20T21:52:34+00:00" }, { "name": "voku/portable-ascii", @@ -5074,16 +5074,16 @@ "packages-dev": [ { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", "shasum": "" }, "require": { @@ -5135,7 +5135,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.2" }, "funding": [ { @@ -5151,7 +5151,7 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-07-12T11:35:52+00:00" }, { "name": "fakerphp/faker", @@ -5289,16 +5289,16 @@ }, { "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": { @@ -5313,8 +5313,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" @@ -5385,7 +5385,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": [ { @@ -5401,7 +5401,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -6022,16 +6022,16 @@ }, { "name": "orchestra/testbench", - "version": "v8.23.2", + "version": "v8.24.0", "source": { "type": "git", "url": "https://github.com/orchestral/testbench.git", - "reference": "c9f89b66aaa245a2e36f046aa431587ba46a3f2e" + "reference": "2e5ca3ac1e8170a787532c4fc19403f91e9dd7d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench/zipball/c9f89b66aaa245a2e36f046aa431587ba46a3f2e", - "reference": "c9f89b66aaa245a2e36f046aa431587ba46a3f2e", + "url": "https://api.github.com/repos/orchestral/testbench/zipball/2e5ca3ac1e8170a787532c4fc19403f91e9dd7d4", + "reference": "2e5ca3ac1e8170a787532c4fc19403f91e9dd7d4", "shasum": "" }, "require": { @@ -6039,7 +6039,7 @@ "fakerphp/faker": "^1.21", "laravel/framework": "^10.48.10", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.24.3", + "orchestra/testbench-core": "^8.25", "orchestra/workbench": "^1.4.1 || ^8.5", "php": "^8.1", "phpunit/phpunit": "^9.6 || ^10.1", @@ -6071,22 +6071,22 @@ ], "support": { "issues": "https://github.com/orchestral/testbench/issues", - "source": "https://github.com/orchestral/testbench/tree/v8.23.2" + "source": "https://github.com/orchestral/testbench/tree/v8.24.0" }, - "time": "2024-06-04T12:24:55+00:00" + "time": "2024-07-13T07:05:48+00:00" }, { "name": "orchestra/testbench-core", - "version": "v8.24.4", + "version": "v8.25.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "3acbb96840e2bd4acbe670ee95e9bd55f517964c" + "reference": "df0a606dd557a1e350914be64632cd9040fa4bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/3acbb96840e2bd4acbe670ee95e9bd55f517964c", - "reference": "3acbb96840e2bd4acbe670ee95e9bd55f517964c", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/df0a606dd557a1e350914be64632cd9040fa4bc0", + "reference": "df0a606dd557a1e350914be64632cd9040fa4bc0", "shasum": "" }, "require": { @@ -6165,7 +6165,7 @@ "issues": "https://github.com/orchestral/testbench/issues", "source": "https://github.com/orchestral/testbench-core" }, - "time": "2024-06-26T12:14:16+00:00" + "time": "2024-07-19T10:25:12+00:00" }, { "name": "orchestra/workbench", @@ -6413,16 +6413,16 @@ }, { "name": "php-di/php-di", - "version": "7.0.6", + "version": "7.0.7", "source": { "type": "git", "url": "https://github.com/PHP-DI/PHP-DI.git", - "reference": "8097948a89f6ec782839b3e958432f427cac37fd" + "reference": "e87435e3c0e8f22977adc5af0d5cdcc467e15cf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/8097948a89f6ec782839b3e958432f427cac37fd", - "reference": "8097948a89f6ec782839b3e958432f427cac37fd", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/e87435e3c0e8f22977adc5af0d5cdcc467e15cf1", + "reference": "e87435e3c0e8f22977adc5af0d5cdcc467e15cf1", "shasum": "" }, "require": { @@ -6470,7 +6470,7 @@ ], "support": { "issues": "https://github.com/PHP-DI/PHP-DI/issues", - "source": "https://github.com/PHP-DI/PHP-DI/tree/7.0.6" + "source": "https://github.com/PHP-DI/PHP-DI/tree/7.0.7" }, "funding": [ { @@ -6482,20 +6482,20 @@ "type": "tidelift" } ], - "time": "2023-11-02T10:04:50+00:00" + "time": "2024-07-21T15:55:45+00:00" }, { "name": "phpstan/phpstan", - "version": "1.11.6", + "version": "1.11.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee" + "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6ac78f1165346c83b4a753f7e4186d969c6ad0ee", - "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec", + "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec", "shasum": "" }, "require": { @@ -6540,7 +6540,7 @@ "type": "github" } ], - "time": "2024-07-01T15:33:06+00:00" + "time": "2024-07-24T07:01:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6865,16 +6865,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.25", + "version": "10.5.28", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "831bf82312be6037e811833ddbea0b8de60ea314" + "reference": "ff7fb85cdf88131b83e721fb2a327b664dbed275" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/831bf82312be6037e811833ddbea0b8de60ea314", - "reference": "831bf82312be6037e811833ddbea0b8de60ea314", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ff7fb85cdf88131b83e721fb2a327b664dbed275", + "reference": "ff7fb85cdf88131b83e721fb2a327b664dbed275", "shasum": "" }, "require": { @@ -6884,26 +6884,26 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.1", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -6946,7 +6946,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.25" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.28" }, "funding": [ { @@ -6962,7 +6962,7 @@ "type": "tidelift" } ], - "time": "2024-07-03T05:49:17+00:00" + "time": "2024-07-18T14:54:16+00:00" }, { "name": "psr/http-factory", @@ -7197,16 +7197,16 @@ }, { "name": "rector/rector", - "version": "1.2.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65" + "reference": "044e6364017882d1e346da8690eeabc154da5495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/2fa387553db22b6f9bcccf5ff16f2c2c18a52a65", - "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/044e6364017882d1e346da8690eeabc154da5495", + "reference": "044e6364017882d1e346da8690eeabc154da5495", "shasum": "" }, "require": { @@ -7244,7 +7244,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.0" + "source": "https://github.com/rectorphp/rector/tree/1.2.2" }, "funding": [ { @@ -7252,7 +7252,7 @@ "type": "github" } ], - "time": "2024-07-01T14:24:45+00:00" + "time": "2024-07-25T07:44:34+00:00" }, { "name": "sebastian/cli-parser", @@ -8172,16 +8172,16 @@ }, { "name": "spatie/backtrace", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23" + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9", + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9", "shasum": "" }, "require": { @@ -8219,7 +8219,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.6.1" + "source": "https://github.com/spatie/backtrace/tree/1.6.2" }, "funding": [ { @@ -8231,20 +8231,20 @@ "type": "other" } ], - "time": "2024-04-24T13:22:11+00:00" + "time": "2024-07-22T08:21:24+00:00" }, { "name": "spatie/laravel-ray", - "version": "1.37.0", + "version": "1.37.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ray.git", - "reference": "f57b294a3815be37effa9d13f54f2fbe5a2fff37" + "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/f57b294a3815be37effa9d13f54f2fbe5a2fff37", - "reference": "f57b294a3815be37effa9d13f54f2fbe5a2fff37", + "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/c2bedfd1172648df2c80aaceb2541d70f1d9a5b9", + "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9", "shasum": "" }, "require": { @@ -8306,7 +8306,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-ray/issues", - "source": "https://github.com/spatie/laravel-ray/tree/1.37.0" + "source": "https://github.com/spatie/laravel-ray/tree/1.37.1" }, "funding": [ { @@ -8318,7 +8318,7 @@ "type": "other" } ], - "time": "2024-07-03T08:48:44+00:00" + "time": "2024-07-12T12:35:17+00:00" }, { "name": "spatie/macroable", diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index c32cef10..19244230 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -5,7 +5,7 @@ $factory->define(ArieTimmerman\Laravel\SCIMServer\Tests\Model\User::class, function (Generator $faker) { return [ // 'username' => $faker->userName, - 'email' => $faker->email, + 'email' => $faker->unique()->email, 'name' => $faker->name, 'password'=>'test' ]; diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 60aab806..f985f363 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -15,6 +15,7 @@ use ArieTimmerman\Laravel\SCIMServer\Events\Patch; use ArieTimmerman\Laravel\SCIMServer\Parser\Parser as ParserParser; use ArieTimmerman\Laravel\SCIMServer\PolicyDecisionPoint; +use Illuminate\Contracts\Pagination\CursorPaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Validator; @@ -121,7 +122,7 @@ public function delete(Request $request, PolicyDecisionPoint $pdp, ResourceType public function replace(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType, Model $resourceObject, $isMe = false) { $originalRaw = Helper::objectToSCIMArray($resourceObject, $resourceType); - + $resourceType->getMapping()->replace($request->input(), $resourceObject, null, true); $newObject = Helper::flatten(Helper::objectToSCIMArray($resourceObject, $resourceType), $resourceType->getSchema()); @@ -210,12 +211,15 @@ public function index(Request $request, PolicyDecisionPoint $pdp, ResourceType $ { $query = $resourceType->getQuery(); - // The 1-based index of the first query result. A value less than 1 SHALL be interpreted as 1. - $startIndex = max(1, intVal($request->input('startIndex', 0))); + // if both cursor and startIndex are present, throw an exception + if ($request->has('cursor') && $request->has('startIndex')) { + throw (new SCIMException('Both cursor and startIndex are present. Only one of them is allowed.'))->setCode(400); + } // Non-negative integer. Specifies the desired maximum number of query results per page, e.g., 10. A negative value SHALL be interpreted as "0". A value of "0" indicates that no resource results are to be returned except for "totalResults". - $count = max(0, intVal($request->input('count', 10))); + $count = min(max(0, intVal($request->input('count', 10))), 100); + $startIndex = null; $sortBy = null; if ($request->input('sortBy')) { @@ -240,8 +244,6 @@ function (Builder $query) use ($filter, $resourceType) { * @var \Illuminate\Database\Query\Builder $resourceObjects */ $resourceObjects = $resourceObjectsBase - ->skip($startIndex - 1) - ->take($count) ->with($resourceType->getWithRelations()); if ($sortBy != null) { @@ -250,12 +252,31 @@ function (Builder $query) use ($filter, $resourceType) { $resourceObjects = $resourceObjects->orderBy($sortBy, $direction); } - $resourceObjects = $resourceObjects->get(); + $resources = null; + if ($request->has('cursor')) { + if($sortBy == null){ + $resourceObjects = $resourceObjects->orderBy('id'); + } + + $resourceObjects = $resourceObjects->cursorPaginate( + $count, + cursor: $request->input('cursor') + ); + $resources = collect($resourceObjects->items()); + + + } else { + // The 1-based index of the first query result. A value less than 1 SHALL be interpreted as 1. + $startIndex = max(1, intVal($request->input('startIndex', 0))); + + $resourceObjects = $resourceObjects->skip($startIndex - 1)->take($count); + $resources = $resourceObjects->get(); + } // TODO: splitting the attributes parameters by dot and comma is not correct, but works in most cases $attributes = $request->input('attributes') ? preg_split('/[,.]/', $request->input('attributes')) : []; - if(!empty($attributes)){ + if (!empty($attributes)) { $attributes[] = 'id'; $attributes[] = 'meta'; $attributes[] = 'schemas'; @@ -263,6 +284,15 @@ function (Builder $query) use ($filter, $resourceType) { $excludedAttributes = []; - return new ListResponse($resourceObjects, $startIndex, $totalResults, $attributes, $excludedAttributes, $resourceType); + return new ListResponse( + $resources, + $startIndex, + $totalResults, + $attributes, + $excludedAttributes, + $resourceType, + ($resourceObjects instanceof CursorPaginator) ? $resourceObjects->nextCursor()?->encode() : null, + ($resourceObjects instanceof CursorPaginator) ? $resourceObjects->previousCursor()?->encode() : null + ); } } diff --git a/src/Http/Controllers/ServiceProviderController.php b/src/Http/Controllers/ServiceProviderController.php index d97d8b3c..1bf7d051 100644 --- a/src/Http/Controllers/ServiceProviderController.php +++ b/src/Http/Controllers/ServiceProviderController.php @@ -50,10 +50,18 @@ public function index() "type" => "httpbasic", ], ], + "pagination" => [ + "cursor" => true, + "index" => true, + "defaultPaginationMethod" => "index", + "defaultPageSize" => 10, + "maxPageSize" => 100, + "cursorTimeout" => 3600 + ], "meta" => [ "location" => route('scim.serviceproviderconfig'), "resourceType" => "ServiceProviderConfig", - + "created" => Carbon::createFromTimestampUTC(filectime(__FILE__))->format('c'), "lastModified" => Carbon::createFromTimestampUTC(filemtime(__FILE__))->format('c'), "version" => sprintf('W/"%s"', sha1(filemtime(__FILE__))), diff --git a/src/SCIM/ListResponse.php b/src/SCIM/ListResponse.php index 0c21f0df..278039f6 100644 --- a/src/SCIM/ListResponse.php +++ b/src/SCIM/ListResponse.php @@ -5,24 +5,21 @@ use Illuminate\Contracts\Support\Jsonable; use ArieTimmerman\Laravel\SCIMServer\Helper; use ArieTimmerman\Laravel\SCIMServer\ResourceType; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Support\Collection; class ListResponse implements Jsonable { - private $resourceObjects = []; - private $startIndex; - private $totalResults; - private $attributes; - private $excludedAttributes; - private $resourceType = null; - - public function __construct($resourceObjects, $startIndex = 1, $totalResults = 10, array $attributes = [], $excludedAttributes = [], ResourceType $resourceType = null) - { - $this->resourceType = $resourceType; - $this->resourceObjects = $resourceObjects; - $this->startIndex = $startIndex; - $this->totalResults = $totalResults; - $this->attributes = $attributes; - $this->excludedAttributes = $excludedAttributes; + public function __construct( + protected Collection $resourceObjects, + protected $startIndex = 1, + protected $totalResults = 10, + protected array $attributes = [], + protected $excludedAttributes = [], + protected ?ResourceType $resourceType = null, + protected $nextCursor = null, + protected $previousCursor = null + ) { } public function toJson($options = 0) @@ -32,14 +29,19 @@ public function toJson($options = 0) public function toSCIMArray() { - return [ + return array_filter([ 'totalResults' => $this->totalResults, - "itemsPerPage" => count($this->resourceObjects), + "itemsPerPage" => count($this->resourceObjects->toArray()), + "startIndex" => $this->startIndex, + + "nextCursor" => $this->nextCursor, + "previousCursor" => $this->previousCursor, + "schemas" => [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType, $this->attributes), - ]; - } + ]); + } } diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 59c928ff..fd611324 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -58,6 +58,37 @@ public function testGetAttributes() ]); } + public function testCursorPagination() + { + $response1 = $this->get('/scim/v2/Users?count=60&cursor'); + + $response1->assertStatus(200); + $response1->assertJsonCount(60, 'Resources'); + $response1->assertJson([ + 'totalResults' => 100, + 'itemsPerPage' => 60 + ]); + + $response2 = $this->get('/scim/v2/Users?count=60&cursor=' . $response1->json('nextCursor')); + $response2->assertStatus(200); + $response2->assertJsonCount(40, 'Resources'); + $response2->assertJson([ + 'totalResults' => 100, + 'itemsPerPage' => 40 + ]); + + // assert the list of items in response 1 and 2 are different, compare the id of the user items + $this->assertNotEquals( + collect($response1->json('Resources'))->pluck('id')->toArray(), + collect($response2->json('Resources'))->pluck('id')->toArray() + ); + + // assert the nextCursor is missing in response2 + $this->assertNull($response2->json('nextCursor')); + $this->assertNotNull($response2->json('previousCursor')); + $this->assertNull($response2->json('startIndex')); + } + public function testPagination() { $response = $this->get('/scim/v2/Users?startIndex=21&count=20'); @@ -69,6 +100,9 @@ public function testPagination() 'itemsPerPage' => 20, 'startIndex' => 21 ]); + + $this->assertNull($response->json('nextCursor')); + $this->assertNull($response->json('previousCursor')); } public function testSort() From 084b3b0ab8d3ae0e25c7059d63440d8fc1264812 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Fri, 16 Aug 2024 23:16:02 +0200 Subject: [PATCH 25/58] Allow omitting null values (and enable by default) (#79) --- config/scim.php | 3 ++- src/Attribute/Complex.php | 3 +++ tests/GroupsTest.php | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config/scim.php b/config/scim.php index dca39d34..3956946b 100644 --- a/config/scim.php +++ b/config/scim.php @@ -2,5 +2,6 @@ return [ "publish_routes" => true, - 'omit_main_schema_in_return' => false + 'omit_main_schema_in_return' => false, + 'omit_null_values' => true, ]; diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index 2f4b98ff..c3d58842 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -34,6 +34,9 @@ protected function doRead(&$object, $attributes = []) $result = []; foreach ($this->subAttributes as $attribute) { if(($r = $attribute->read($object, $attributes)) != null){ + if(config('scim.omit_null_values') && $r->value == null){ + continue; + } $result[$attribute->name] = $r->value; } } diff --git a/tests/GroupsTest.php b/tests/GroupsTest.php index 48a3ae5b..794304ee 100644 --- a/tests/GroupsTest.php +++ b/tests/GroupsTest.php @@ -24,6 +24,7 @@ protected function getEnvironmentSetUp($app) public function testGet() { + config(['scim.omit_null_values' => false]); $response = $this->get('/scim/v2/Groups'); $response->assertStatus(200); From ca08d4332c95adc62638e140ddb1885cb6d3bfef Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 19 Aug 2024 15:00:39 +0200 Subject: [PATCH 26/58] get default schema fix (#80) --- src/Attribute/Attribute.php | 6 ------ src/Attribute/Complex.php | 10 ++++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index 58504d01..cef5d923 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -161,12 +161,6 @@ public function getFullKey() } } - public function getDefaultSchema() - { - // TODO: why this? - return Schema::SCHEMA_USER; - } - public function getSchema() { if ($this instanceof AttributeSchema) { diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index c3d58842..4649ad7b 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -225,4 +225,14 @@ public function applyComparison(Builder &$query, Path $path, $parentAttribute = } } + + /** + * Return the default (core) schema. Assume it is the first one. + * TODO: This method is only relevant for the top-level complex attribute. + * @return string + */ + public function getDefaultSchema() + { + return collect($this->subAttributes)->first(fn ($element) => $element instanceof Schema)->name; + } } From 55a32914f9772d8c38f08496f077210bf2a3499d Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 19 Aug 2024 20:11:26 +0200 Subject: [PATCH 27/58] fix no schema attributes in return (#81) --- src/Helper.php | 4 +- tests/BasicTest.php | 16 ++++++ tests/ConfigTest.php | 115 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 tests/ConfigTest.php diff --git a/src/Helper.php b/src/Helper.php index 68df2f63..d97ac7cd 100644 --- a/src/Helper.php +++ b/src/Helper.php @@ -59,7 +59,9 @@ public static function objectToSCIMArray($object, ResourceType $resourceType = n if (config('scim.omit_main_schema_in_return')) { $defaultSchema = collect($mapping->getDefaultSchema())->first(); - $main = $result[$defaultSchema]; + // Move main schema to the top. It may not be defined, for example when only specific attributes are requested. + $main = $result[$defaultSchema] ?? []; + unset($result[$defaultSchema]); $result = array_merge($result, $main); diff --git a/tests/BasicTest.php b/tests/BasicTest.php index fd611324..cad8c619 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -58,6 +58,22 @@ public function testGetAttributes() ]); } + public function testGetGroupsAttribute() + { + config(['scim.omit_main_schema_in_return' => true]); + $response = $this->get('/scim/v2/Users?attributes=groups'); + + $response->assertStatus(200); + $response->assertJsonCount(10, 'Resources'); + $response->assertJsonStructure([ + 'Resources' => [ + '*' => [ + + ] + ] + ]); + } + public function testCursorPagination() { $response1 = $this->get('/scim/v2/Users?count=60&cursor'); diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php new file mode 100644 index 00000000..b44cc7bc --- /dev/null +++ b/tests/ConfigTest.php @@ -0,0 +1,115 @@ +post('/scim/v2/Users', [ + // "id" => 1, + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:User", + ], + "urn:ietf:params:scim:schemas:core:2.0:User" => [ + "userName" => "Dr. Marie Jo", + "password" => "Password123", + "emails" => [ + [ + "value" => "mariejo@example.com", + "type" => "primary", + "primary" => true + ] + ] + ] + ]); + } + + public function testOmitMainSchema() + { + config(['scim.omit_main_schema_in_return' => true]); + // create user with post + $response = $this->createUser(); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $this->assertArrayNotHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $response->json()); + } + + public function testDoNotOmitMainSchema() + { + config(['scim.omit_main_schema_in_return' => false]); + // create user with post + $response = $this->createUser(); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $response->json()); + } + + public function testDoNotOmitNullValues() + { + config(['scim.omit_null_values' => false]); + // create user with post + $response = $this->createUser(); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $response->json()); + + $expected = [ + "employeeNumber" => null + + ]; + + $this->assertEquals($expected, Arr::get($response->json(), 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User')); + } + + public function testOmitNullValues() + { + config(['scim.omit_null_values' => true]); + // create user with post + $response = $this->createUser(); + + $this->assertEquals( + 201, + $response->baseResponse->getStatusCode(), + 'Wrong status: ' . $response->baseResponse->content() + ); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $response->json()); + + // null because of scim.omit_null_values set to true + $expected = null; + $this->assertEquals($expected, Arr::get($response->json(), 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User')); + } + +} From b813b5e50960f4a8fdbb66095c2430e9fcfe5d14 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 22 Aug 2024 16:42:53 +0200 Subject: [PATCH 28/58] improved null check (#82) --- src/Attribute/Complex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index 4649ad7b..58739fbf 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -34,7 +34,7 @@ protected function doRead(&$object, $attributes = []) $result = []; foreach ($this->subAttributes as $attribute) { if(($r = $attribute->read($object, $attributes)) != null){ - if(config('scim.omit_null_values') && $r->value == null){ + if(config('scim.omit_null_values') && $r->value === null){ continue; } $result[$attribute->name] = $r->value; From b111b9a91fe29aa401a8f8900c6e32139819a7ee Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 22 Aug 2024 16:49:03 +0200 Subject: [PATCH 29/58] Allow defaults (#83) --- src/Attribute/Attribute.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index cef5d923..dfd0279f 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -38,6 +38,7 @@ class Attribute protected $mutability = 'readWrite'; protected $type = 'string'; protected $description = null; + protected $defaultValue = null; public $dirty = false; @@ -101,6 +102,12 @@ public function ensure(...$validations) return $this; } + public function default($value){ + $this->defaultValue = $value; + + return $this; + } + public function isRequired() { return in_array('required', $this->validations); @@ -143,7 +150,7 @@ public function read(&$object, array $attributes = []): ?AttributeValue return null; } - return new AttributeValue($this->doRead($object, $attributes)); + return new AttributeValue($this->doRead($object, $attributes) ?? $this->defaultValue); } protected function doRead(&$object, $attributes = []) From 95eae91698a74ef8608c773a45fe5fc0a9a615f4 Mon Sep 17 00:00:00 2001 From: Sebastiaan Kloos Date: Mon, 26 Aug 2024 22:04:59 +0200 Subject: [PATCH 30/58] Update Parser.php (#84) --- src/Parser/Parser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index c8f4bab4..6f517b76 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -6,7 +6,7 @@ use Tmilos\ScimFilterParser\Parser as ScimFilterParserParser; class Parser { - public static function parse($input): Path { + public static function parse($input): ?Path { if($input == null){ return null; } @@ -19,7 +19,7 @@ public static function parse($input): Path { ); } - public static function parseFilter($input): Path { + public static function parseFilter($input): ?Path { if($input == null){ return null; } From 51ebce21ea54060693dac1519c36cd28e92b2692 Mon Sep 17 00:00:00 2001 From: Sebastiaan Kloos Date: Mon, 26 Aug 2024 22:07:21 +0200 Subject: [PATCH 31/58] Update ListResponse.php (#85) --- src/SCIM/ListResponse.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SCIM/ListResponse.php b/src/SCIM/ListResponse.php index 278039f6..e54efe9f 100644 --- a/src/SCIM/ListResponse.php +++ b/src/SCIM/ListResponse.php @@ -29,7 +29,7 @@ public function toJson($options = 0) public function toSCIMArray() { - return array_filter([ + return [ 'totalResults' => $this->totalResults, "itemsPerPage" => count($this->resourceObjects->toArray()), @@ -42,6 +42,6 @@ public function toSCIMArray() "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType, $this->attributes), - ]); - } + ]; + } } From 23a2eed93a69d2d48fc5597088183941444b0002 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Tue, 27 Aug 2024 10:03:15 +0200 Subject: [PATCH 32/58] filter out null values (#86) --- src/SCIM/ListResponse.php | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/SCIM/ListResponse.php b/src/SCIM/ListResponse.php index e54efe9f..c17035b0 100644 --- a/src/SCIM/ListResponse.php +++ b/src/SCIM/ListResponse.php @@ -29,19 +29,22 @@ public function toJson($options = 0) public function toSCIMArray() { - return [ - 'totalResults' => $this->totalResults, - "itemsPerPage" => count($this->resourceObjects->toArray()), - - "startIndex" => $this->startIndex, - - "nextCursor" => $this->nextCursor, - "previousCursor" => $this->previousCursor, - - "schemas" => [ - "urn:ietf:params:scim:api:messages:2.0:ListResponse" - ], - 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType, $this->attributes), - ]; + return array_filter( + [ + 'totalResults' => $this->totalResults, + "itemsPerPage" => count($this->resourceObjects->toArray()), + + "startIndex" => $this->startIndex, + + "nextCursor" => $this->nextCursor, + "previousCursor" => $this->previousCursor, + + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:ListResponse" + ], + 'Resources' => Helper::prepareReturn($this->resourceObjects, $this->resourceType, $this->attributes), + ], + fn ($value) => $value !== null + ); } } From 9fefb3f5e0a6c1ab1a6423a825c128d03dac3784 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 5 Sep 2024 17:13:57 +0200 Subject: [PATCH 33/58] Support for add/replace of multiple attributes (#89) --- database/factories/UserFactory.php | 4 +- src/Attribute/Attribute.php | 2 +- src/Attribute/Complex.php | 98 +++++++++++++++++++++++++----- src/Parser/Path.php | 2 +- src/SCIMConfig.php | 3 +- tests/BasicTest.php | 54 +++++++++++++++- tests/CustomSchemaTest.php | 1 - tests/Model/User.php | 5 ++ tests/TestCase.php | 5 ++ 9 files changed, 153 insertions(+), 21 deletions(-) diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 19244230..641ef872 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -6,7 +6,9 @@ return [ // 'username' => $faker->userName, 'email' => $faker->unique()->email, + 'formatted' => $faker->name, 'name' => $faker->name, - 'password'=>'test' + 'password'=>'test', + 'active' => false ]; }); diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index dfd0279f..483210aa 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -277,7 +277,7 @@ public function applyComparison(Builder &$query, Path $path, $parentAttribute = public function add($value, Model &$object) { - new SCIMException(sprintf('Write is not implemented for "%s"', $this->getFullKey())); + throw new SCIMException(sprintf('Write is not implemented for "%s"', $this->getFullKey())); } public function replace($value, Model &$object, Path $path = null) diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index 58739fbf..a358e694 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -3,13 +3,14 @@ namespace ArieTimmerman\Laravel\SCIMServer\Attribute; use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; +use ArieTimmerman\Laravel\SCIMServer\Parser\Parser; use ArieTimmerman\Laravel\SCIMServer\Parser\Path; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Complex extends AbstractComplex { - + /** * @return string[] */ @@ -18,9 +19,9 @@ public function getSchemas() return collect($this->getSchemaNodes())->map(fn ($element) => $element->name)->values()->toArray(); } - + public function read(&$object, array $attributes = []): ?AttributeValue - { + { if (!($this instanceof Schema) && $this->parent != null && !empty($attributes) && !in_array($this->name, $attributes) && !in_array($this->getFullKey(), $attributes)) { return null; } @@ -33,8 +34,8 @@ protected function doRead(&$object, $attributes = []) { $result = []; foreach ($this->subAttributes as $attribute) { - if(($r = $attribute->read($object, $attributes)) != null){ - if(config('scim.omit_null_values') && $r->value === null){ + if (($r = $attribute->read($object, $attributes)) != null) { + if (config('scim.omit_null_values') && $r->value === null) { continue; } $result[$attribute->name] = $r->value; @@ -47,7 +48,7 @@ public function patch($operation, $value, Model &$object, Path $path = null, $re { $this->dirty = true; - if($this->mutability == 'readOnly'){ + if ($this->mutability == 'readOnly') { // silently ignore return; } @@ -112,21 +113,44 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS $match = false; $this->dirty = true; - if($this->mutability == 'readOnly'){ + if ($this->mutability == 'readOnly') { // silently ignore return; } - // if there is no path, keys of value are attribute names + // if there is no path, keys of value are attribute paths foreach ($value as $key => $v) { if (is_numeric($key)) { throw new SCIMException('Invalid key: ' . $key . ' for complex object ' . $this->getFullKey()); } - $attribute = $this->getSubNode($key); - if ($attribute != null) { - $attribute->replace($v, $object, $path); + $subNode = null; + + // if path contains : it is a schema node + if (strpos($key, ':') !== false) { + $subNode = $this->getSubNode($key); $match = true; + } else { + $path = Parser::parse($key); + + if ($path->isNotEmpty()) { + $attributeNames = $path->getAttributePathAttributes(); + $path = $path->shiftAttributePathAttributes(); + $sub = $attributeNames[0] ?? $path->getAttributePath()?->path?->schema; + $subNode = $this->getSubNode($attributeNames[0] ?? $path->getAttributePath()?->path?->schema); + $match = true; + } + } + + if ($match) { + $newValue = $v; + if ($path->isNotEmpty()) { + $newValue = [ + implode('.', $path->getAttributePathAttributes()) => $v + ]; + } + + $subNode->replace($newValue, $object, $path); } } @@ -148,10 +172,55 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS } } + public function add($value, Model &$object) + { + $match = false; + $this->dirty = true; + + if ($this->mutability == 'readOnly') { + // silently ignore + return; + } + + // keys of value are attribute names + foreach ($value as $key => $v) { + if (is_numeric($key)) { + throw new SCIMException('Invalid key: ' . $key . ' for complex object ' . $this->getFullKey()); + } + + $path = Parser::parse($key); + + if ($path->isNotEmpty()) { + $attributeNames = $path->getAttributePathAttributes(); + $path = $path->shiftAttributePathAttributes(); + $subNode = $this->getSubNode($attributeNames[0]); + $match = true; + + $newValue = $v; + if ($path->isNotEmpty()) { + $newValue = [ + implode('.', $path->getAttributePathAttributes()) => $v + ]; + } + + $subNode->add($newValue, $object); + } + } + + // if this is the root, we may also check the schema nodes + if (!$match && $this->parent == null) { + foreach ($this->subAttributes as $attribute) { + if ($attribute instanceof Schema) { + $attribute->add($value, $object); + } + } + } + } + public function remove($value, Model &$object, string $path = null) { - if($this->mutability == 'readOnly'){ + if ($this->mutability == 'readOnly') { // silently ignore return; } @@ -186,8 +255,8 @@ public function getSortAttributeByPath(Path $path) return $result; } - public function applyComparison(Builder &$query, Path $path, $parentAttribute = null){ - + public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) + { if ($path != null && $path->isNotEmpty()) { $attributeNames = $path->getValuePathAttributes(); @@ -223,7 +292,6 @@ public function applyComparison(Builder &$query, Path $path, $parentAttribute = } } } - } /** diff --git a/src/Parser/Path.php b/src/Parser/Path.php index 18f47360..06417c6e 100644 --- a/src/Parser/Path.php +++ b/src/Parser/Path.php @@ -92,7 +92,7 @@ public function __toString() public function isNotEmpty(){ return !empty($this->getAttributePathAttributes()) || - !empty($this->getAttributePathAttributes()) || + !empty($this->getValuePathAttributes()) || $this->getValuePathFilter() != null; } } \ No newline at end of file diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index cf146318..0bbe7249 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -76,7 +76,8 @@ protected function doRead(&$object, $attributes = []) new Meta('Users'), (new AttributeSchema(Schema::SCHEMA_USER, true))->withSubAttributes( eloquent('userName', 'name')->ensure('required'), - complex('name')->withSubAttributes(eloquent('formatted', 'name')), + eloquent('active')->ensure('boolean')->default(false), + complex('name')->withSubAttributes(eloquent('formatted')), eloquent('password')->ensure('nullable'), (new class ('emails') extends Complex { protected function doRead(&$object, $attributes = []) diff --git a/tests/BasicTest.php b/tests/BasicTest.php index cad8c619..47c09387 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -68,7 +68,7 @@ public function testGetGroupsAttribute() $response->assertJsonStructure([ 'Resources' => [ '*' => [ - + ] ] ]); @@ -259,6 +259,58 @@ public function testPatch() $this->assertEquals('something@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); } + public function testPatchMultiple() + { + $response = $this->patch('/scim/v2/Users/2', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "add", + "value" => [ + "userName" => "johndoe9858", + "name.formatted" => "John", + "active" => false + ] + ]] + ]); + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertEquals('johndoe9858', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + $this->assertEquals('John', $json['urn:ietf:params:scim:schemas:core:2.0:User']['name']['formatted']); + $this->assertFalse($json['urn:ietf:params:scim:schemas:core:2.0:User']['active']); + } + + public function testPatchMultipleReplace() + { + $response = $this->patch('/scim/v2/Users/2', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "replace", + "value" => [ + "userName" => "johndoe9858", + "name.formatted" => "John", + "active" => true + ] + ]] + ]); + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertEquals('johndoe9858', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + $this->assertEquals('John', $json['urn:ietf:params:scim:schemas:core:2.0:User']['name']['formatted']); + $this->assertTrue($json['urn:ietf:params:scim:schemas:core:2.0:User']['active']); + } + public function testPatchUsername() { $response = $this->patch('/scim/v2/Users/4', [ diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php index 6b3f8852..a6d56a99 100644 --- a/tests/CustomSchemaTest.php +++ b/tests/CustomSchemaTest.php @@ -56,7 +56,6 @@ public function testPost() "urn:ietf:params:scim:schemas:core:2.0:User" => [ "userName" => "Dr. Marie Jo", "password" => "Password123", - 'employeeNumber' => '123', "emails" => [ [ "value" => "mariejo@example.com", diff --git a/tests/Model/User.php b/tests/Model/User.php index 8c6bfbba..61e06237 100644 --- a/tests/Model/User.php +++ b/tests/Model/User.php @@ -4,6 +4,11 @@ class User extends \Illuminate\Foundation\Auth\User { + + protected $casts = [ + 'active' => 'boolean', + ]; + public function groups() { return $this->belongsToMany(Group::class); diff --git a/tests/TestCase.php b/tests/TestCase.php index a78c101a..d9945430 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -43,6 +43,11 @@ protected function setUp(): void $table->timestamps(); }); + Schema::table('users', function (Blueprint $table) { + $table->string('formatted')->nullable(); + $table->boolean('active')->default(false); + }); + $this->withFactories(realpath(dirname(__DIR__) . '/database/factories')); \ArieTimmerman\Laravel\SCIMServer\RouteProvider::routes(); From 2f219c47e5bf3f87186e61712ff8ffeaebe1b3ec Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 16 Sep 2024 22:30:33 +0200 Subject: [PATCH 34/58] Support for JSONCollection columns (#91) --- Dockerfile | 9 +- docker-compose.yml | 5 + src/Attribute/Attribute.php | 4 +- src/Attribute/Collection.php | 2 +- src/Attribute/Complex.php | 2 +- src/Attribute/JSONCollection.php | 155 +++++++++++++++++++ src/Attribute/Meta.php | 3 +- src/Attribute/MutableCollection.php | 6 +- src/Attribute/Schema.php | 9 +- src/Http/Controllers/ResourceController.php | 4 +- src/Parser/Filter.php | 15 +- src/Parser/Path.php | 3 +- src/SCIMConfig.php | 10 +- tests/Model/Role.php | 16 ++ tests/Model/User.php | 7 + tests/RolesTest.php | 158 ++++++++++++++++++++ tests/TestCase.php | 13 +- 17 files changed, 395 insertions(+), 26 deletions(-) create mode 100644 src/Attribute/JSONCollection.php create mode 100644 tests/Model/Role.php create mode 100644 tests/RolesTest.php diff --git a/Dockerfile b/Dockerfile index ecd97bf8..6caeddd1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,13 @@ -FROM php:8.0-alpine +FROM php:8.1-alpine RUN apk add --no-cache git jq moreutils +RUN apk add --no-cache $PHPIZE_DEPS postgresql-dev \ + && docker-php-ext-install pdo_pgsql \ + && pecl install xdebug-3.1.5 \ + && docker-php-ext-enable xdebug \ + && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo "xdebug.client_host = 172.19.0.1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN composer create-project --prefer-dist laravel/laravel example && \ cd example diff --git a/docker-compose.yml b/docker-compose.yml index c3a84a2f..0dc043c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,11 @@ services: laravel-scim-server: build: . ports: + # forward xdebug ports - "127.0.0.1:18123:8000" + working_dir: /laravel-scim-server + environment: + - XDEBUG_MODE=debug + - XDEBUG_SESSION=1 volumes: - .:/laravel-scim-server diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index 483210aa..dd85ea53 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -270,7 +270,7 @@ public function withFilter($filter) return $this; } - public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) + public function applyComparison(Builder &$query, Path $path, Path $parentAttribute = null) { throw new SCIMException(sprintf('Comparison is not implemented for "%s"', $this->getFullKey())); } @@ -290,7 +290,7 @@ public function patch($operation, $value, Model &$object, Path $path = null) throw new SCIMException(sprintf('Patch is not implemented for "%s"', $this->getFullKey())); } - public function remove($value, Model &$object, string $path = null) + public function remove($value, Model &$object, Path $path = null) { throw new SCIMException(sprintf('Remove is not implemented for "%s"', $this->getFullKey())); } diff --git a/src/Attribute/Collection.php b/src/Attribute/Collection.php index b5c99c99..f6de40a2 100644 --- a/src/Attribute/Collection.php +++ b/src/Attribute/Collection.php @@ -49,7 +49,7 @@ protected function doRead(&$object, $attributes = []) return $result; } - public function applyComparison(Builder &$query, Path $path, $parentAttribute = null) + public function applyComparison(Builder &$query, Path $path, Path $parentAttribute = null) { if ($path == null || empty($path->getAttributePathAttributes())) { throw new SCIMException('No attribute path attributes found. Could not apply comparison in ' . $this->getFullKey()); diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index a358e694..61c459ad 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -218,7 +218,7 @@ public function add($value, Model &$object) } - public function remove($value, Model &$object, string $path = null) + public function remove($value, Model &$object, Path $path = null) { if ($this->mutability == 'readOnly') { // silently ignore diff --git a/src/Attribute/JSONCollection.php b/src/Attribute/JSONCollection.php new file mode 100644 index 00000000..d17f43e0 --- /dev/null +++ b/src/Attribute/JSONCollection.php @@ -0,0 +1,155 @@ +{$this->attribute})->contains( + fn ($item) => collect($item)->diffAssoc($v)->isEmpty() + )) { + throw new SCIMException('Value already exists', 400); + } + } + $object->{$this->attribute} = collect($object->{$this->attribute})->merge($value); + } + + public function replace($value, Model &$object, ?Path $path = null) + { + $object->{$this->attribute} = $value; + } + + public function doRead(&$object, $attributes = []) + { + return $object->{$this->attribute}?->values()->all(); + } + + public function remove($value, Model &$object, Path $path = null) + { + if ($path?->getValuePathFilter()?->getComparisonExpression() != null) { + $attributes = $path?->getValuePathFilter()?->getComparisonExpression()?->attributePath?->attributeNames; + $operator = $path?->getValuePathFilter()?->getComparisonExpression()?->operator; + $compareValue = $path?->getValuePathFilter()?->getComparisonExpression()?->compareValue; + + if ($value != null) { + throw new SCIMException('Non-null value is currently not supported for remove operation with filter'); + } + + if (count($attributes) != 1) { + throw new SCIMException('Only one attribute is currently supported for remove operation with filter'); + } + + $object->{$this->attribute} = collect($object->{$this->attribute})->filter(function ($item) use ($attributes, $operator, $compareValue) { + // check operator eq and ne + if ($operator == 'eq') { + return !(isset($item[$attributes[0]]) && $item[$attributes[0]] == $compareValue); + } elseif ($operator == 'ne') { + return !(!isset($item[$attributes[0]]) || $item[$attributes[0]] != $compareValue); + } else { + throw new SCIMException('Unsupported operator for remove operation with filter'); + } + })->values()->all(); + } else { + foreach ($value as $v) { + $object->{$this->attribute} = collect($object->{$this->attribute})->filter(function ($item) use ($v) { + return !collect($item)->diffAssoc($v)->isEmpty(); + })->values()->all(); + } + } + } + + public function applyComparison(Builder &$query, Path $path, Path $parentAttribute = null) + { + $fieldName = 'value'; + + if ($path != null && !empty($path->getAttributePathAttributes())) { + $fieldName = $path->getAttributePathAttributes()[0]; + } + + $operator = $path->node->operator; + $value = $path->node->compareValue; + + $exists = false; + + foreach ($this->subAttributes as $subAttribute) { + if ($subAttribute->name == $fieldName) { + $exists = true; + break; + } + } + + if (!$exists) { + throw new SCIMException('No attribute found with name ' . $path->getAttributePathAttributes()[0]); + } + + // check if engine is postgres + if (DB::getConfig("driver") == 'pgsql') { + $baseQuery = sprintf("EXISTS ( + SELECT 1 + FROM json_array_elements(%s) elem + WHERE elem ->> '%s' LIKE ? + )", $this->attribute, $fieldName); + } elseif (DB::getConfig("driver") == 'sqlite') { + $baseQuery = sprintf("EXISTS ( + SELECT 1 + FROM json_each(%s) AS elem + WHERE json_extract(elem.value, '$.%s') LIKE ? + )", $this->attribute, $fieldName); + } else { + throw new SCIMException('Unsupported database engine'); + } + + switch ($operator) { + case "eq": + $query->whereRaw($baseQuery, [addcslashes($value, '%_')]); + break; + case "ne": + if (DB::getConfig("driver") == 'pgsql') { + $baseQuery = sprintf("EXISTS ( + SELECT 1 + FROM json_array_elements(%s) elem + WHERE elem ->> '%s' NOT LIKE ? + )", $this->attribute, $fieldName); + } elseif (DB::getConfig("driver") == 'sqlite') { + $baseQuery = sprintf("EXISTS ( + SELECT 1 + FROM json_each(%s) AS elem + WHERE json_extract(elem.value, '$.%s') NOT LIKE ? + )", $this->attribute, $fieldName); + } else { + throw new SCIMException('Unsupported database engine'); + } + $query->whereRaw($baseQuery, [addcslashes($value, '%_')])->orWhereNull($this->attribute); + break; + case "co": + $query->whereRaw($baseQuery, ['%' . addcslashes($value, '%_') . "%"]); + break; + case "sw": + // $query->where($jsonAttribute, 'like', addcslashes($value, '%_') . '%'); + $query->whereRaw($baseQuery, [addcslashes($value, '%_') . "%"]); + break; + case "ew": + $query->whereRaw($baseQuery, ['%' . addcslashes($value, '%_')]); + break; + case "pr": + $query->whereNotNull($this->attribute); + break; + case "gt": + case "ge": + case "lt": + case "le": + throw new SCIMException("This operator is not supported for this field: " . $operator); + break; + default: + throw new SCIMException("Unknown operator " . $operator); + } + } +} diff --git a/src/Attribute/Meta.php b/src/Attribute/Meta.php index 91939410..97caed89 100644 --- a/src/Attribute/Meta.php +++ b/src/Attribute/Meta.php @@ -3,6 +3,7 @@ namespace ArieTimmerman\Laravel\SCIMServer\Attribute; use ArieTimmerman\Laravel\SCIMServer\Helper; +use ArieTimmerman\Laravel\SCIMServer\Parser\Path; use Illuminate\Database\Eloquent\Model; class Meta extends Complex @@ -41,7 +42,7 @@ protected function doRead(&$object, $attributes = []) ); } - public function remove($value, Model &$object, ?string $path = null) + public function remove($value, Model &$object, Path $path = null) { // ignore } diff --git a/src/Attribute/MutableCollection.php b/src/Attribute/MutableCollection.php index 8bb891d9..717d80e4 100644 --- a/src/Attribute/MutableCollection.php +++ b/src/Attribute/MutableCollection.php @@ -31,7 +31,7 @@ public function add($value, Model &$object) $object->load($this->attribute); } - public function remove($value, Model &$object, string $path = null) + public function remove($value, Model &$object, Path $path = null) { $values = collect($value)->pluck('value')->all(); @@ -66,9 +66,9 @@ public function patch($operation, $value, Model &$object, ?Path $path = null) if ($operation == 'add') { $this->add($value, $object); } elseif ($operation == 'remove') { - $this->remove($value, $object); + $this->remove($value, $object, $path); } elseif ($operation == 'replace') { - $this->replace($value, $object); + $this->replace($value, $object, $path); } else { throw new SCIMException('Operation not supported: ' . $operation); } diff --git a/src/Attribute/Schema.php b/src/Attribute/Schema.php index 7e15d14d..24a351a5 100644 --- a/src/Attribute/Schema.php +++ b/src/Attribute/Schema.php @@ -7,7 +7,7 @@ class Schema extends Complex public function generateSchema() { - return [ + $result = [ "schemas" => [ "urn:ietf:params:scim:schemas:core:2.0:Schema" ], @@ -21,9 +21,14 @@ public function generateSchema() ], // name is substring after last occurence of : "name" => substr($this->name, strrpos($this->name, ':') + 1), - "description" => $this->description, "attributes" => collect($this->subAttributes)->map(fn ($element) => $element->generateSchema())->toArray() ]; + + if($this->description !== null){ + $result['description'] = $this->description; + } + + return $result; } } diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index f985f363..40bd653a 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -158,12 +158,12 @@ public function update(Request $request, PolicyDecisionPoint $pdp, ResourceType foreach ($input['Operations'] as $operation) { switch (strtolower($operation['op'])) { case "add": - $resourceType->getMapping()->patch('add', $operation['value'], $resourceObject, ParserParser::parse($operation['path'] ?? null)); + $resourceType->getMapping()->patch('add', $operation['value'] ?? null, $resourceObject, ParserParser::parse($operation['path'] ?? null)); break; case "remove": if (isset($operation['path'])) { - $resourceType->getMapping()->patch('remove', $operation['value'], $resourceObject, ParserParser::parse($operation['path'] ?? null)); + $resourceType->getMapping()->patch('remove', $operation['value'] ?? null, $resourceObject, ParserParser::parse($operation['path'] ?? null)); } else { throw new SCIMException('You MUST provide a "Path"'); } diff --git a/src/Parser/Filter.php b/src/Parser/Filter.php index 8fd1d61b..0826d57a 100644 --- a/src/Parser/Filter.php +++ b/src/Parser/Filter.php @@ -2,14 +2,17 @@ namespace ArieTimmerman\Laravel\SCIMServer\Parser; +use Tmilos\ScimFilterParser\Ast\ComparisonExpression; use Tmilos\ScimFilterParser\Ast\Filter as AstFilter; -class Filter { - - public $filter; - - public function __construct(AstFilter $filter){ - $this->filter = $filter; +class Filter +{ + public function __construct(public AstFilter $filter) + { } + public function getComparisonExpression(): ComparisonExpression + { + return $this->filter instanceof ComparisonExpression ? $this->filter : null; + } } diff --git a/src/Parser/Path.php b/src/Parser/Path.php index 06417c6e..e2905598 100644 --- a/src/Parser/Path.php +++ b/src/Parser/Path.php @@ -68,7 +68,8 @@ public function shiftValuePathAttributes(): Path { $this->getValuePath()->getAttributePath()->shiftAttributeName(); if(empty($this->getValuePathAttributes())){ - $this->setValuePath(null); + // The line below isp probably not needed + // $this->setValuePath(null); } return $this; diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index 0bbe7249..8691baa7 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -9,6 +9,7 @@ use ArieTimmerman\Laravel\SCIMServer\Attribute\Complex; use ArieTimmerman\Laravel\SCIMServer\Attribute\Constant; use ArieTimmerman\Laravel\SCIMServer\Attribute\Eloquent; +use ArieTimmerman\Laravel\SCIMServer\Attribute\JSONCollection; use ArieTimmerman\Laravel\SCIMServer\Attribute\Meta; use ArieTimmerman\Laravel\SCIMServer\Attribute\MutableCollection; use ArieTimmerman\Laravel\SCIMServer\Attribute\Schema as AttributeSchema; @@ -33,7 +34,6 @@ function eloquent($name, $attribute = null): Attribute class SCIMConfig { - public function __construct() { } @@ -120,6 +120,12 @@ protected function doRead(&$object, $attributes = []) }), eloquent('display', 'name') ), + (new JSONCollection('roles'))->withSubAttributes( + eloquent('value')->ensure('required', 'min:3', 'alpha_dash:ascii'), + eloquent('display')->ensure('nullable', 'min:3', 'alpha_dash:ascii'), + eloquent('type')->ensure('nullable', 'min:3', 'alpha_dash:ascii'), + eloquent('primary')->ensure('boolean')->default(false) + )->ensure('nullable', 'array', 'max:20') ), (new AttributeSchema('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', true))->withSubAttributes( eloquent('employeeNumber')->ensure('nullable') @@ -160,7 +166,7 @@ protected function doRead(&$object, $attributes = []) (new AttributeSchema(Schema::SCHEMA_GROUP, true))->withSubAttributes( eloquent('displayName')->ensure('required', 'min:3', function ($attribute, $value, $fail) { // check if group does not exist or if it exists, it is the same group - $group = Group::where('name', $value)->first(); + $group = Group::where('displayName', $value)->first(); if ($group && (request()->route('resourceObject') == null || $group->id != request()->route('resourceObject')->id)) { $fail('The name has already been taken.'); } diff --git a/tests/Model/Role.php b/tests/Model/Role.php new file mode 100644 index 00000000..6b24ffc4 --- /dev/null +++ b/tests/Model/Role.php @@ -0,0 +1,16 @@ + 'boolean', + 'roles' => AsCollection::class, ]; public function groups() diff --git a/tests/RolesTest.php b/tests/RolesTest.php new file mode 100644 index 00000000..dbaab7f3 --- /dev/null +++ b/tests/RolesTest.php @@ -0,0 +1,158 @@ +json('roles')->nullable(); + }); + } + + public function testCreate() + { + $response = $this->post('/scim/v2/Users', [ + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:User", + ], + "urn:ietf:params:scim:schemas:core:2.0:User" => [ + "userName" => "Dr. John Doe", + "password" => "password", + "emails" => [ + [ + "value" => "john@exampl.com", + "type" => "work" + ] + ], + "roles" => [ + [ + "value" => "admin", + "display" => "Administrator", + ], + [ + "value" => "user", + "display" => "Users", + ] + ] + ] + ]); + + $response->assertStatus(201); + } + + + public function testPatch() + { + $response = $this->patch('/scim/v2/Users/2', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "add", + "path" => "roles", + "value" => [ + [ + "value" => "admin", + "display" => "Administrator", + ] + ] + ]] + ]); + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertArrayHasKey('roles', $json['urn:ietf:params:scim:schemas:core:2.0:User']); + $this->assertCount(1, $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles']); + $this->assertEquals('admin', $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles'][0]['value']); + $this->assertEquals('Administrator', $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles'][0]['display']); + } + + public function testPut() + { + $response = $this->put('/scim/v2/Users/1', [ + "id" => "1", + "meta" => [ + "resourceType" => "User", + "created" => "2010-01-23T04:56:22Z", + "lastModified" => "2011-05-13T04:42:34Z", + "version" => "W\/\"3694e05e9dff594\"" + ], + "schemas" => [ + "urn:ietf:params:scim:schemas:core:2.0:User", + ], + "urn:ietf:params:scim:schemas:core:2.0:User" => [ + "userName" => "Dr. John Doe", + "emails" => [ + [ + "value" => "johndoe@bailey.org", + "type" => "other", + "primary" => true + ] + ], + "groups" => [ + [ + "value" => "1" + ] + ], + "roles" => [ + [ + "value" => "admin", + "display" => "Administrator", + ], + [ + "value" => "user", + "display" => "Users", + ] + ] + ] + ]); + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); + $this->assertArrayHasKey('roles', $json['urn:ietf:params:scim:schemas:core:2.0:User']); + $this->assertCount(2, $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles']); + $this->assertEquals('admin', $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles'][0]['value']); + $this->assertEquals('Administrator', $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles'][0]['display']); + + // find role with value admin + $response = $this->get('/scim/v2/Users?filter=roles.value sw "adm"'); + $response->assertStatus(200); + $json = $response->json(); + $this->assertCount(1, $json['Resources']); + + $response = $this->patch('/scim/v2/Users/1', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "remove", + "path" => "roles", + "value" => [ + [ + "value" => "admin", + "display" => "Administrator", + ] + ] + ]] + ]); + + $json = $response->json(); + + $this->assertCount(1, $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles']); + $this->assertEquals('user', $json['urn:ietf:params:scim:schemas:core:2.0:User']['roles'][0]['value']); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index d9945430..4c47817b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -32,8 +32,8 @@ protected function setUp(): void Schema::create('group_user', function (Blueprint $table) { $table->increments('id'); - $table->uuid('group_id'); - $table->uuid('user_id'); + $table->integer('group_id')->unsigned(); + $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade'); @@ -62,6 +62,12 @@ protected function setUp(): void }); } + protected function beforeRefreshingDatabase(){ + // Schema::dropIfExists('group_user'); + // Schema::dropIfExists('groups'); + // Schema::dropIfExists('users'); + } + protected function getEnvironmentSetUp($app) { $app ['config']->set('app.url', 'http://localhost'); @@ -79,5 +85,4 @@ protected function getEnvironmentSetUp($app) 'prefix' => '', ]); } - -} \ No newline at end of file +} From fa601958e2ddbec404e516f3b9617bb66dc7f4d3 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 30 Sep 2024 22:08:04 +0200 Subject: [PATCH 35/58] Ensure resource types are presented correctly in list response (#93) --- src/Helper.php | 2 +- src/SCIM/ResourceType.php | 3 ++- tests/ResourceTypesTest.php | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Helper.php b/src/Helper.php index d97ac7cd..73f65678 100644 --- a/src/Helper.php +++ b/src/Helper.php @@ -50,7 +50,7 @@ public static function prepareReturn(Arrayable $object, ResourceType $resourceTy public static function objectToSCIMArray($object, ResourceType $resourceType = null, array $attributes = []) { if($resourceType == null){ - return $object; + return $object instanceof Arrayable ? $object->toArray() : $object; } $mapping = $resourceType->getMapping(); diff --git a/src/SCIM/ResourceType.php b/src/SCIM/ResourceType.php index 34892e2f..7d310e3e 100644 --- a/src/SCIM/ResourceType.php +++ b/src/SCIM/ResourceType.php @@ -2,9 +2,10 @@ namespace ArieTimmerman\Laravel\SCIMServer\SCIM; +use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Jsonable; -class ResourceType implements Jsonable +class ResourceType implements Jsonable, Arrayable { public $id; diff --git a/tests/ResourceTypesTest.php b/tests/ResourceTypesTest.php index f5ff9303..10fd8a29 100644 --- a/tests/ResourceTypesTest.php +++ b/tests/ResourceTypesTest.php @@ -16,6 +16,26 @@ public function testGet() { $response = $this->get('/scim/v2/ResourceTypes'); $response->assertStatus(200); + + $response->assertJsonStructure([ + 'schemas', + 'totalResults', + 'Resources' => [ + '*' => [ + 'schemas', + 'id', + 'name', + 'endpoint', + 'description', + 'schema', + 'schemaExtensions', + 'meta' => [ + 'location', + 'resourceType' + ] + ] + ] + ]); } public function testGetOne(){ From 47d8379005231f4f13fb48874358dec242f3fe14 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 3 Oct 2024 06:50:47 +0200 Subject: [PATCH 36/58] Allow not returning certain attributes (#94) --- src/Attribute/Attribute.php | 12 +++++++++++- src/SCIMConfig.php | 2 +- tests/BasicTest.php | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index dd85ea53..c82f7b59 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -39,6 +39,7 @@ class Attribute protected $type = 'string'; protected $description = null; protected $defaultValue = null; + protected $returned = 'default'; public $dirty = false; @@ -68,7 +69,7 @@ public function generateSchema(){ 'name' => $this->name, 'type' => $this->type, 'mutability' => $this->mutability, - 'returned' => 'default', + 'returned' => $this->returned, 'uniqueness' => 'server', 'required' => $this->isRequired(), 'multiValued' => $this->getMultiValued(), @@ -133,6 +134,11 @@ public function shouldReturn(&$object) return true; } + public function setReturned($returned){ + $this->returned = $returned; + return $this; + } + public function setParent($parent) { $this->parent = $parent; @@ -150,6 +156,10 @@ public function read(&$object, array $attributes = []): ?AttributeValue return null; } + if($this->returned == 'never'){ + return null; + } + return new AttributeValue($this->doRead($object, $attributes) ?? $this->defaultValue); } diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index 8691baa7..24a33c97 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -78,7 +78,7 @@ protected function doRead(&$object, $attributes = []) eloquent('userName', 'name')->ensure('required'), eloquent('active')->ensure('boolean')->default(false), complex('name')->withSubAttributes(eloquent('formatted')), - eloquent('password')->ensure('nullable'), + eloquent('password')->ensure('nullable')->setReturned('never'), (new class ('emails') extends Complex { protected function doRead(&$object, $attributes = []) { diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 47c09387..87e036a8 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -369,6 +369,7 @@ public function testPost() $this->assertArrayHasKey('urn:ietf:params:scim:schemas:core:2.0:User', $json); $this->assertEquals('mariejo@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); + $this->assertArrayNotHasKey('password', $json['urn:ietf:params:scim:schemas:core:2.0:User']); } public function testPostTopLevel() From fd2dbacee60468b3999a3b226069f3c93e6cf6e9 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 10 Oct 2024 22:36:31 +0200 Subject: [PATCH 37/58] ignore non existing attributes (#95) --- src/Attribute/Complex.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index 61c459ad..b111e0ac 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -110,7 +110,6 @@ public function patch($operation, $value, Model &$object, Path $path = null, $re */ public function replace($value, Model &$object, Path $path = null, $removeIfNotSet = false) { - $match = false; $this->dirty = true; if ($this->mutability == 'readOnly') { @@ -129,7 +128,6 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS // if path contains : it is a schema node if (strpos($key, ':') !== false) { $subNode = $this->getSubNode($key); - $match = true; } else { $path = Parser::parse($key); @@ -138,11 +136,10 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS $path = $path->shiftAttributePathAttributes(); $sub = $attributeNames[0] ?? $path->getAttributePath()?->path?->schema; $subNode = $this->getSubNode($attributeNames[0] ?? $path->getAttributePath()?->path?->schema); - $match = true; } } - if ($match) { + if ($subNode != null) { $newValue = $v; if ($path->isNotEmpty()) { $newValue = [ @@ -155,7 +152,7 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS } // if this is the root, we may also check the schema nodes - if (!$match && $this->parent == null) { + if ($subNode == null && $this->parent == null) { foreach ($this->subAttributes as $attribute) { if ($attribute instanceof Schema) { $attribute->replace($value, $object, $path); From d634f0fd7a43515a72b4ae22f30fe15a2ba7291e Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 30 Oct 2024 16:28:45 +0100 Subject: [PATCH 38/58] Support for Querying Resources Using HTTP POST (#97) --- src/Http/Controllers/ResourceController.php | 24 +++++++++- src/RouteProvider.php | 2 +- tests/BasicTest.php | 53 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 40bd653a..0419a0ef 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -15,6 +15,7 @@ use ArieTimmerman\Laravel\SCIMServer\Events\Patch; use ArieTimmerman\Laravel\SCIMServer\Parser\Parser as ParserParser; use ArieTimmerman\Laravel\SCIMServer\PolicyDecisionPoint; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\User; use Illuminate\Contracts\Pagination\CursorPaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Validator; @@ -274,7 +275,12 @@ function (Builder $query) use ($filter, $resourceType) { } // TODO: splitting the attributes parameters by dot and comma is not correct, but works in most cases - $attributes = $request->input('attributes') ? preg_split('/[,.]/', $request->input('attributes')) : []; + // if body contains attributes and this is an array, use that, else use existing + if($request->json('attributes') && is_array($request->json('attributes'))){ + $attributes = $request->json('attributes'); + } else { + $attributes = $request->input('attributes') ? preg_split('/[,.]/', $request->input('attributes')) : []; + } if (!empty($attributes)) { $attributes[] = 'id'; @@ -282,6 +288,7 @@ function (Builder $query) use ($filter, $resourceType) { $attributes[] = 'schemas'; } + // TODO: implement excludedAttributes $excludedAttributes = []; return new ListResponse( @@ -295,4 +302,19 @@ function (Builder $query) use ($filter, $resourceType) { ($resourceObjects instanceof CursorPaginator) ? $resourceObjects->previousCursor()?->encode() : null ); } + + public function search(Request $request, PolicyDecisionPoint $pdp, ResourceType $resourceType){ + + $input = $request->json()->all(); + + // ensure request post body is a scim SearchRequest + if (!is_array($input) || !isset($input['schemas']) || !in_array("urn:ietf:params:scim:api:messages:2.0:SearchRequest", $input['schemas'])) { + throw (new SCIMException('Invalid schema. MUST be "urn:ietf:params:scim:api:messages:2.0:SearchRequest"'))->setCode(400); + } + + // ensure $request->input reads from payload/post only, not query parameters + $request->replace($request->json()->all()); + + return $this->index($request, $pdp, $resourceType); + } } diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 548159a5..447ad1c0 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -78,7 +78,7 @@ private static function allRoutes(array $options = []) // TODO: Use the attributes parameters ?attributes=userName, excludedAttributes=asdg,asdg (respect "returned" settings "always") Route::get('/{resourceType}/{resourceObject}', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@show')->name('scim.resource'); Route::get("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@index')->name('scim.resources'); - + Route::post("/{resourceType}/.search", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@search'); Route::post("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@create'); Route::put("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@replace'); diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 87e036a8..51b095d3 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -56,6 +56,41 @@ public function testGetAttributes() ] ] ]); + + foreach ($response->json('Resources') as $resource) { + $this->assertArrayNotHasKey('emails', $resource['urn:ietf:params:scim:schemas:core:2.0:User']); + } + } + + public function testGetAttributesSearch() + { + $response = $this->postJson( + '/scim/v2/Users/.search', + [ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:SearchRequest'], + 'attributes' => [ + 'userName', + 'name.formatted', + 'groups' + ] + ] + ); + + $response->assertStatus(200); + $response->assertJsonCount(10, 'Resources'); + $response->assertJsonStructure([ + 'Resources' => [ + '*' => [ + 'urn:ietf:params:scim:schemas:core:2.0:User' => [ + 'userName', + ] + ] + ] + ]); + + foreach ($response->json('Resources') as $resource) { + $this->assertArrayNotHasKey('emails', $resource['urn:ietf:params:scim:schemas:core:2.0:User']); + } } public function testGetGroupsAttribute() @@ -180,6 +215,24 @@ public function testFilterByGroup() $response->assertStatus(200); } + public function testSearch() + { + // First get a username to search for + $response = $this->postJson('/scim/v2/Users/.search', [ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:SearchRequest'], + "startIndex" => 30, + "count" => 1 + ]); + + $userName = $response->json('Resources')[0]['urn:ietf:params:scim:schemas:core:2.0:User']['userName']; + + // Now search for this username + $response = $this->get('/scim/v2/Users?filter=userName eq "'.$userName.'"'); + $response->assertStatus(200); + + $this->assertEquals(1, count($response->json('Resources'))); + } + public function testGroupAssignment() { // First get a username to search for From f913bbfdf582831d68c226661ed3c1a1c0767e30 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 11 Dec 2024 23:20:27 +0100 Subject: [PATCH 39/58] Support for saving members attribute upon group create (and similar cases) (#100) --- src/Attribute/MutableCollection.php | 19 ++++++++++++----- tests/GroupsTest.php | 32 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/Attribute/MutableCollection.php b/src/Attribute/MutableCollection.php index 717d80e4..e50cb317 100644 --- a/src/Attribute/MutableCollection.php +++ b/src/Attribute/MutableCollection.php @@ -46,19 +46,28 @@ public function replace($value, Model &$object, ?Path $path = null) // Check if objects exist $existingObjects = $object - ->{$this->attribute}() - ->getRelated() - ::findMany($values) + ->{$this->attribute}() + ->getRelated() + ::findMany($values); + $existingObjectIds = $existingObjects ->map(fn ($o) => $o->getKey()); - if (($diff = collect($values)->diff($existingObjects))->count() > 0) { + if (($diff = collect($values)->diff($existingObjectIds))->count() > 0) { throw new SCIMException( sprintf('One or more %s are unknown: %s', $this->attribute, implode(',', $diff->all())), 500 ); } - $object->{$this->attribute}()->sync($existingObjects->all()); + // Act like the relation is already saved. This allows running validations, if needed. + $object->setRelation($this->attribute, $existingObjects); + + $object->saved(function (Model $model) use ($object, $existingObjectIds) { + // Save relationships only after the model is saved. Essential if the model is new. + // Intentionlly `$object` is used instead of `$model`, to avoid accidentially updating the wrong model. + $object->{$this->attribute}()->sync($existingObjectIds->all()); + }); + } public function patch($operation, $value, Model &$object, ?Path $path = null) diff --git a/tests/GroupsTest.php b/tests/GroupsTest.php index 794304ee..c483b89d 100644 --- a/tests/GroupsTest.php +++ b/tests/GroupsTest.php @@ -3,6 +3,7 @@ namespace ArieTimmerman\Laravel\SCIMServer\Tests; use ArieTimmerman\Laravel\SCIMServer\Tests\Model\Group; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\User; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -69,6 +70,37 @@ public function testCreate(){ } + public function testCreateWithMembers(){ + $response = $this->post('/scim/v2/Groups', [ + 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], // Required + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'displayName' => 'TestGroup', + 'members' => [ + [ + 'value' => User::first()->id, + ] + ] + ] + ]); + + $response->assertJsonStructure([ + 'id', + 'urn:ietf:params:scim:schemas:core:2.0:Group' => [ + 'displayName', + 'members' => [ + 0 => [ + 'value', + 'display' + ] + ] + ] + ]); + + $this->assertEquals(User::first()->id, $response->json(['urn:ietf:params:scim:schemas:core:2.0:Group'])['members'][0]['value']); + $this->assertNotNull(Group::find($response->json('id'))); + $this->assertNotNull(Group::where('displayName', 'TestGroup')->first()); + } + public function testBulk(){ $response = $this->post('/scim/v2/Bulk', [ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:BulkRequest'], // Required From a5f9fa7f8ddbc122879fa5c0449e965dce911dab Mon Sep 17 00:00:00 2001 From: Miguel dos Santos Vaz Dias Wicht Date: Wed, 11 Dec 2024 23:26:59 +0100 Subject: [PATCH 40/58] Use static instead of self when calling static functions to allow easier customisation in subclasses (#96) --- src/Http/Controllers/ResourceController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 0419a0ef..6272de20 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -55,9 +55,9 @@ public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint } $flattened = Helper::flatten($input, $input['schemas']); - $flattened = self::validateScim($resourceType, $flattened, null); + $flattened = static::validateScim($resourceType, $flattened, null); - if (!$allowAlways && !self::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_POST, $flattened, $resourceType, null, $isMe)) { + if (!$allowAlways && !static::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_POST, $flattened, $resourceType, null, $isMe)) { throw (new SCIMException('This is not allowed'))->setCode(403); } @@ -68,7 +68,7 @@ public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint //validate $newObject = Helper::flatten(Helper::objectToSCIMArray($resourceObject, $resourceType), $resourceType->getSchema()); - $flattened = self::validateScim($resourceType, $newObject, $resourceObject); + $flattened = static::validateScim($resourceType, $newObject, $resourceObject); $resourceObject->save(); @@ -82,7 +82,7 @@ public function createObject(Request $request, PolicyDecisionPoint $pdp, Resourc { $input = $request->input(); - $resourceObject = self::createFromSCIM($resourceType, $input, $pdp, $request, false, $isMe); + $resourceObject = static::createFromSCIM($resourceType, $input, $pdp, $request, false, $isMe); event(new Create($resourceObject, $resourceType, $isMe, $request->input())); @@ -130,7 +130,7 @@ public function replace(Request $request, PolicyDecisionPoint $pdp, ResourceType $flattened = $this->validateScim($resourceType, $newObject, $resourceObject); - if (!self::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PATCH, $flattened, $resourceType, null)) { + if (!static::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PATCH, $flattened, $resourceType, null)) { throw new SCIMException('This is not allowed'); } @@ -185,7 +185,7 @@ public function update(Request $request, PolicyDecisionPoint $pdp, ResourceType $flattened = $this->validateScim($resourceType, $newObject, $resourceObject); - if (!self::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PATCH, $flattened, $resourceType, null)) { + if (!static::isAllowed($pdp, $request, PolicyDecisionPoint::OPERATION_PATCH, $flattened, $resourceType, null)) { throw new SCIMException('This is not allowed'); } From 376c61231eefeb91e1b81646fdc7b6c2f257a551 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 12 Dec 2024 20:38:46 +0100 Subject: [PATCH 41/58] Fixes error code #74 (#101) --- src/Attribute/Constant.php | 2 +- src/Http/Controllers/ResourceController.php | 2 +- tests/GroupsTest.php | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Attribute/Constant.php b/src/Attribute/Constant.php index 722d715a..6ab312c0 100644 --- a/src/Attribute/Constant.php +++ b/src/Attribute/Constant.php @@ -32,7 +32,7 @@ public function replace($value, &$object, $path = null) $current = json_encode($this->read($object)?->value); if (json_encode($value) != $current) { - throw (new SCIMException(sprintf('Write to "%s" is not supported, tried to change "%s" to "%s"', $this->getFullKey(), $current, json_encode($value))))->setCode(500)->setScimType('mutability'); + throw (new SCIMException(sprintf('Write to "%s" is not supported, tried to change "%s" to "%s"', $this->getFullKey(), $current, json_encode($value))))->setCode(403)->setScimType('mutability'); } $this->dirty = true; diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index 6272de20..d5eafffb 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -51,7 +51,7 @@ protected static function validateScim(ResourceType $resourceType, $flattened, ? public static function createFromSCIM($resourceType, $input, PolicyDecisionPoint $pdp = null, Request $request = null, $allowAlways = false, $isMe = false) { if (!isset($input['schemas']) || !is_array($input['schemas'])) { - throw (new SCIMException('Missing a valid schemas-attribute.'))->setCode(500); + throw (new SCIMException('Missing a valid schemas-attribute.'))->setCode(400); } $flattened = Helper::flatten($input, $input['schemas']); diff --git a/tests/GroupsTest.php b/tests/GroupsTest.php index c483b89d..2d7c7968 100644 --- a/tests/GroupsTest.php +++ b/tests/GroupsTest.php @@ -142,4 +142,9 @@ public function testBulk(){ // confirm testgroup1 exists $this->assertNotNull(Group::where('displayName', 'TestGroup2')->first()); } + + public function testGroupCreationFailure(){ + $response = $this->post('/scim/v2/Groups', []); + $response->assertStatus(400); + } } From 2b77122a257e0a8bd19abaf04234acb9c214941d Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 12 Dec 2024 21:50:04 +0100 Subject: [PATCH 42/58] New version for #90 (#102) --- src/Attribute/Schema.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attribute/Schema.php b/src/Attribute/Schema.php index 24a351a5..fa6fd1df 100644 --- a/src/Attribute/Schema.php +++ b/src/Attribute/Schema.php @@ -16,7 +16,7 @@ public function generateSchema() "resourceType" => "Schema", "created" => "2001-01-01T00:00:00+00:00", "lastModified" => "2001-01-01T00:00:00+00:00", - "version" => 'W/"1"', + "version" => sprintf('W/"%s"', sha1(filemtime(__FILE__))), "location" => route('scim.schemas', ['id' => $this->name]) ], // name is substring after last occurence of : From cbc6fad309e3278bcf5f0696a37cf463b9a45aeb Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Mon, 16 Dec 2024 20:47:02 +0100 Subject: [PATCH 43/58] fix type of name attribute #103 (#104) --- src/Attribute/Complex.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index b111e0ac..35c32df7 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -11,6 +11,8 @@ class Complex extends AbstractComplex { + protected $type = 'complex'; + /** * @return string[] */ From 44cfc5f783e866c5cdce8d26bb254a6a479b9995 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Fri, 10 Jan 2025 20:36:45 +0100 Subject: [PATCH 44/58] Added invalidCursor error (#105) --- src/Http/Controllers/ResourceController.php | 9 +++++++++ tests/BasicTest.php | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index d5eafffb..cb59f25a 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -18,6 +18,7 @@ use ArieTimmerman\Laravel\SCIMServer\Tests\Model\User; use Illuminate\Contracts\Pagination\CursorPaginator; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Pagination\Cursor; use Illuminate\Support\Facades\Validator; class ResourceController extends Controller @@ -259,6 +260,14 @@ function (Builder $query) use ($filter, $resourceType) { $resourceObjects = $resourceObjects->orderBy('id'); } + if($request->input('cursor')){ + $cursor = @Cursor::fromEncoded($request->input('cursor')); + + if($cursor == null){ + throw (new SCIMException('Invalid Cursor'))->setCode(400)->setScimType('invalidCursor'); + } + } + $resourceObjects = $resourceObjects->cursorPaginate( $count, cursor: $request->input('cursor') diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 51b095d3..3db41542 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -140,6 +140,19 @@ public function testCursorPagination() $this->assertNull($response2->json('startIndex')); } + public function testCursorPaginationFailure() + { + $response1 = $this->get('/scim/v2/Users?count=60&cursor=invalid'); + + $response1->assertStatus(400); + $response1->assertJson([ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'], + 'status' => '400', + 'scimType' => 'invalidCursor' + ]); + + } + public function testPagination() { $response = $this->get('/scim/v2/Users?startIndex=21&count=20'); From 3fd46be6ac6bfdbb59b90bcf68b1697ef65ff7b1 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Sat, 11 Jan 2025 20:17:39 +0100 Subject: [PATCH 45/58] Improved pagesize and max size support, especially for cusor pagination (#106) --- config/scim.php | 5 +++++ src/Http/Controllers/ResourceController.php | 10 +++++++++- .../Controllers/ServiceProviderController.php | 4 ++-- tests/BasicTest.php | 18 ++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/config/scim.php b/config/scim.php index 3956946b..e92941b0 100644 --- a/config/scim.php +++ b/config/scim.php @@ -4,4 +4,9 @@ "publish_routes" => true, 'omit_main_schema_in_return' => false, 'omit_null_values' => true, + + 'pagination' => [ + 'defaultPageSize' => 10, + 'maxPageSize' => 100, + ] ]; diff --git a/src/Http/Controllers/ResourceController.php b/src/Http/Controllers/ResourceController.php index cb59f25a..515eb6f4 100644 --- a/src/Http/Controllers/ResourceController.php +++ b/src/Http/Controllers/ResourceController.php @@ -219,7 +219,7 @@ public function index(Request $request, PolicyDecisionPoint $pdp, ResourceType $ } // Non-negative integer. Specifies the desired maximum number of query results per page, e.g., 10. A negative value SHALL be interpreted as "0". A value of "0" indicates that no resource results are to be returned except for "totalResults". - $count = min(max(0, intVal($request->input('count', 10))), 100); + $count = min(max(0, intVal($request->input('count', config('scim.pagination.defaultPageSize')))), config('scim.pagination.maxPageSize')); $startIndex = null; $sortBy = null; @@ -267,6 +267,14 @@ function (Builder $query) use ($filter, $resourceType) { throw (new SCIMException('Invalid Cursor'))->setCode(400)->setScimType('invalidCursor'); } } + + $countRaw = $request->input('count'); + + if($countRaw < 1 || $countRaw > config('scim.pagination.maxPageSize')){ + throw (new SCIMException( + sprintf('Count value is invalid. Count value must be between 1 - and maxPageSize (%s) (when using cursor pagination)', config('scim.pagination.maxPageSize')) + ))->setCode(400)->setScimType('invalidCount'); + } $resourceObjects = $resourceObjects->cursorPaginate( $count, diff --git a/src/Http/Controllers/ServiceProviderController.php b/src/Http/Controllers/ServiceProviderController.php index 1bf7d051..90987216 100644 --- a/src/Http/Controllers/ServiceProviderController.php +++ b/src/Http/Controllers/ServiceProviderController.php @@ -54,8 +54,8 @@ public function index() "cursor" => true, "index" => true, "defaultPaginationMethod" => "index", - "defaultPageSize" => 10, - "maxPageSize" => 100, + "defaultPageSize" => config('scim.pagination.defaultPageSize'), + "maxPageSize" => config('scim.pagination.maxPageSize'), "cursorTimeout" => 3600 ], "meta" => [ diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 3db41542..67a6d54f 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -153,6 +153,19 @@ public function testCursorPaginationFailure() } + public function testCursorPaginationFailureMaxCount() + { + $response1 = $this->get('/scim/v2/Users?count=200&cursor'); + + $response1->assertStatus(400); + $response1->assertJson([ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'], + 'status' => '400', + 'scimType' => 'invalidCount' + ]); + + } + public function testPagination() { $response = $this->get('/scim/v2/Users?startIndex=21&count=20'); @@ -470,4 +483,9 @@ public function testPostTopLevel() $this->assertEquals('mariejo@example.com', $json['urn:ietf:params:scim:schemas:core:2.0:User']['emails'][0]['value']); $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); } + + public function testTotalResultsOnly(){ + $response = $this->get('/scim/v2/Users?count=0'); + $this->assertTrue(true); + } } From 2054442bbb4e14e420594ce1fdb7de1cfa00f4fd Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 6 Feb 2025 07:26:03 +0100 Subject: [PATCH 46/58] fixes incorrect schemaExtensions format #107 (#108) --- src/Attribute/Schema.php | 11 +++++++++++ .../Controllers/ResourceTypesController.php | 18 ++++++++++++++++-- src/SCIMConfig.php | 2 +- tests/ResourceTypesTest.php | 5 ++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Attribute/Schema.php b/src/Attribute/Schema.php index fa6fd1df..ee3bd7b2 100644 --- a/src/Attribute/Schema.php +++ b/src/Attribute/Schema.php @@ -4,6 +4,13 @@ class Schema extends Complex { + public $required; + + function __construct($name, $required = true) + { + parent::__construct($name); + $this->required = $required; + } public function generateSchema() { @@ -31,4 +38,8 @@ public function generateSchema() return $result; } + public function getName(){ + return $this->name; + } + } diff --git a/src/Http/Controllers/ResourceTypesController.php b/src/Http/Controllers/ResourceTypesController.php index f3adfafe..95e31ba8 100644 --- a/src/Http/Controllers/ResourceTypesController.php +++ b/src/Http/Controllers/ResourceTypesController.php @@ -20,9 +20,23 @@ public function __construct() $resourceTypes = []; foreach ($config as $key => $value) { - $schemas = $value['map']->getSchemas(); + $schemas = $value['map']->getSchemaNodes(); - $resourceTypes[] = new ResourceType($value['singular'], $key, $key, $value['description'] ?? null, $schemas[0], array_slice($schemas, 1)); + $resourceTypes[] = new ResourceType( + $value['singular'], + $key, + $key, + $value['description'] ?? null, + $schemas[0]->getName(), + collect(array_slice($schemas, 1))->map( + function ($element) { + return [ + 'schema' => $element->getName(), + 'required' => $element->required + ]; + } + )->toArray() + ); } $this->resourceTypes = collect($resourceTypes); diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index 24a33c97..bda4c4a9 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -127,7 +127,7 @@ protected function doRead(&$object, $attributes = []) eloquent('primary')->ensure('boolean')->default(false) )->ensure('nullable', 'array', 'max:20') ), - (new AttributeSchema('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', true))->withSubAttributes( + (new AttributeSchema('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', false))->withSubAttributes( eloquent('employeeNumber')->ensure('nullable') ) ), diff --git a/tests/ResourceTypesTest.php b/tests/ResourceTypesTest.php index 10fd8a29..5eea15f7 100644 --- a/tests/ResourceTypesTest.php +++ b/tests/ResourceTypesTest.php @@ -50,7 +50,10 @@ public function testGetOne(){ "description" => "User Account", "schema" => "urn:ietf:params:scim:schemas:core:2.0:User", "schemaExtensions" => [ - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" + [ + "schema" => "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", + "required" => false + ] ], "meta" => [ "location" => "http://localhost/scim/v2/ResourceTypes/User", From 6c31ac718741251bbdb15faf29b04d8b072cb66d Mon Sep 17 00:00:00 2001 From: Robert Jelen Date: Wed, 19 Mar 2025 23:03:45 +0100 Subject: [PATCH 47/58] Add support for Laravel 12 (#112) --- .github/workflows/ci.yml | 10 +++++++++- composer.json | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9131ebaf..8f2aa694 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: php: [8.0, 8.1, 8.2] - laravel: [8.*, 9.*, 10.*, 11.*] + laravel: [8.*, 9.*, 10.*, 11.*, 12.*] exclude: - laravel: 8.* php: 8.2 @@ -23,6 +23,14 @@ jobs: php: 8.0 - laravel: 11.* php: 8.1 + - laravel: 12.* + php: 7.3 + - laravel: 12.* + php: 7.4 + - laravel: 12.* + php: 8.0 + - laravel: 12.* + php: 8.1 name: PHP ${{ matrix.php }} on Laravel ${{ matrix.laravel }} diff --git a/composer.json b/composer.json index 87e6a26b..e63dfbca 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^8.0", - "illuminate/database": "^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", - "illuminate/console": "^8.0|^9.0|^10.0|^11.0", + "illuminate/database": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, @@ -29,7 +29,7 @@ } }, "require-dev": { - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", "laravel/legacy-factories": "*" }, From 7ece4d79bd576fb544de4c0a6e86f050810d7735 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 26 Mar 2025 16:28:11 +0100 Subject: [PATCH 48/58] Support remove operations by specifying path with a filter (#113) --- src/Attribute/MutableCollection.php | 34 ++++++++++++++++++++++++++--- tests/BasicTest.php | 18 +++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Attribute/MutableCollection.php b/src/Attribute/MutableCollection.php index e50cb317..69733370 100644 --- a/src/Attribute/MutableCollection.php +++ b/src/Attribute/MutableCollection.php @@ -31,11 +31,39 @@ public function add($value, Model &$object) $object->load($this->attribute); } - public function remove($value, Model &$object, Path $path = null) + public function remove($value, Model &$object, ?Path $path = null) { - $values = collect($value)->pluck('value')->all(); + if ($path?->getValuePathFilter()?->getComparisonExpression() != null) { + $attributes = $path?->getValuePathFilter()?->getComparisonExpression()?->attributePath?->attributeNames; + $operator = $path?->getValuePathFilter()?->getComparisonExpression()?->operator; + $compareValue = $path?->getValuePathFilter()?->getComparisonExpression()?->compareValue; + + if ($value != null) { + throw new SCIMException('Remove operation with filter requires a null value parameter', 400); + } + + if (count($attributes) != 1) { + throw new SCIMException(sprintf('Filter must specify exactly one attribute, found %d attributes', count($attributes)), 400); + } + + if ($operator != 'eq') { + throw new SCIMException(sprintf('Unsupported filter operator "%s" - only "eq" is supported', $operator), 400); + } + + if ($attributes[0] != 'value') { + throw new SCIMException(sprintf('Cannot filter on "%s" - only filtering on "value" attribute is supported', $attributes[0]), 400); + } + + if ($value != null) { + throw new SCIMException('Cannot specify both a filter path and a value parameter simultaneously', 400); + } + + $object->{$this->attribute}()->detach([$compareValue]); + } else { + $values = collect($value)->pluck('value')->all(); - $object->{$this->attribute}()->detach($values); + $object->{$this->attribute}()->detach($values); + } $object->load($this->attribute); } diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 67a6d54f..4ae64575 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -4,6 +4,7 @@ use ArieTimmerman\Laravel\SCIMServer\ResourceType; use ArieTimmerman\Laravel\SCIMServer\SCIMConfig; +use ArieTimmerman\Laravel\SCIMServer\Tests\Model\Group; use Illuminate\Support\Arr; class BasicTest extends TestCase @@ -239,6 +240,23 @@ public function testFilterByGroup() ]); $response->assertStatus(200); + + $this->assertTrue(Group::find($groupValue)->members->pluck('id')->contains($userValue), 'User was not added to the group'); + + // SCIM Patch remove member request + $response = $this->patch('/scim/v2/Groups/' . $groupValue, [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [[ + "op" => "remove", + "path" => sprintf("members[value eq \"%s\"]", $userValue), + ]] + ]); + + $response->assertStatus(200); + + $this->assertFalse(Group::find($groupValue)->members->pluck('id')->contains($userValue), 'User was not removed from the group'); } public function testSearch() From 34239300fb88a4b27072782aaa12aae0c7274676 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 9 Apr 2025 18:10:06 +0200 Subject: [PATCH 49/58] Set attribute type based on validations used, or fallback to default (#115) Closes #114 --- src/Attribute/Attribute.php | 38 ++++++++++++++++++++++++++++--------- tests/SchemaTest.php | 27 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index c82f7b59..65a562b8 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -9,8 +9,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Tmilos\ScimFilterParser\Ast\AttributePath; -use Tmilos\ScimFilterParser\Ast\ComparisonExpression; -use Tmilos\ScimSchema\Model\Resource; +use Illuminate\Support\Str; class Attribute { @@ -36,7 +35,7 @@ class Attribute protected $multiValued = false; protected $mutability = 'readWrite'; - protected $type = 'string'; + protected $type; protected $description = null; protected $defaultValue = null; protected $returned = 'default'; @@ -64,10 +63,11 @@ public function getValidations() /** * Return SCIM schema for this attribute */ - public function generateSchema(){ + public function generateSchema() + { return [ 'name' => $this->name, - 'type' => $this->type, + 'type' => $this->getType(), 'mutability' => $this->mutability, 'returned' => $this->returned, 'uniqueness' => 'server', @@ -103,7 +103,8 @@ public function ensure(...$validations) return $this; } - public function default($value){ + public function default($value) + { $this->defaultValue = $value; return $this; @@ -119,6 +120,23 @@ public function nullable() return in_array('nullable', $this->validations); } + public function getType() + { + $result = $this->type; + + if ($result == null) { + if (in_array('boolean', $this->validations)) { + $result = 'boolean'; + } elseif (in_array('integer', $this->validations)) { + $result = 'integer'; + } else { + $result = 'string'; + } + } + + return $result; + } + public function isReadSupported() { return $this->readEnabled; @@ -134,7 +152,8 @@ public function shouldReturn(&$object) return true; } - public function setReturned($returned){ + public function setReturned($returned) + { $this->returned = $returned; return $this; } @@ -145,7 +164,8 @@ public function setParent($parent) return $this; } - protected function isRequested($attributes){ + protected function isRequested($attributes) + { return empty($attributes) || in_array($this->name, $attributes) || in_array($this->getFullKey(), $attributes) || ($this->parent != null && $this->parent->isRequested($attributes)); } @@ -156,7 +176,7 @@ public function read(&$object, array $attributes = []): ?AttributeValue return null; } - if($this->returned == 'never'){ + if ($this->returned == 'never') { return null; } diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 5fcc7449..f4f2e1ec 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -37,6 +37,33 @@ public function testGet() { $response = $this->get('/scim/v2/Schemas'); $response->assertStatus(200); + + $jsonResponse = $response->json(); + $this->assertNotEmpty($jsonResponse); + + // Find the User schema + $userSchema = null; + foreach ($jsonResponse['Resources'] as $schema) { + if ($schema['id'] === 'urn:ietf:params:scim:schemas:core:2.0:User') { + $userSchema = $schema; + break; + } + } + + $this->assertNotNull($userSchema, "User schema not found"); + + // Find the active attribute in the schema attributes + $activeAttribute = null; + foreach ($userSchema['attributes'] as $attribute) { + if ($attribute['name'] === 'active') { + $activeAttribute = $attribute; + break; + } + } + + $this->assertNotNull($activeAttribute, "Active attribute not found"); + $this->assertEquals('boolean', $activeAttribute['type'], "Active attribute is not of type boolean"); + } public function getSchemaGenerator(){ From 6f486907ffcc47ec42a9bf8fe6e768847e8d9c34 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Tue, 22 Apr 2025 20:19:17 +0200 Subject: [PATCH 50/58] Fix for resource types endpoint (#116) --- src/Http/Controllers/ResourceTypesController.php | 2 +- src/Http/Controllers/SchemaController.php | 2 +- tests/ResourceTypesTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Controllers/ResourceTypesController.php b/src/Http/Controllers/ResourceTypesController.php index 95e31ba8..445d971c 100644 --- a/src/Http/Controllers/ResourceTypesController.php +++ b/src/Http/Controllers/ResourceTypesController.php @@ -24,7 +24,7 @@ public function __construct() $resourceTypes[] = new ResourceType( $value['singular'], - $key, + $value['singular'], $key, $value['description'] ?? null, $schemas[0]->getName(), diff --git a/src/Http/Controllers/SchemaController.php b/src/Http/Controllers/SchemaController.php index 72be376e..e7b937a4 100644 --- a/src/Http/Controllers/SchemaController.php +++ b/src/Http/Controllers/SchemaController.php @@ -16,7 +16,7 @@ public function getSchemas() $schemaNodes = []; $schemas = []; - foreach ($config as $key => $value) { + foreach ($config as $value) { $value['map']->generateSchema(); $schemaNodes = array_merge($schemaNodes, $value['map']->getSchemaNodes()); diff --git a/tests/ResourceTypesTest.php b/tests/ResourceTypesTest.php index 5eea15f7..24193378 100644 --- a/tests/ResourceTypesTest.php +++ b/tests/ResourceTypesTest.php @@ -45,7 +45,7 @@ public function testGetOne(){ "urn:ietf:params:scim:schemas:core:2.0:ResourceType" ], "id" => "User", - "name" => "Users", + "name" => "User", "endpoint" => "http://localhost/scim/v2/Users", "description" => "User Account", "schema" => "urn:ietf:params:scim:schemas:core:2.0:User", From c6d848d74a3407258dfd9b3e8cdf25176150b4e4 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Tue, 20 May 2025 22:39:35 +0200 Subject: [PATCH 51/58] Development flow improvements (#117) * Development flow improvements * use scimconfig for now --- .devcontainer/Dockerfile | 14 + .devcontainer/devcontainer.json | 37 + .devcontainer/setup-laravel-test.sh | 33 + .gitignore | 1 + .vscode/launch.json | 49 + .vscode/settings.json | 12 + .vscode/tasks.json | 35 + README.md | 60 +- composer.json | 3 +- composer.lock | 1331 +++++++++++++++++++++++++-- database/factories/GroupFactory.php | 1 - laravel-scim-server.svg | 51 + src/Attribute/Collection.php | 18 +- 13 files changed, 1517 insertions(+), 128 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/setup-laravel-test.sh create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 laravel-scim-server.svg diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..935c9fa3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/devcontainers/php:8.2 + +# Create Laravel test project directory in vscode user home +USER vscode +RUN mkdir -p /home/vscode/laravel-test + +# Expose port 8000 for Laravel +EXPOSE 8000 + +# Create setup script for Laravel test environment +COPY ./setup-laravel-test.sh /usr/local/bin/setup-laravel-test.sh +USER root +RUN chmod +x /usr/local/bin/setup-laravel-test.sh +USER vscode \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..901d9dcb --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,37 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/php +{ + "name": "Laravel SCIM Server Dev", + // Use the Dockerfile in the .devcontainer folder + "build": { + "dockerfile": "Dockerfile", + "context": "." + }, + + // Configure tool-specific properties + "customizations": { + "vscode": { + "extensions": [ + "bmewburn.vscode-intelephense-client", + "xdebug.php-debug", + "mikestead.dotenv", + "mehedidracula.php-namespace-resolver", + "recca0120.vscode-phpunit", + "formulahendry.terminal", + "junstyle.php-cs-fixer" + ], + "settings": { + "php.validate.executablePath": "/usr/local/bin/php" + } + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally + "forwardPorts": [8000], + + // Use 'postCreateCommand' to run commands after the container is created + "postCreateCommand": "composer install", + + // Add a non-root user to run the server + "remoteUser": "vscode" +} diff --git a/.devcontainer/setup-laravel-test.sh b/.devcontainer/setup-laravel-test.sh new file mode 100644 index 00000000..91c1f09b --- /dev/null +++ b/.devcontainer/setup-laravel-test.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +# Create Laravel project if it doesn't exist +if [ ! -d "/home/vscode/laravel-test/vendor" ]; then + echo "Creating new Laravel project..." + cd /home/vscode + composer create-project --prefer-dist laravel/laravel laravel-test + + cd laravel-test + + # Add the local package as a repository + jq '.repositories=[{"type": "path","url": "/workspaces/laravel-scim-server"}]' ./composer.json > composer.json.tmp && mv composer.json.tmp composer.json + + # Require the package and laravel/tinker + composer require arietimmerman/laravel-scim-server @dev + composer require laravel/tinker + + # Set up SQLite database + touch ./.database.sqlite + echo "DB_CONNECTION=sqlite" >> ./.env + echo "DB_DATABASE=/home/vscode/laravel-test/.database.sqlite" >> ./.env + + # Run migrations + php artisan migrate + + # Create test users + echo "User::factory()->count(100)->create();" | php artisan tinker + + echo "Laravel test environment setup complete!" +else + echo "Laravel test environment already exists!" +fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index 22d0d82f..58be7c72 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ vendor +.phpunit.result.cache diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c40bd55d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,49 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 0, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "Launch Built-in web server", + "type": "php", + "request": "launch", + "runtimeArgs": [ + "-dxdebug.mode=debug", + "-dxdebug.start_with_request=yes", + "-S", + "localhost:0" + ], + "program": "", + "cwd": "${workspaceRoot}", + "port": 9003, + "serverReadyAction": { + "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", + "uriFormat": "http://localhost:%s", + "action": "openExternally" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..70491936 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "php.validate.enable": true, + "php.validate.run": "onType", + "php.validate.executablePath": "/usr/local/bin/php", + "php-cs-fixer.executablePath": "/workspaces/laravel-scim-server/vendor/bin/php-cs-fixer", + "files.associations": { + "*.php": "php" + }, + "[php]": { + "editor.formatOnSave": true + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..b774c71f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,35 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Setup Laravel SCIM Test Server", + "type": "shell", + "command": "setup-laravel-test.sh", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "new" + }, + "group": "test" + }, + { + "label": "Start Laravel SCIM Test Server", + "type": "shell", + "command": "export XDEBUG_MODE=debug,develop && cd /home/vscode/laravel-test && COMPOSER_AUTOLOAD_DEV=1 composer dump-autoload && php artisan serve --host=0.0.0.0 --port=8000", + "dependsOn": "Setup Laravel SCIM Test Server", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "group": "test" + }, + { + "label": "Open SCIM Server in Browser", + "type": "shell", + "command": "$BROWSER http://localhost:8000/scim/v2/Users", + "problemMatcher": [], + "group": "test" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 0bf3ea84..303eedfd 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,17 @@ [![Latest Stable Version](https://poser.pugx.org/arietimmerman/laravel-scim-server/v/stable)](https://packagist.org/packages/arietimmerman/laravel-scim-server) [![Total Downloads](https://poser.pugx.org/arietimmerman/laravel-scim-server/downloads)](https://packagist.org/packages/arietimmerman/laravel-scim-server) +![Logo of Laravel SCIM Server, the SCIM server implementation from scim.dev, SCIM Playground](./laravel-scim-server.svg) + # SCIM 2.0 Server implementation for Laravel -Add SCIM 2.0 Server capabilities with ease. Usually, no configuration is needed in order to benefit from the basic functionalities. +Add SCIM 2.0 Server capabilities to your Laravel application with ease. This package requires minimal configuration to get started with basic functionalities. + +This implementation is used by [The SCIM Playground](https://scim.dev) and is therefore one of the most widely tested SCIM servers available. + +## Installation + +Simply run: ~~~ composer require arietimmerman/laravel-scim-server @@ -17,37 +25,29 @@ And optionally php artisan vendor:publish --tag=laravel-scim ~~~ -The module is used by [idaas.nl](https://www.idaas.nl/) and by [The SCIM Playground](https://scim.dev). - # Routes -~~~ -+----------+-----------------------------------------+ -| GET|HEAD | scim/v1 | -| GET|HEAD | scim/v1/{fallbackPlaceholder} | -| POST | scim/v2/.search | -| | | -| GET|HEAD | scim/v2/ResourceTypes | -| GET|HEAD | scim/v2/ResourceTypes/{id} | -| GET|HEAD | scim/v2/Schemas | -| GET|HEAD | scim/v2/Schemas/{id} | -| GET|HEAD | scim/v2/ServiceProviderConfig | -| GET|HEAD | scim/v2/{fallbackPlaceholder} | -| | | -| GET|HEAD | scim/v2/{resourceType} | -| | | -| POST | scim/v2/{resourceType} | -| | | -| GET|HEAD | scim/v2/{resourceType}/{resourceObject} | -| | | -| PUT | scim/v2/{resourceType}/{resourceObject} | -| | | -| PATCH | scim/v2/{resourceType}/{resourceObject} | -| | | -| DELETE | scim/v2/{resourceType}/{resourceObject} | -| | | -+----------+-----------------------------------------+ -~~~ +| Method | Path | +|--------|------| +| GET\|HEAD | / | +| GET\|HEAD | scim/v1 | +| GET\|HEAD | scim/v1/{fallbackPlaceholder} | +| POST | scim/v2/.search | +| POST | scim/v2/Bulk | +| GET\|HEAD | scim/v2/ResourceTypes | +| GET\|HEAD | scim/v2/ResourceTypes/{id} | +| GET\|HEAD | scim/v2/Schemas | +| GET\|HEAD | scim/v2/Schemas/{id} | +| GET\|HEAD | scim/v2/ServiceProviderConfig | +| GET\|HEAD | scim/v2/{fallbackPlaceholder} | +| GET\|HEAD | scim/v2/{resourceType} | +| POST | scim/v2/{resourceType} | +| POST | scim/v2/{resourceType}/.search | +| GET\|HEAD | scim/v2/{resourceType}/{resourceObject} | +| PUT | scim/v2/{resourceType}/{resourceObject} | +| PATCH | scim/v2/{resourceType}/{resourceObject} | +| DELETE | scim/v2/{resourceType}/{resourceObject} | + # Configuration diff --git a/composer.json b/composer.json index e63dfbca..d5fd81a6 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ }, "require-dev": { "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", - "laravel/legacy-factories": "*" + "laravel/legacy-factories": "*", + "friendsofphp/php-cs-fixer": "^3.66" }, "extra": { diff --git a/composer.lock b/composer.lock index 82675ac0..21e125a0 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": "d828374ef6a0ef831c83368a046325f2", + "content-hash": "f14e11f45ad5b8e86f365d789b1c7621", "packages": [ { "name": "brick/math", @@ -5072,6 +5072,149 @@ } ], "packages-dev": [ + { + "name": "clue/ndjson-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\NDJson\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" + }, + { + "name": "composer/pcre", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.8" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.2.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-07-25T09:36:02+00:00" + }, { "name": "composer/semver", "version": "3.4.2", @@ -5153,6 +5296,119 @@ ], "time": "2024-07-12T11:35:52+00:00" }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, { "name": "fakerphp/faker", "version": "v1.23.1", @@ -5216,6 +5472,67 @@ }, "time": "2024-01-02T13:46:09+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, { "name": "filp/whoops", "version": "2.15.4", @@ -5288,48 +5605,74 @@ "time": "2023-11-03T12:00:00+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "2.7.0", + "name": "friendsofphp/php-cs-fixer", + "version": "v3.66.0", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "5f5f2a142ff36b93c41885bca29cc5f861c013e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/5f5f2a142ff36b93c41885bca29cc5f861c013e6", + "reference": "5f5f2a142ff36b93c41885bca29cc5f861c013e6", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.2", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0 <7.2", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "facile-it/paraunit": "^1.3.1 || ^2.4", + "infection/infection": "^0.29.8", + "justinrainbow/json-schema": "^5.3 || ^6.0", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.5", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.5", + "phpunit/phpunit": "^9.6.21 || ^10.5.38 || ^11.4.3", + "symfony/var-dumper": "^5.4.47 || ^6.4.15 || ^7.1.8", + "symfony/yaml": "^5.4.45 || ^6.4.13 || ^7.1.6" }, "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", "autoload": { "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/Fixer/Internal/*" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5337,9 +5680,86 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.66.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-12-29T13:46:23+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", @@ -7094,36 +7514,507 @@ "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, - "conflict": { - "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.12.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + }, + "time": "2024-06-10T01:18:23+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-01-01T16:37:48+00:00" + }, + { + "name": "react/dns", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "react/socket", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" - }, - "suggest": { - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, - "bin": [ - "bin/psysh" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "0.12.x-dev" - }, - "bamarni-bin": { - "bin-links": false, - "forward-command": false - } - }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Psy\\": "src/" + "React\\Socket\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -7132,51 +8023,74 @@ ], "authors": [ { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", "keywords": [ - "REPL", - "console", - "interactive", - "shell" + "Connection", + "Socket", + "async", + "reactphp", + "stream" ], "support": { - "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" }, - "time": "2024-06-10T01:18:23+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "php": ">=5.6" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "React\\Stream\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7184,16 +8098,48 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A polyfill for getallheaders.", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" }, { "name": "rector/rector", @@ -8455,6 +9401,139 @@ ], "time": "2024-04-24T14:21:46+00:00" }, + { + "name": "symfony/filesystem", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:15:23+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-20T11:17:29+00:00" + }, { "name": "symfony/polyfill-iconv", "version": "v1.30.0", @@ -8535,6 +9614,82 @@ ], "time": "2024-05-31T15:07:36+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/stopwatch", "version": "v6.4.8", @@ -8930,12 +10085,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^8.0" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/database/factories/GroupFactory.php b/database/factories/GroupFactory.php index 295d49b8..1147295d 100644 --- a/database/factories/GroupFactory.php +++ b/database/factories/GroupFactory.php @@ -5,7 +5,6 @@ $factory->define(Group::class, function (Generator $faker) { return [ - // 'username' => $faker->userName, 'displayName' => $faker->company ]; }); diff --git a/laravel-scim-server.svg b/laravel-scim-server.svg new file mode 100644 index 00000000..638af5b0 --- /dev/null +++ b/laravel-scim-server.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + diff --git a/src/Attribute/Collection.php b/src/Attribute/Collection.php index f6de40a2..47bc2db7 100644 --- a/src/Attribute/Collection.php +++ b/src/Attribute/Collection.php @@ -34,16 +34,18 @@ protected function doRead(&$object, $attributes = []) { $result = []; - foreach ($object->{$this->attribute} as $o) { - $element = []; - - foreach ($this->subAttributes as $attribute) { - if (($r = $attribute->read($o)) != null) { - $element[$attribute->name] = $r->value; + if ($object->{$this->attribute} !== null) { + foreach ($object->{$this->attribute} as $o) { + $element = []; + + foreach ($this->subAttributes as $attribute) { + if (($r = $attribute->read($o)) != null) { + $element[$attribute->name] = $r->value; + } } - } - $result[] = $element; + $result[] = $element; + } } return $result; From b2f41b8ced74bbbc757ceaaca6af7a0634159f5a Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Sat, 23 Aug 2025 23:07:56 +0200 Subject: [PATCH 52/58] Fix for replace for extension schemas (#120) --- Dockerfile | 2 +- src/Attribute/Complex.php | 26 +++++++++++++++++--------- tests/CustomSchemaTest.php | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6caeddd1..8f051e46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN composer require arietimmerman/laravel-scim-server @dev && \ RUN touch ./.database.sqlite && \ echo "DB_CONNECTION=sqlite" >> ./.env && \ - echo "DB_DATABASE=/example/.database.sqlite" >> ./.env && \ + echo "DB_DATABASE=/.database.sqlite" >> ./.env && \ echo "APP_URL=http://localhost:18123" >> ./.env RUN php artisan migrate && \ diff --git a/src/Attribute/Complex.php b/src/Attribute/Complex.php index 35c32df7..b3a9c1e7 100644 --- a/src/Attribute/Complex.php +++ b/src/Attribute/Complex.php @@ -18,7 +18,7 @@ class Complex extends AbstractComplex */ public function getSchemas() { - return collect($this->getSchemaNodes())->map(fn ($element) => $element->name)->values()->toArray(); + return collect($this->getSchemaNodes())->map(fn($element) => $element->name)->values()->toArray(); } @@ -76,21 +76,29 @@ public function patch($operation, $value, Model &$object, Path $path = null, $re $attributeNames = $path?->getAttributePath()?->getAttributeNames() ?? []; if (!empty($attributeNames)) { - $attribute = $this->getSubNode($attributeNames[0]); + $schema = $path->getAttributePath()?->path?->schema; + // Resolve attribute either directly or via schema parent when specified + $attribute = $schema + ? ((($parent = $this->getSubNode($schema)) instanceof Schema) ? $parent->getSubNode($attributeNames[0]) : null) + : $this->getSubNode($attributeNames[0]); + if ($attribute != null) { $attribute->patch($operation, $value, $object, $path->shiftAttributePathAttributes()); - } elseif ($this->parent == null) { - // this is the root node, check within the first (the default) schema node - // pass the unchanged path object + return; // done + } + + if ($this->parent == null) { + // root node: delegate unchanged path to default schema node $this->getSchemaNode()->patch($operation, $value, $object, $path); - } else { - throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); + return; // done } + + throw new SCIMException('Unknown path: ' . (string)$path . ", in object: " . $this->getFullKey()); } } } else { // if there is no path, keys of value are attribute names - switch($operation) { + switch ($operation) { case 'replace': $this->replace($value, $object, $path, false); break; @@ -300,6 +308,6 @@ public function applyComparison(Builder &$query, Path $path, $parentAttribute = */ public function getDefaultSchema() { - return collect($this->subAttributes)->first(fn ($element) => $element instanceof Schema)->name; + return collect($this->subAttributes)->first(fn($element) => $element instanceof Schema)->name; } } diff --git a/tests/CustomSchemaTest.php b/tests/CustomSchemaTest.php index a6d56a99..d1734757 100644 --- a/tests/CustomSchemaTest.php +++ b/tests/CustomSchemaTest.php @@ -79,4 +79,28 @@ public function testPost() $this->assertEquals('Dr. Marie Jo', $json['urn:ietf:params:scim:schemas:core:2.0:User']['userName']); $this->assertEquals('123', $json['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['employeeNumber']); } + + public function testPatchEnterpriseSchema() + { + $response = $this->patch('/scim/v2/Users/2', [ + "schemas" => [ + "urn:ietf:params:scim:api:messages:2.0:PatchOp", + ], + "Operations" => [ + [ + "op" => "replace", + "path" => "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber", + "value" => "12345" + ] + ] + ]); + + + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertArrayHasKey('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', $json); + $this->assertEquals('12345', $json['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['employeeNumber']); + } } From f6f94a42dd086ce828b8bfa2b08cc13fc2dafd2a Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 27 Aug 2025 21:48:30 +0200 Subject: [PATCH 53/58] Docker image improvements (#122) --- .github/workflows/ci.yml | 26 +++++++-- Dockerfile | 122 +++++++++++++++++++++++++++++++++++++-- composer.json | 6 +- docker-compose.yml | 2 +- src/RouteProvider.php | 54 ++++++++--------- src/SCIMConfig.php | 5 ++ src/ServiceProvider.php | 5 +- 7 files changed, 177 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f2aa694..87866981 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,10 +9,8 @@ jobs: strategy: matrix: php: [8.0, 8.1, 8.2] - laravel: [8.*, 9.*, 10.*, 11.*, 12.*] + laravel: [10.*, 11.*, 12.*] exclude: - - laravel: 8.* - php: 8.2 - laravel: 10.* php: 8.0 - laravel: 11.* @@ -46,4 +44,24 @@ jobs: composer require "illuminate/console:${{ matrix.laravel }}" "illuminate/database:${{ matrix.laravel }}" "illuminate/support:${{ matrix.laravel }}" --no-interaction --no-update composer update - name: Run tests - run: vendor/bin/phpunit \ No newline at end of file + run: vendor/bin/phpunit + + docker: + runs-on: ubuntu-latest + needs: tests + if: github.event_name == 'push' || github.event_name == 'pull_request' + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ghcr.io/${{ github.repository }}:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8f051e46..f8480f43 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,12 +20,122 @@ RUN jq '.repositories=[{"type": "path","url": "/laravel-scim-server"}]' ./compos RUN composer require arietimmerman/laravel-scim-server @dev && \ composer require laravel/tinker -RUN touch ./.database.sqlite && \ - echo "DB_CONNECTION=sqlite" >> ./.env && \ - echo "DB_DATABASE=/.database.sqlite" >> ./.env && \ - echo "APP_URL=http://localhost:18123" >> ./.env +RUN touch /example/database.sqlite && \ + echo "DB_CONNECTION=sqlite" >> /example/.env && \ + echo "DB_DATABASE=/example/database.sqlite" >> /example/.env && \ + echo "APP_URL=http://localhost:18123" >> /example/.env + +# Add migration for groups table using heredoc +RUN cat > /example/database/migrations/2021_01_01_000001_create_groups_table.php <<'EOM' +id(); + $table->string('displayName')->unique(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('groups'); + } +}; +EOM + +# Add Group model +RUN cat > app/Models/Group.php <<'EOM' + app/SCIM/CustomSCIMConfig.php <<'EOM' + app/Providers/AppServiceProvider.php <<'EOM' +app->singleton(BaseSCIMConfig::class, CustomSCIMConfig::class); + } + + public function boot(): void + { + // Additional boot logic if needed + } +} +EOM + +# Add Group factory +RUN cat > database/factories/GroupFactory.php <<'EOM' + $this->faker->unique()->company(), + ]; + } +} +EOM + +# Run migrations and seed demo data RUN php artisan migrate && \ - echo "User::factory()->count(100)->create();" | php artisan tinker + echo "User::factory()->count(100)->create(); App\\Models\\Group::factory()->count(10)->create();" | php artisan tinker -CMD php artisan serve --host=0.0.0.0 --port=8000 +CMD ["php","artisan","serve","--host=0.0.0.0","--port=8000"] diff --git a/composer.json b/composer.json index d5fd81a6..c738ea72 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ ], "require": { "php": "^8.0", - "illuminate/database": "^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/console": "^10.0|^11.0|^12.0", "tmilos/scim-schema": "^0.1.0", "tmilos/scim-filter-parser": "^1.3" }, diff --git a/docker-compose.yml b/docker-compose.yml index 0dc043c1..12f972e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: ports: # forward xdebug ports - "127.0.0.1:18123:8000" - working_dir: /laravel-scim-server + working_dir: /example environment: - XDEBUG_MODE=debug - XDEBUG_SESSION=1 diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 447ad1c0..a98d1bff 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -1,7 +1,10 @@ group( function () use ($options) { - Route::prefix('v2')->middleware( - [ - // TODO: Not loading this middleware introduces resolve issues. But having it, might slow things down. - \Illuminate\Routing\Middleware\SubstituteBindings::class, - 'ArieTimmerman\Laravel\SCIMServer\Middleware\SCIMHeaders' - ] - )->group( + Route::prefix('v2')->middleware([ + SubstituteBindings::class, + SCIMHeaders::class, + ])->group( function () use ($options) { static::allRoutes($options); } ); - Route::get('v1', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@wrongVersion'); + Route::get('v1', [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'wrongVersion']); Route::prefix('v1')->group( function () { - Route::fallback('\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@wrongVersion'); + Route::fallback([\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'wrongVersion']); } ); } @@ -44,47 +44,47 @@ function () { public static function meRoutes(array $options = []) { Route::prefix(static::$prefix)->group(function () { - Route::get("/v2/Me", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@getMe')->name('scim.me.get'); - Route::put('/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@replaceMe')->name('scim.me.put'); + Route::get("/v2/Me", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController::class, 'getMe'])->name('scim.me.get'); + Route::put('/v2/Me', [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController::class, 'replaceMe'])->name('scim.me.put'); }); } public static function meRoutePost(array $options = []) { Route::prefix(static::$prefix)->group(function () { - Route::post('/v2/Me', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController@createMe')->name('scim.me.post'); + Route::post('/v2/Me', [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\MeController::class, 'createMe'])->name('scim.me.post'); }); } public static function publicRoutes(array $options = []) { Route::prefix(static::$prefix)->group(function () { - Route::get("/v2/ServiceProviderConfig", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ServiceProviderController@index')->name('scim.serviceproviderconfig'); + Route::get("/v2/ServiceProviderConfig", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ServiceProviderController::class, 'index'])->name('scim.serviceproviderconfig'); - Route::get("/v2/Schemas", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@index'); - Route::get("/v2/Schemas/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController@show')->name('scim.schemas'); + Route::get("/v2/Schemas", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController::class, 'index']); + Route::get("/v2/Schemas/{id}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\SchemaController::class, 'show'])->name('scim.schemas'); - Route::get("/v2/ResourceTypes", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@index'); - Route::get("/v2/ResourceTypes/{id}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController@show')->name('scim.resourcetype'); + Route::get("/v2/ResourceTypes", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController::class, 'index']); + Route::get("/v2/ResourceTypes/{id}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceTypesController::class, 'show'])->name('scim.resourcetype'); }); } private static function allRoutes(array $options = []) { - Route::post('.search', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@notImplemented'); + Route::post('.search', [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'notImplemented']); - Route::post("/Bulk", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\BulkController@processBulkRequest'); + Route::post("/Bulk", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\BulkController::class, 'processBulkRequest']); // TODO: Use the attributes parameters ?attributes=userName, excludedAttributes=asdg,asdg (respect "returned" settings "always") - Route::get('/{resourceType}/{resourceObject}', '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@show')->name('scim.resource'); - Route::get("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@index')->name('scim.resources'); - Route::post("/{resourceType}/.search", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@search'); - Route::post("/{resourceType}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@create'); + Route::get('/{resourceType}/{resourceObject}', [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'show'])->name('scim.resource'); + Route::get("/{resourceType}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'index'])->name('scim.resources'); + Route::post("/{resourceType}/.search", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'search']); + Route::post("/{resourceType}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'create']); - Route::put("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@replace'); - Route::patch("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@update'); - Route::delete("/{resourceType}/{resourceObject}", '\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@delete'); + Route::put("/{resourceType}/{resourceObject}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'replace']); + Route::patch("/{resourceType}/{resourceObject}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'update']); + Route::delete("/{resourceType}/{resourceObject}", [\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'delete']); - Route::fallback('\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController@notImplemented'); + Route::fallback([\ArieTimmerman\Laravel\SCIMServer\Http\Controllers\ResourceController::class, 'notImplemented']); } } diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index bda4c4a9..1fd6cb42 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -44,6 +44,11 @@ public function getConfigForResource($name) return @$result[$name]; } + public function getGroupClass() + { + return Group::class; + } + public function getUserConfig() { return [ diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index e2e493a0..88aaceb4 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -1,4 +1,5 @@ loadMigrationsFrom(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations'); $this->publishes([ - __DIR__.'/../config/scim.php' => config_path('scim.php'), + __DIR__ . '/../config/scim.php' => config_path('scim.php'), ], 'laravel-scim'); // Match everything, except the Me routes @@ -79,7 +80,7 @@ function ($id, $route) { public function register() { $this->mergeConfigFrom( - __DIR__.'/../config/scim.php', + __DIR__ . '/../config/scim.php', 'scim' ); } From d2f33375f4828eebbbcfffdfead7d99a68dd9c21 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 27 Aug 2025 21:50:43 +0200 Subject: [PATCH 54/58] Workflow fix (#123) --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87866981..2c24de2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,10 @@ name: CI on: ['push', 'pull_request'] +permissions: + contents: read + packages: write + jobs: tests: runs-on: ubuntu-latest @@ -47,12 +51,13 @@ jobs: run: vendor/bin/phpunit docker: + # Only push Docker image for direct pushes to master branch + if: github.event_name == 'push' && github.ref == 'refs/heads/master' runs-on: ubuntu-latest needs: tests - if: github.event_name == 'push' || github.event_name == 'pull_request' steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Log in to the Container registry uses: docker/login-action@v3 with: From 6137477ed17402d9ab9d1fd55c7564e5fe8f07ad Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 27 Aug 2025 22:05:07 +0200 Subject: [PATCH 55/58] Laravel SCIM Server logo update (#124) * Workflow fix * logo --- laravel-scim-server.svg | 130 ++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 39 deletions(-) diff --git a/laravel-scim-server.svg b/laravel-scim-server.svg index 638af5b0..1d79d20f 100644 --- a/laravel-scim-server.svg +++ b/laravel-scim-server.svg @@ -2,50 +2,102 @@ - + id="defs2"> + + - - + transform="translate(-1.748224,-1.0010848)"> + + + + + + + + + + + + + + + + + + + + From f48efe7bd2bc67775345dbcf3be346470c0e03b8 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Wed, 27 Aug 2025 23:48:14 +0200 Subject: [PATCH 56/58] Fix separator variable name (#121) --- src/Attribute/Attribute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Attribute/Attribute.php b/src/Attribute/Attribute.php index 65a562b8..66a666f9 100644 --- a/src/Attribute/Attribute.php +++ b/src/Attribute/Attribute.php @@ -191,8 +191,8 @@ protected function doRead(&$object, $attributes = []) public function getFullKey() { if ($this->parent != null && $this->parent->name != null) { - $seperator = $this->parent instanceof AttributeSchema ? ':' : '.'; - return $this->parent->getFullKey() . $seperator . $this->name; + $separator = $this->parent instanceof AttributeSchema ? ':' : '.'; + return $this->parent->getFullKey() . $separator . $this->name; } else { return $this->name; } From 79056ac4bffa2a4e93df95290b7edce99aa84006 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 28 Aug 2025 20:04:07 +0200 Subject: [PATCH 57/58] Docker image updates (#125) --- Dockerfile | 137 +++++++++++++++++++++++++++++++++++++++------ src/SCIMConfig.php | 12 +++- 2 files changed, 129 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index f8480f43..2d507dfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,39 @@ FROM php:8.1-alpine -RUN apk add --no-cache git jq moreutils -RUN apk add --no-cache $PHPIZE_DEPS postgresql-dev \ - && docker-php-ext-install pdo_pgsql \ +# Base tools and PHP extensions +RUN apk add --no-cache git jq moreutils \ + && apk add --no-cache $PHPIZE_DEPS postgresql-dev sqlite-dev \ + && docker-php-ext-install pdo_pgsql pdo_sqlite \ && pecl install xdebug-3.1.5 \ && docker-php-ext-enable xdebug \ && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ && echo "xdebug.client_host = 172.19.0.1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +# Composer + fresh Laravel app RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -RUN composer create-project --prefer-dist laravel/laravel example && \ - cd example +RUN composer create-project --prefer-dist laravel/laravel example && cd example WORKDIR /example +# Link local package COPY . /laravel-scim-server RUN jq '.repositories=[{"type": "path","url": "/laravel-scim-server"}]' ./composer.json | sponge ./composer.json +# Install package and dev helpers RUN composer require arietimmerman/laravel-scim-server @dev && \ composer require laravel/tinker +# SQLite config RUN touch /example/database.sqlite && \ echo "DB_CONNECTION=sqlite" >> /example/.env && \ echo "DB_DATABASE=/example/database.sqlite" >> /example/.env && \ echo "APP_URL=http://localhost:18123" >> /example/.env +# Make users.password nullable to allow SCIM-created users without passwords +RUN sed -i -E "s/\\$table->string\('password'\);/\\$table->string('password')->nullable();/g" \ + database/migrations/*create_users_table.php || true -# Add migration for groups table using heredoc +# Groups table migration RUN cat > /example/database/migrations/2021_01_01_000001_create_groups_table.php <<'EOM' app/Models/Group.php <<'EOM' belongsToMany(User::class, 'group_user', 'group_id', 'user_id')->withTimestamps(); + } } EOM -# Add Custom SCIM Config overriding Group model class +# Override SCIM config to use app's Group model RUN mkdir -p app/SCIM && cat > app/SCIM/CustomSCIMConfig.php <<'EOM' app/Providers/AppServiceProvider.php <<'EOM' database/factories/GroupFactory.php <<'EOM' /example/database/migrations/2021_01_01_000002_create_group_user_table.php <<'EOM' +id(); + $table->foreignId('group_id')->constrained('groups')->cascadeOnDelete(); + $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); + $table->timestamps(); + $table->unique(['group_id', 'user_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('group_user'); + } +}; +EOM + +# Ensure User model has groups() relation (overwrite default) +RUN cat > app/Models/User.php <<'EOM' + 'datetime', + ]; + + public function groups(): BelongsToMany + { + return $this->belongsToMany(Group::class, 'group_user', 'user_id', 'group_id')->withTimestamps(); + } +} +EOM + +# Seeder for demo data +RUN cat > /example/database/seeders/DemoSeeder.php <<'EOM' +count(50)->create(); + $groups = Group::factory()->count(10)->create(); + + foreach ($groups as $g) { + $g->members()->sync($users->random(rand(3, 10))->pluck('id')->toArray()); + } + } +} +EOM + # Run migrations and seed demo data -RUN php artisan migrate && \ - echo "User::factory()->count(100)->create(); App\\Models\\Group::factory()->count(10)->create();" | php artisan tinker +RUN php artisan migrate && php artisan db:seed --class=Database\\Seeders\\DemoSeeder CMD ["php","artisan","serve","--host=0.0.0.0","--port=8000"] + diff --git a/src/SCIMConfig.php b/src/SCIMConfig.php index 1fd6cb42..e8931db8 100644 --- a/src/SCIMConfig.php +++ b/src/SCIMConfig.php @@ -76,6 +76,10 @@ protected function doRead(&$object, $attributes = []) { return (string)$object->id; } + public function remove($value, &$object, $path = null) + { + // do nothing + } } ), new Meta('Users'), @@ -143,7 +147,7 @@ public function getGroupConfig() { return [ - 'class' => Group::class, + 'class' => $this->getGroupClass(), 'singular' => 'Group', //eager loading @@ -165,13 +169,17 @@ protected function doRead(&$object, $attributes = []) { return (string)$object->id; } + public function remove($value, &$object, $path = null) + { + // do nothing + } } ), new Meta('Groups'), (new AttributeSchema(Schema::SCHEMA_GROUP, true))->withSubAttributes( eloquent('displayName')->ensure('required', 'min:3', function ($attribute, $value, $fail) { // check if group does not exist or if it exists, it is the same group - $group = Group::where('displayName', $value)->first(); + $group = $this->getGroupClass()::where('displayName', $value)->first(); if ($group && (request()->route('resourceObject') == null || $group->id != request()->route('resourceObject')->id)) { $fail('The name has already been taken.'); } From 043f5a397b84e27a6f02e8ec14ea053c13043764 Mon Sep 17 00:00:00 2001 From: Arie Timmerman Date: Thu, 28 Aug 2025 21:24:25 +0200 Subject: [PATCH 58/58] Add Docker instructions for spinning up SCIM test server --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 303eedfd..24e43170 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Add SCIM 2.0 Server capabilities to your Laravel application with ease. This package requires minimal configuration to get started with basic functionalities. This implementation is used by [The SCIM Playground](https://scim.dev) and is therefore one of the most widely tested SCIM servers available. +## Docker + +To quickly spin up a SCIM test server using Docker, run: + +~~~ +docker run -d -p 8000:8000 --name laravel-scim-server ghcr.io/limosa-io/laravel-scim-server:latest +~~~ + +This command will start the server and bind it to port 8000 on your local machine. You can then access the SCIM endpoints at `http://localhost:8000/scim/v2/Users`. Other SCIM endpoints like `/Groups`, `/Schemas`, and `/ResourceTypes` will also be available. ## Installation